黑马程序员
一
File类 RandomAccessFile类 各种节点流类 字符编码 各种过滤流与包装类 IO类得相关应用
File类
File类是IO包中唯一代表磁盘文件本身信息的类,而不是文件中的内容。
File类定义了一些与平台无关的方法来操纵文件,例如,调用File类得各种方法可以判断文件的读写权限以及文件是否存在,创建,删除文件和重命名文件,查询文件的最近修改时间。
Java中的目录被当做一种特殊的文件使用,list方法可以返回目录中所有子目录和文件名。
在Unix下的路径分隔符为(/),在Dos下的路径分隔符为(\),Java可以正确处理Unix和Dos的路径分隔符。举例 判断某个文件是否存在,存在则删除,不存在则创建。
import java.io.*
public class FileCreate{
public static void main(String[] args){
FileCreate fc=new FileCreate();
fc.newFile("path");
}
public void newFile(String filePathAndName){
try {
String filePath = filePathAndName;
filePath = filePath.toString();
File myFilePath=new File(filePath);
if(!myFilePath.exists()) {
myFilePath.createNewFile();}
else
myFilePath.delete();
} catch(Exception e) { e.printStackTrace();}
}
}
二
RandomAccessFile类
RandomAccessFile类提供了众多丰富的文件访问方法。
RandomAccessFile类支持"随机访问"方式。其中有个文件"指示器"的东西。
RandomAccessFile类在随机读写等长记录格式的文件时有很大优势。
RandomAccessFile类仅限于操作文件,不能访问其他的IO设备,如网络,内存印象等。两种构造方法
new RandomAccessFile(f,"rw");//读写方式
new RandomAccessFile(f,"r");//只读方式
三
四
字节流
流的概念:流是字节序列的抽象概念,例如文件输入输出设备,网络内存,内部通信进程管道,TCP/IP套接字等设备传输的数据序列都可以理解为流。流提供了一种统一的方式从各种输入输出设备中读取和向这些设备写入的方法。
文件是数据的静态存储形式,而流是指数据传输时的形态。文件是一些具有永久存储即特定顺序的字节组成的一个有序的有名称的数据的集合。而流提供了一种向IO设备读取和向其中写入字节的方式,文件只是流可操作的IO设备之一,除了文件流还存在网络流,内存流,磁盘流等。数据流是一串连续传输的数据集合。
流类分为两个大流:节点流类和过滤流(也叫处理流类)。用于直接操作目标设备所对应的流类叫节点流类。节点流类所对应的I/O源或目标称为流节点。
InputStream与OutStream类:
程序可以从中连续读取字节的对象叫输入流,在java中,用InputStream类来描述所有输入流的抽象概念。
InputStream类得方法 int read()从输入流中读取一个字节,并用整数的方式返回。如果到输入流的结束,返回-1。如果流未结束,而又没有可读的内容,则堵塞。
abstract int read()
从输入流读取下一个数据字节。
int read(byte[] b)
从输入流中读取一定数量的字节并将其存储在缓冲区数组 b 中。
int read(byte[] b, int off, int len)
将输入流中最多 len 个数据字节读入字节数组。
long skip(long n)
跳过和放弃此输入流中的 n 个数据字节。
int available()
返回此输入流方法的下一个调用方可以不受阻塞地从此输入流读取(或跳过)的字节数。
void mark(int readlimit)
在此输入流中标记当前的位置。
void reset()
将此流重新定位到对此输入流最后调用 mark 方法时的位置。
boolean markSupported()
测试此输入流是否支持 mark 和 reset 方法。
void close()
关闭此输入流并释放与该流关联的所有系统资源。
OUtputStream类:程序可以向其中连续写入字节的对象叫输出流,在java中,用OutputStream类来描述所有输出流的抽象概念。
OutputStream类得方法:
abstract void write(int b) 将指定的字节写入此输出流。
void write(byte[] b)
将 b.length 个字节从指定的字节数组写入此输出流。
void write(byte[] b, int off, int len)
将指定字节数组中从偏移量 off 开始的 len 个字节写入此输出流
void flush()
刷新此输出流并强制写出所有缓冲的输出字节。
void close()
关闭此输出流并释放与此流有关的所有系统资源。
FileInputStream与FileOutStream类:
Rader与Writer类:
PipedInputStream与PipedOutStream类:
ByteArrayInputStream与ByteArrayStream类:
重视代码程序的复用性:
拓展知识面,寻找可能导致问题发生的原因。
五
FileInputStream和FileOutputStream类
FileInputStream和FileOutStream类分别用来创建磁盘文件的输入流和输出流对象,通过它们的构造函数来指定文件路径和文件名。
创建FileInputStream实例对象时,制定的文件应当是存在和可读的。创建FileOutStream实例对象时,如果指定的文件已经存在,这个文件的原来内容将被覆盖清除。
对同一个磁盘文件创建FileInputStream对象的两种方式:
(1)FileInputStream inOne=new FileInputStream("hello.test");
(2)File f=new File("hello.test"); FileInputStream inTwo=new FileInputStream(f);
创建FileOutStream实例对象时可以指定还不存在的文件名,但不能指定一个已经被其他程序打开到了的文件。
Reader和Writer类(Unicode码是双字节的)
Reader和Writer是所有字符流类的抽象基类,同于简化对字符串的输入输出编程,即用于读写文本数据。
二进制文件和文本文件的区别:举例说明用FileReader和FileWriter类来实现文件字符的读写。
六
PipedInputStream与PipedOutStream类
PipedInputStream类与PipedOutputStream类用于在应用程序中的创建管道通信。主要完成管道线程之间的通信。
PipedWriter和PipedReader类
使用管道流类,可以实现各个程序模块之间的松耦通信。高内聚低耦合。
ByteArrayInputStream和ByetArrayOutputStream类
ByteArrayInputStream和ByteArrayOutputStream,用于以IO流的方式来完成对字节数组的读写,来支持类似内存虚拟文件或者内存映像文件的功能。
ByteArrayInputStream的两个构造函数:
ByteArrayInputStream(byte[] buf)
ByteArrayInputStream(byte[] buf,int offset,int length)
ByteArrayOutputStream的两个构造函数:
ByteArrayOutputStream()
ByteArrayOutputStream(int)
编程举例:编写一个把输入流中所有英文字母变成大写字母,然后将结果写入到一个输出流对象。用这个函数来将一个字符串中的所有字符转换成大写。
StringReader类和StringWriter类来以字符IO流的方式处理字符串。
重视IO程序代码的复用
System.in连接到键盘,是InputStream类型的实例对象 System.out连接到显示器,是PrintStream类得实例对象。
不管各种底层物理设备用什么方式实现数据的终止点,InputStream的read方法总是返回-1来表示输入流的结束。
在Windows下,按下Ctrl+Z组合键可以产生键盘输入流的结束标记,在linux下,则是按下Ctrl+D组合键来产生键盘输入流ude结束标记。
编程举例:借助上一页编写的函数,将键盘上输入的内容转变成大写字母后打印在屏幕上。l利用transform函数。以输入流和输出流作为参数。transform(InputStream object,outputStream object).
建议:要编程从键盘上连续读取一大段数据时,应尽量将读取的过程放在函数中完成,使用-1来作为键盘输入的结束点。在该函数中编写的程序代码不应直接使用System.in读取数据,而是用一个InputStream类型的形式参数对象来读取数据,然后将System.in作为实参传递给InputStream类型的形式参数来调用该函数。
七
Unicode占两个字节。
UTF_8,UTF_16
UTF_16在Unicode基础上进行了一些细节上的扩充,增加了对Unicode编码没有包括的那些字符的表现方式。
UTF_16对Unicode的扩充并没有影响Unicode编码所包括的那些字符,只是增加了对Unicode编码没有包括的那些字符表示方式,一个使用Unicode编码的字符就是UTF_16格式的。
Unicode编码将0xD800-0xDFFF区间的数值保留下来,UTF-16扩充的字符,占用四个字节,前面两个字节的数值为0xD800-0xD8FF之间,后面的两个字节的数值为0xDC00-0xDFFF之间。
为什么不让前面和后面的两个字节的数值都位于0xD800-0xDFFF之间呢?
因为0xD800-0xD8FF能够表示的数的范围为256,后面的两个字节的数值为1101 1100 0000-1111 1111 1111所表示的数值个数为4*16*16=256*4=1024 前后共四个字节可以表示的总字符个数为262144
0xD800-0xDFFF之间的表示字符的个数为1011 1000 0000 0000-1011 1000 1111 1111 共计2048+53248=55296 53248+3840+240+15=57343 57343-55296+1=2048
某两个特定的进制数之间的表示范围为: 与十进制是一样的,最后加一。
在不同的体系结构的计算机系统中,UTF-16编码的Unicode字符在内存中的字节存储顺序是不同的。
区分Big_Endian和Little_Endian文件只要看文件开头的0xFF 0xFE或者0xFE 0xFF
八
字符编码的操作体验
查看中文字符的GB2312码
查看中文字符的UTF-8码
查看中文字符的Unicode码
在Windows记事本程序中用不同的编码格式存储文本文件
字符编码应用的一个奇怪现象
联想 联通 联 现象
九
字符编码的编程体验
打印中文字符的Unicode码
打印中文字符的 GB2312码
验证写入到屏幕输出流的中文字符所采用的编码
查看系统的缺省编码
修改系统的缺省编码
验证从键盘输入流中读取的中文字符所采用的编码
研究GB2312码到Unicode码的解码过程。
GB2312码的中文字符被按照ISO8859-1字符集解码生成了Unicode字符串后,如何将这个字符串转换成正确Unicode编码字符串。
System.getProperties()返回一个Properties类。
十
十一
过滤流与包装类
包装类的概念与作用
BufferedInputStream与BufferedOutputStream类
DataInputStream与DataOutputStream类
PrintStream类
ObjectInputStream与ObjectOutputStream类
字节流与字符流的转换
包装类的概念与作用
通过FileOutputStream对性爱那个将一个浮点小数写入到文件中,你感觉有点困难吧?能否通过FileOutputStream对象直接将一个整数写入到文件呢?
加入有个DataOutputStream类提供了往各种输出流对象中写入各种类型的数据(当然包括浮点小数)的方法。你现在所要做的工作就是: 传递一个FileOutputStream输出流对象给DataOutputStream实例对象和调用。DataOutputStream实例对象用于写入浮点小数的方法。
DataOutputStream并没有对应到任何具体的流设备,一定要给它传递一个对应具体流设备的输出流对象,完成类似DataOutputStream功能的类就是一个包装类,也叫过滤流或处理流类。
DataOutputStream包装类的构造函数语法:public DataoutputStream(outputStream out)
BufferedInputStream与BufferedOutputStream类
缓冲流为I/O流增加了内存缓冲区,增加缓冲区有两个基本目的:
-允许Java程序一次不知操作ige字节,这样提高了程序的性能。
-由于有了缓冲区,使得在流上执行skip,mark和reset方法都成为可能。
BufferInputStream和BufferedOutputStream是Java提供的两个缓冲区包装类,不管底层系统是否使用了缓冲区,这两个类在自己的实例对象中创建缓冲区。想象这种缓冲区与底层系统提供的缓冲区的的区别?
底层系统提供的缓冲区与目标设备直接交换数据;包装类要调用包装的那个输出流的对象将数据直接写入到目标设备或底层系统缓冲区中。或者包装类要调用包装的输入流对象将数据读取到目标设备或底层缓冲区。
BufferedInputStream的两个构造函数:
BufferedInputStream(InputStream in)
BufferedInputStream(InputStream in,int size)
BufferedOutputStream类的两个构造函数 :
BufferedOutputStream(OutputStream out)
BufferedOutputStream(OutputStreeam out,int size)
BufferedReader和BufferedWriter类
BufferedReader的readLine方法可以一次读取一行文本,BufferedWriter的newLine方法可以向字符流中写入不同操作系统下的换行符。
十二
DataInputStream与DataOutputStream类
DataOutputStream类提供了三个写入字符串的方法:
- public final void writeBytes(String s) 将每个字符的第一个字节写入到目标设备中。
- public final void writeChars(String s) 将每字符的两个字节写入到目标设备中。
- public final void writeUTF(String s) 将字符串以UTF编码写入到目标设备。
DatainputStream类中有一个readUTF方法,而没有"readBytes"和"readChars"方法。
关闭流栈中的最上层的流对象,将会自动关闭流栈中的所有底层流对象。
PrintStream类
PrintStream类提供了一系列的print和println方法,可以将基本数据类型的数据格式化成字符串输出。
格式化输出的意思为:例如,97被格式化输出的实际字节数据为0x39和0x37。
PrintStream的3个构造函数:
- PrintStream(OutputStream out)
- PrintStream(OutputStream out,boolean autoflush)
- printStream(OutStream out,boolean autoflush,String encoding)
autoflush: 自动刷新
encoding: 本地字符集编码
帮助文档中输入printStream来查询类printStream
与PrintStream对应的PrintWriter类,即使遇到了文本换行标识符(\n),PrintWriter类也不会自动清空缓冲区。
PrintWriter的println方法能根据操作系统的不同而生成相应的文本换行标志符。在Windoes下的文本换行标志符是"\r\n",而Linux下的文本换行标志符是"\n".
十三
ObjectInputStream与ObjectoutputStream类
ObjectInputstream和ObjectOutputStream这两个包装类,用于从底层输入流中读取对象类型的数据和对象类型的数据写入到底层输出流。
ObjectInputStream与ObjectOutputStream类所读写的对象必须实现了Serializable接口。对象中的transient和static类型的成员变量不会被读取和写入。
一个可以被序列化的Myclass类定义:
public class MyClass implements Serializable
{
public transient Thread t;
private String customerID;
private int total;
}
序列化的类可以解决不同操作系统之间的差异。
编程举例:创建了一个可序列化的学生对象,并用ObjectOutputStream类把它存储到一个文件(Mytext.text)中,然后再用ObjectInputStream类把存储的数据读取到一个学生对象中,即恢复保存的学生对象。
以上实例告诉我们在网络流中可以利用这两个类轻松传送对象数据。
代码如下
import java.IO.*
import java.IO.serializable;
public class Serialization
{
public static void main(String[] args)
{
Student stu1=new Student(1,"zz");
Student stu2=new student(2,"zdf");
FileOutputStream fos=new FileOutputStream("student.txt");
ObjectOutputStream os=new ObjectOutputStream(fos);
os.writeObject(stu1);
os.writeObject(stu2);
os.close();
FileInputStream fis=new FileInputStream("student.txt");
ObjectInputStream ois=new ObjectinputStream(fis);
stu1=(Student)ois.readObject();
stu2=(Student)ois.readObject();
ois.close();
System.out.println("id:"+stu1.ID);
System.out.println("name"+stu1.Name);
System.out.println("name"+stu2.ID);
System.out.println("name"+stu2.Name);
}
}
class Student
{ public Student(int id,String name)
{
this.ID=id;
this.Name=name;
}
int ID;
String Name;
}
字节流与字符流的转换
InputStreamReader和OutputStreamWriter,是用于将字节流转换成字符流来读写的两个类,InputStreamReader可以将一个字节流中的字节解码成字符后读取,OutputStreamReader可以将字符编码成字节后写入到一个字节流。
InputStreamReader的两个主要的构造函数:
- InputStreamReader(InputStream in)
- InputStreamReader(InputStream in,String CharseName)
OutputStreamWriter的两个主要的构造函数:
- OutputStreamWriter(OutputStream in)
- OutputStreamwriter(OutputStream in,String CharseName)
避免频繁的恶在字符和字节间进行转换,最好不要直接使用
InputStreamReader和OutputStreamWriter类来读写数据,应尽量使用 BufferedWriter类包装OutputStreamWriter类,用BufferedReader类包装InputStreamReader。
十四
Java程序与其他进程的数据通信
在Java程序中可以用Process类的实例对象来表示子进程,子进程的标准输入和输出不再连接到键盘和显示器,而是以管道流的形式连接到父进程的一个输出流和输入流对象上。
调用Process类的getOutputStream和getInputStream方法可以获得连接到子进程的输出流和输入流对象。
编程实例:在TestInout类中启动java.exe命令执行另外一个MyTest类,TestOut和MyTest通过进程间的管道相互传递数据。
验证管道缓冲区满后,将发生下面的哪种情况:
(1) 新的数据写入时,将最前面写入的数据挤出去,从而发生数据丢失。
(2) 与PipedInputStream相连的PipedoutputStream无法再写入新的数据,PipedOutputStream.write方法处于阻塞状态。
(3) 与PipedInputstream相连的PipedOutputStream无法再写入新的数据,PipedOutputStream.write方法抛出异常。
记住调用Process类的destroy方法结束子进程的运行。
提高程序的运行效率
1)
for(int i=0;i<str.length();i++)
{
......
}
与下面的代码的比较:
int len=strlength();
for(int i=0;i<len;i++)
{
......
}
上面程序相对效率要高,不用每次都计算字符串的长度。
2)
byte[] buf=new byte[1024]
while(true)
{
byte[] buf=new byte[1024];
对 buf元素的操作语句
}
与下面代码的比较:
while(true)
{
byte[] buf=new byte[1024];
对buf元素的操作语句
}
在循环中每次循环都要创建对象数组效率较低。
十五
字节输入流类的类层次关系图
最上层为InputStream (FileInputStream ObjectInputStream PipedInputStream SequenceInputStream FilterInputStream(DataInputStream PushbackinputStream BufferedInputStream LinenumberInputStream) StringBufferInputStream ByteArrayInputStream )
public class SequenceInputStreamextends InputStreamSequenceInputStream 表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。
SequenceInputStream 将若干个InputStream类合并成一个逻辑上整体的Inputstream类 来读取。
字节输出流类的类关系最上层的类为OutputStream(FileOutputStream ObjectOutoutStream FilterOutputStream(DataOutputstream BufferOutputStream PrintStream) PipedOutputStream ByteArrayOutputStream)
字符输入流类的类关系图
最上层的类为Reader(BufferedReader(LinenumberReader) CharArrayReader StringReader InputStreamReader(FileReader) PipedReader FilterReader(PushbackReader))
字符输出流类
最上层的是Writer(BufferedWriter CharArrayWriter StringWriter OutputSreamWriter(FileWriter) PrintWriter PipedWriter FilterWriter)
Decorator设计模式
在程序设计中用一个对象(the Decorators)包装另外的一个对象,这是一种被称为Decorator的设计模式。
如果要设计自己的IO包装类,这个类需要继承以FilterXXX命名的类,例如,设计一对输入输出包装类:RecordInputStream和RecordOutputStream,来完成从数据库文件中读取记录和往数据库文件中写入记录。
如下编程举例
Exception类从Throwwable类继承的三个printStackTrace方法的定义如下:
-public void printStackTrace()
-public void printStacktrace(Printstream s)
-public void printStackTrace(PrintWriter s)
该如何把printStackTrace方法打出的详细异常信息存储到一个字符串中?
字符串与输出流的连接对象就是StringWriter。可以将StringWriter当做字符串输出流。
思考题(一):
1编写一个程序,将一个目录及子目录下的所有txt类型的文本文件中的内容合并到若干个新的文件中,当一个新产生的文件中存储的内容达到1Mbytes时,剩下的内容存储到第二个新的文件中,依次往下,新产生的文本文件名依次为1.txt,2.txt......。
2用自己的话叙述什么是流,什么是节点流,什么是包装类。
3编写一个函数,把Stringreader输入流中所有英文字母变成大写字母,并将结果写入到一个StringWriter输出流对象中。然后,用这个函数将一个字符串中的所有字符转换成大写。
4用记事本程序创建一个内容只有"中国"这两个字符的文本文件,然后用不同的编码格式分别保存这个文本文件。用 UbicodeEdit打开这些不同编码格式的文件,并用十六进制方式查看他们的内容,结合你所看到的内容来叙述各种编码格式之间的差异。
思考题(二):
5 编写下面的程序代码,分析和观察程序的运行结果:
import java.io.*;
public class InputReader
{
public static void main(String[] args) throws exception
{
InputStreamReader isr=new InputStreamreader(System.in,"iso8859-1x");
BufferedReader kr=new BufferedReader(isr);
String strLine=kr.readLine();
for(int i=0;i<strLine.length();i++)
{
System.out.println(integer.toHexString(int)strLine.charAt(i));
}
isr.close();
System.out.println(strLine);
}
}