JAVA 流与文件
流
InputStream和OutputStream是所有的输入流和输出流的超类。他们两个都是抽象类。
read方法和write方法都是阻塞方法,这意味着如果不能里可以写入或者读取,比如因为网络问题,那么当前线程将会被阻塞。
InputStream的API:
方法摘要 | |
int | available() |
void | close() |
void | mark(int readlimit) |
boolean | markSupported() |
abstract int | read() |
int | read(byte[] b) |
int | read(byte[] b, int off, int len) |
void | reset() |
long | skip(long n) |
OutStream的API:
方法摘要 | |
void | close() |
void | flush() |
void | write(byte[] b) |
void | write(byte[] b, int off, int len) |
abstract void | write(int b) |
Reader和Writer
用于读取和写入Unicode字符流的抽象类。和字节流的API类似。
看下面的API,何止是类似,兼职就是一样。因为不论是读取字节还是读取Unicode字符(这里其实是Unicode编码单元,2个字节),都是使用的int类型(4个字节)作为参数。
abstract void | close() |
void | mark(int readAheadLimit) |
boolean | markSupported() |
int | read() |
int | read(char[] cbuf) |
abstract int | read(char[] cbuf, int off, int len) |
int | read(CharBuffer target) |
boolean | ready() |
void | reset() |
long | skip(long n) |
组合流过滤器
如下使用:
DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream("person.dat")));
DataInputStream/ DataOutStream
数据输入流允许应用程序以与机器无关方式从基础输入流中读取基本 Java 数据类型。应用程序可以使用数据输出流写入稍后由数据输入流读取的数据。
通过它的方法可以看出它的常用使用场景:
PrintWriter和DataOutStream的API基本一致,一个是使用流的方式输出,一种是使用文本的方式输出,个人理解如果,输出后,需要打开文件看到显示效果不想看到乱码,那么要用PrintWriter,如果输出并不是为了查看,那么使用DataOutStream的话,性能应该会更高。
ByteArrayInputStream/ ByteArrayOutputStream
写入一个字节数组。
BufferedInputStream/ BufferedOutStream
通过在内部维护一个缓冲区,每次执行read方法的时候,它底层其实是读取了固定大小数目的字节到内存的缓冲区中(通过构造参数可以配置每次读取字节的数目,也就是缓冲区的大小)中,然后下次读取的时候,直接从内存的缓冲区中拿,不同在通过原始的方式(如网络或者磁盘)。
write的时候,也是只写入到内部的缓冲区中,当缓冲区满了才会做真正的写入操作。
一般将一个大的文件读取到内存,一般这么写:
- InputStream in=new FileInputStream(FILENAME);
- byte[] b=new byte[8192];
- int l=0;
- while(in.read(b,0,8192)!=-1){
- }
这种是自己实现了一次性读取多个的方式。一般配置为8k,也就是8192。
也可以使用BufferedInputStream,如下:
- BufferedInputStream in=new BufferedInputStream(new FileInputStream(FILENAME));
- byte[] b=new byte[8192];
- int l=0;
- while(in.read(b,0,8192)!=-1){
- }
注意,这里的BufferedInputStream是在FileInputStream的外层,在调用BufferedInputStream.read()的时候,其实BufferedInputStream调用了FileInputStream的read(byte[] b, int off, int len)。
个人认为,如果是从文件中读取,那么还是使用第一种方式比较好,虽然第二种方式也能使用缓存,但是毕竟执行8000多遍read。
FilterOutputStream/ FilterInputStream
此类是过滤输出流的所有类的超类。它的功能很简单,就是构造器接受一个Stream,然后将上层的调用传递给它。
RandomAccessFile
void | seek(long pos) |
InputStreamReader/OutputStreamWriter
InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,否则可能接受平台默认的字符集。
每次调用 InputStreamReader 中的一个 read() 方法都会导致从基础输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从基础流读取更多的字节,使其超过满足当前读取操作所需的字节。
为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader。例如:
BufferedReader in
= new BufferedReader(new InputStreamReader(System.in));
OutputStreamWriter类似于上面:
OutputStreamWriter 是字符流通向字节流的桥梁:使用指定的 charset 将要向其写入的字符编码为字节。它使用的字符集可以由名称指定或显式给定,否则可能接受平台默认的字符集。
每次调用 write() 方法都会针对给定的字符(或字符集)调用编码转换器。在写入基础输出流之前,得到的这些字节会在缓冲区累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。注意,传递到此 write() 方法的字符是未缓冲的。
为了达到最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中以避免频繁调用转换器。例如:
Writer out
= new BufferedWriter(new OutputStreamWriter(System.out));
注意:根据上面的描述,OutputStreamWriter是自带缓冲区的。但是InputStreamReader没有。
BufferedReader/BufferedWriter
BufferedReader:
从字符输入流中读取文本,缓冲各个字符,从而提供字符、数组和行的高效读取。
可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。
通常,Reader 所作的每个读取请求都会导致对基础字符或字节流进行相应的读取请求。因此,建议用 BufferedReader 包装所有其 read() 操作可能开销很高的 Reader(如 FileReader 和 InputStreamReader)。例如,
BufferedReader in
= new BufferedReader(new FileReader("foo.in"));
将缓冲指定文件的输入。如果没有缓冲,则每次调用 read() 或 readLine() 都会导致从文件中读取字节,并将其转换为字符后返回,而这是极其低效的。
BufferedReader有一个readLine()方法。可以读取一行。这个在InputStreamReader和FileReader中是没有的。
FileReader/ FileWriter
用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先在 FileInputStream 上构造一个 InputStreamReader。
也就是说,FileReader内部的实现就是:
new InputStreamReader(new File…),但是InputStreamReader是可以指定字符集编码的,但是使用的是默认值。
序列化
类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。
java.io.ObjectOutputStream:表示对象输出流
它的writeObject(Object obj)方法可以对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
java.io.ObjectInputStream:表示对象输入流
它的readObject()方法源输入流中读取字节序列,再把它们反序列化成为一个对象,并将其返回。
只有实现了Serializable或Externalizable接口的类的对象才能被序列化,否则抛出异常。
对象的序列化会将对象中的所有属性,包含私有属性都序列化。
对象的序列化会将对象中的引用指向的对象序列化,而不是引用本身。
可以讲一个对象序列化到一个字节数组然后再反序列化回来实现对对象的深拷贝。
在反序列化的时候,如果类的定义发生了变化,那么ObjectInputStream将"尽力"将流对象转换为当前的类版本。不能转换的字段将会被设置为默认值。