参考:
java.io
包主要涉及文件访问、网络数据流、内存缓冲访问、线程内部通信(管道)、缓冲、过滤、解析、读写文件(Readers/Writers)、读写基本类型数据(long,int etc)、读写对象等输入输出。
字节流
InputStream
字节输入流
- ByteArrayInputStream
- FileInputStream
- FilterInputStream
- BufferedInputStream
- DataInputStream
LineNumberInputStream- PushbackInputStream
- ObjectInputStream
- PipedInputStream
- SequenceInputStream
- StringBufferStream
InputStream
1 | public abstract class InputStream implements Closeable { |
ByteArrayInputStream
ByteArrayInputStream 是字节数组输入流。它继承于InputStream。
它包含一个内部缓冲区,该缓冲区包含从流中读取的字节;通俗点说,它的内部缓冲区就是一个字节数组,而ByteArrayInputStream本质就是通过字节数组来实现的。
我们都知道,InputStream通过read()向外提供接口,供它们来读取字节数据;而ByteArrayInputStream 的内部额外的定义了一个计数器,它被用来跟踪 read() 方法要读取的下一个字节。
1 | // 构造函数; |
ByteArrayInputStream实际上是通过“字节数组”去保存数据。
- 通过ByteArrayInputStream(byte buf[]) 或 ByteArrayInputStream(byte buf[], int offset, int length) ,我们可以根据buf数组来创建字节流对象。
- read()的作用是从字节流中“读取下一个字节”。
- read(byte[] buffer, int offset, int length)的作用是从字节流读取字节数据,并写入到字节数组buffer中。offset是将字节写入到buffer的起始位置,length是写入的字节的长度。
- markSupported()是判断字节流是否支持“标记功能”。它一直返回true。
- mark(int readlimit)的作用是记录标记位置。记录标记位置之后,某一时刻调用reset()则将“字节流下一个被读取的位置”重置到“mark(int readlimit)所标记的位置”;也就是说,reset()之后再读取字节流时,是从mark(int readlimit)所标记的位置开始读取。
PipedInputStream
在java中,PipedOutputStream和PipedInputStream分别是管道输出流和管道输入流。
它们的作用是让多线程可以通过管道进行线程间的通讯。在使用管道通信时,必须将PipedOutputStream和PipedInputStream配套使用。
使用管道通信时,大致的流程是:我们在线程A中向PipedOutputStream中写入数据,这些数据会自动的发送到与PipedOutputStream对应的PipedInputStream中,进而存储在PipedInputStream的缓冲中;此时,线程B通过读取PipedInputStream中的数据。就可以实现,线程A和线程B的通信。
PipedInputStream类中属性有closedByWriter
、closedByReader
、connected
、readSide
、writeSide
、buffer
、in
、out
,分别代码被写入线程关闭连接,被读取线程关闭连接,是否连接,读取线程,写入线程,内部缓冲byte数组,写入数,读取数。通过协调读取和写入线程,一方面从PipedOutputStream接收数据,一方面当调用read()
、read(byte[], int, int)
方法是从内部缓存byte数组中读取数据。
ObjectInputStream
ObjectInputStream 和 ObjectOutputStream 的作用是,对基本数据和对象进行序列化操作支持。
当我们需要读取ObjectOutputStream存储的“基本数据或对象”时,可以创建“文件输入流”对应的ObjectInputStream,进而读取出这些“基本数据或对象”。
注意: 只有支持 java.io.Serializable 或 java.io.Externalizable 接口的对象才能被ObjectInputStream/ObjectOutputStream所操作!
1 | // 构造函数; |
FileInputStream
FileInputStream 是文件输入流,它继承于InputStream。
通常,我们使用FileInputStream从某个文件中获得输入字节。
1 | FileInputStream(File file); // 构造函数1:创建“File对象”对应的“文件输入流” |
FilterInputStream
FilterInputStream 的作用是用来“封装其它的输入流,并为它们提供额外的功能”。它的常用的子类有BufferedInputStream和DataInputStream。
BufferedInputStream的作用就是为“输入流提供缓冲功能,以及mark()和reset()功能”。
DataInputStream 是用来装饰其它输入流,它“允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型”。应用程序可以使用DataOutputStream(数据输出流)写入由DataInputStream(数据输入流)读取的数据。
FilterInputStream很明显使用了装饰器模式 ( Decorator )。
BufferedInputStream
BufferedInputStream 是缓冲输入流。它继承于FilterInputStream。
BufferedInputStream 的作用是为另一个输入流添加一些功能,例如,提供“缓冲功能”以及支持“mark()标记”和“reset()重置方法”。
BufferedInputStream 本质上是通过一个内部缓冲区数组实现的。例如,在新建某输入流对应的BufferedInputStream后,当我们通过read()读取输入流的数据时,BufferedInputStream会将该输入流的数据分批的填入到缓冲区中。每当缓冲区中的数据被读完之后,输入流会再次填充数据缓冲区;如此反复,直到我们读完输入流数据位置。
1 | BufferedInputStream(InputStream in); |
DataInputStream
DataInputStream 是数据输入流。它继承于FilterInputStream。
DataInputStream 是用来装饰其它输入流,它“允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型”。应用程序可以使用DataOutputStream(数据输出流)写入由DataInputStream(数据输入流)读取的数据。
1 | DataInputStream(InputStream in); |
OutputStream
字节输出流
- ByteArrayOutputStream
- FileOutputStream
- FilterOutputStream
- BufferedOutputStream
- DataOutputStream
- PrintStream
- ObjectOutputStream
- PipedOutputStream
OutputStream
1 | public abstract class OutputStream implements Closeable, Flushable { |
ByteArrayOutputStream
ByteArrayOutputStream 是字节数组输出流。它继承于OutputStream。
ByteArrayOutputStream 中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长,“新容量”的初始化 = “旧容量”x2。可使用 toByteArray() 和 toString() 获取数据。
1 | // 构造函数; |
ByteArrayOutputStream实际上是将字节数据写入到“字节数组”中去。
- 通过ByteArrayOutputStream()创建的“字节数组输出流”对应的字节数组大小是32。
- 通过ByteArrayOutputStream(int size) 创建“字节数组输出流”,它对应的字节数组大小是size。
- write(int oneByte)的作用将int类型的oneByte换成byte类型,然后写入到输出流中。
- write(byte[] buffer, int offset, int len) 是将字节数组buffer写入到输出流中,offset是从buffer中读取数据的起始偏移位置,len是读取的长度。
- writeTo(OutputStream out) 将该“字节数组输出流”的数据全部写入到“输出流out”中。
PipedOutputStream
在java中,PipedOutputStream和PipedInputStream分别是管道输出流和管道输入流。
它们的作用是让多线程可以通过管道进行线程间的通讯。在使用管道通信时,必须将PipedOutputStream和PipedInputStream配套使用。
使用管道通信时,大致的流程是:我们在线程A中向PipedOutputStream中写入数据,这些数据会自动的发送到与PipedOutputStream对应的PipedInputStream中,进而存储在PipedInputStream的缓冲中;此时,线程B通过读取PipedInputStream中的数据。就可以实现,线程A和线程B的通信。
PipedOutputStream类中持有PipedInputStream实例,调用write/flush等方法时均对PipedInputStream实例进行操作。
ObjectOutputStream
ObjectInputStream 和 ObjectOutputStream 的作用是,对基本数据和对象进行序列化操作支持。
创建“文件输出流”对应的ObjectOutputStream对象,该ObjectOutputStream对象能提供对“基本数据或对象”的持久存储。
注意: 只有支持 java.io.Serializable 或 java.io.Externalizable 接口的对象才能被ObjectInputStream/ObjectOutputStream所操作!
1 | // 构造函数; |
FileOutputStream
FileOutputStream 是文件输出流,它继承于OutputStream。
通常,我们使用FileOutputStream 将数据写入 File 或 FileDescriptor 的输出流。
1 | FileOutputStream(File file); // 构造函数1:创建“File对象”对应的“文件输入流”;默认“追加模式”是false,即“写到输出的流内容”不是以追加的方式添加到文件中。 |
FilterOutputStream
FilterOutputStream 的作用是用来“封装其它的输出流,并为它们提供额外的功能”。它主要包括BufferedOutputStream, DataOutputStream和PrintStream。
BufferedOutputStream的作用就是为“输出流提供缓冲功能”。
DataOutputStream 是用来装饰其它输出流,将DataOutputStream和DataInputStream输入流配合使用,“允许应用程序以与机器无关方式从底层输入流中读写基本 Java 数据类型”。
PrintStream 是用来装饰其它输出流。它能为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。
FilteroutputStream很明显使用了装饰器模式 ( Decorator )。
BufferedOutputStream
BufferedOutputStream 是缓冲输出流。它继承于FilterOutputStream。
BufferedOutputStream 的作用是为另一个输出流提供“缓冲功能”。
1 | BufferedOutputStream(OutputStream out); |
BufferedOutputStream的源码非常简单,这里就BufferedOutputStream的思想进行简单说明:BufferedOutputStream通过字节数组来缓冲数据,当缓冲区满或者用户调用flush()函数时,它就会将缓冲区的数据写入到输出流中。
DataOutputStream
DataOutputStream 是数据输出流。它继承于FilterOutputStream。
DataOutputStream 是用来装饰其它输出流,将DataOutputStream和DataInputStream输入流配合使用,“允许应用程序以与机器无关方式从底层输入流中读写基本 Java 数据类型”。
PrintStream
PrintStream 是打印输出流,它继承于FilterOutputStream。
PrintStream 是用来装饰其它输出流。它能为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。
与其他输出流不同,PrintStream 永远不会抛出 IOException;它产生的IOException会被自身的函数所捕获并设置错误标记, 用户可以通过 checkError() 返回错误标记,从而查看PrintStream内部是否产生了IOException。
另外,PrintStream 提供了自动flush 和 字符集设置功能。所谓自动flush,就是往PrintStream写入的数据会立刻调用flush()函数。
1 | /* |
System.out.Println
out的定义
一个PrintStream实例
1
2
3public final class System {
public final static PrintStream out = null;
}out的初始化
1
2
3
4
5
6
7pubilc final class System {
private static void initializeSystemClass() {
//省略其他代码...
FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true));
}
}FileDescriptor.out是啥
1
2
3
4
5
6
7
8
9
10
11
12
13
14public final class FileDescriptor {
private int fd;
/**
* A handle to the standard output stream. Usually, this file
* descriptor is not used directly, but rather via the output stream
* known as {@code System.out}.
* @see java.lang.System#out
*/
public static final FileDescriptor out = new FileDescriptor(1);
private FileDescriptor(int fd) {
this.fd = fd;
useCount = new AtomicInteger();
}
}FileDescriptor.out是一个FileDescriptor实例,其fd=1,正是标准输出的标识符。
setOut0方法
setOut0()是一个native本地方法,其功能是设置out为传入的参数
1
2
3public final class System {
private static native void setOut0(PrintStream out);
}System.out.println
所以System.out其实是标准输出的PrintStream实例,而println方法正是调用的PrintStream的println方法。这样PrintStream中的多个输出方法均可以通过System.out调用。
字符流
Reader
字符输入流
- BufferedReader
- LineNumberReader
- CharArrayReader
- FilterReader
- PushbackReader
- InputStreamReader
- FileReader
- PipedReader
- StringReader
Reader
1 | public abstract class Reader implements Readable, Closeable { |
CharArrayReader
CharArrayReader 是字符数组输入流。它和ByteArrayInputStream类似,只不过ByteArrayInputStream是字节数组输入流,而CharArray是字符数组输入流。CharArrayReader 是用于读取字符数组,它继承于Reader。操作的数据是以字符为单位!
1 | CharArrayReader(char[] buf); |
PipedReader
PipedReader 是字符管道输入流,它继承于Writer。
PipedWriter和PipedReader的作用是可以通过管道进行线程间的通讯。在使用管道通信时,必须将PipedWriter和PipedReader配套使用。
1 | public PipedReader(); |
InpustreamReader
InputStreamReader和OutputStreamWriter 是字节流通向字符流的桥梁:它使用指定的 charset 读写字节并将其解码为字符。
InputStreamReader 的作用是将“字节输入流”转换成“字符输入流”。它继承于Reader。
1 | public InputStreamReader(InputStream in); |
InputStreamReader内部持有一个StreamDecoder sd
的私有属性,大部分的功能由StreamDecoder
实现。
FileReader
FileReader 是用于读取字符流的类,它继承于InputStreamReader。要读取原始字节流,请考虑使用 FileInputStream。
1 | package java.io; |
BufferedReader
BufferedReader 是缓冲字符输入流。它继承于Reader。
BufferedReader 的作用是为其他字符输入流添加一些缓冲功能。
1 | BufferedReader(Reader in); |
Writer
字符输出流
- BufferedWriter
- CharArrayWriter
- FilterWriter
- OutputStreamWriter
- FileWriter
- PipedWriter
- PrintWriter
- StringWriter
Writer
1 | public abstract class Writer implements Appendable, Closeable, Flushable { |
CharArrayWriter
CharArrayReader 用于写入数据符,它继承于Writer。操作的数据是以字符为单位!
1 | CharArrayWriter(); |
PipedWrite
PipedWriter 是字符管道输出流,它继承于Writer。
PipedWriter和PipedReader的作用是可以通过管道进行线程间的通讯。在使用管道通信时,必须将PipedWriter和PipedReader配套使用。
1 | public PipedWriter(); |
OutputStreamWriter
InputStreamReader和OutputStreamWriter 是字节流通向字符流的桥梁:它使用指定的 charset 读写字节并将其解码为字符。
OutputStreamWriter 的作用是将“字节输出流”转换成“字符输出流”。它继承于Writer。
1 | public OutputStreamWriter(OutputStream out); |
OutputStreamWriter内部持有一个StreamEecoder se
的私有属性,大部分的功能由StreamDecoder
实现。
FileWriter
FileWriter 是用于写入字符流的类,它继承于OutputStreamWriter。要写入原始字节流,请考虑使用 FileOutputStream。
1 | package java.io; |
BufferedWriter
BufferedWriter 是缓冲字符输出流。它继承于Writer。
BufferedWriter 的作用是为其他字符输出流添加一些缓冲功能。
1 | // 构造函数 |
PrintWriter
PrintWriter 是字符类型的打印输出流,它继承于Writer。
PrintStream 用于向文本输出流打印对象的格式化表示形式。它实现在 PrintStream 中的所有 print 方法。它不包含用于写入原始字节的方法,对于这些字节,程序应该使用未编码的字节流进行写入。
1 | PrintWriter(OutputStream out); |
File
File
File 是“文件”和“目录路径名”的抽象表示形式。
File 直接继承于Object,实现了Serializable接口和Comparable接口。实现Serializable接口,意味着File对象支持序列化操作。而实现Comparable接口,意味着File对象之间可以比较大小;File能直接被存储在有序集合(如TreeSet、TreeMap中)。
1 | // 静态成员 |
FileDescriptor
FileDescriptor 是“文件描述符”。
FileDescriptor 可以被用来表示开放文件、开放套接字等。
以FileDescriptor表示文件来说:当FileDescriptor表示某文件时,我们可以通俗的将FileDescriptor看成是该文件。但是,我们不能直接通过FileDescriptor对该文件进行操作;若需要通过FileDescriptor对该文件进行操作,则需要新创建FileDescriptor对应的FileOutputStream,再对文件进行操作。
FileDescriptor 类中三个类属性in
/out
/err
,分别为标准输入/标准输出/标准错误,Java提供了 System.in
/System.out
/System.error
对应这三个属性,以简化对其的使用。
1 |
|
RandomAccessFile
RandomAccessFile 是随机访问文件(包括读/写)的类。它支持对文件随机访问的读取和写入,即我们可以从指定的位置读取/写入文件数据。
需要注意的是,RandomAccessFile 虽然属于java.io包,但它不是InputStream或者OutputStream的子类;它也不同于FileInputStream和FileOutputStream。 FileInputStream 只能对文件进行读操作,而FileOutputStream 只能对文件进行写操作;但是,RandomAccessFile 同时支持文件的读和写,并且它支持随机访问。
1 | public class RandomAccessFile implements DataOutput, DataInput, Closeable { |
RandomAccessFile共有4种模式:”r”, “rw”, “rws”和”rwd”。
1 | "r" 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。 |
“rw”, “rws”, “rwd” 的区别。
当操作的文件是存储在本地的基础存储设备上时(如硬盘, NandFlash等),”rws” 或 “rwd”, “rw” 才有区别。
当模式是 “rws” 并且 操作的是基础存储设备上的文件;那么,每次“更改文件内容[如write()写入数据]” 或 “修改文件元数据(如文件的mtime)”时,都会将这些改变同步到基础存储设备上。
当模式是 “rwd” 并且 操作的是基础存储设备上的文件;那么,每次“更改文件内容[如write()写入数据]”时,都会将这些改变同步到基础存储设备上。
当模式是 “rw” 并且 操作的是基础存储设备上的文件;那么,关闭文件时,会将“文件内容的修改”同步到基础存储设备上。至于,“更改文件内容”时,是否会立即同步,取决于系统底层实现。