Java I/O是Java中很重要的组成部分
一.以字节为向导的Stream InputStream/OutputStream
Java I/O最基本的概念就是输入流与输出流,即InputStream(输入流)和OutputStream(输出流)
InputStream
最基本的字节输入流,抽象类,定义了读取原始字节的所有基本方法
- read() 读取一个字节的方法
- close() 关闭stream方法,这个是每次在用完流之后必须调用的方法。
- available() 返回stream中的可读字节数,inputstream类中的这个方法始终返回的是0,这个方法需要子类去实现。
- skip(long n) 从stream中跳过long类型参数个位置
- read(byte b[]) 一次性读取内容到缓冲字节数组
- read(byte b[],int off,int len) 从数据流中的哪个位置offset开始读长度为len的内容到缓冲字节数组
下面还有三个方法:
- mark(int readlimit) 用于标记stream的作用
- markSupported() 返回的是boolean类型,因为不是所有的stream都可以调用mark方法的,这个方法就是用来判断stream是否可以调用mark方法和reset方法
- reset() 这个方法和mark方法一起使用的,让stream回到mark的位置。
OutputStream
最基本的字节输出流,抽象类,定义了读取原始字节的所有基本方法
- write(int b) 写入一个字节到stream中
- close() 关闭流,这个是在操作完stream之后必须要调用的方法
- flush() 这个方法是用来刷新stream中的数据,让缓冲区中的数据强制的输出
- write(byte b[]) 写入一个byte数组到stream中
- write(byte b[], int off, int len) 把byte数组中从offset开始处写入长度为len的数据
总结
InputStream和OutputStream定义了I/O领域最基础的行为,也就是读取和写入一个字节,同时使用了模板方法将读取和写入的行为进行了适当扩展。
扩展一:对I/O流的继承
InputStream和OutputStream都是抽象类,它们仅仅定义了I/O领域最基础的方法,但不涉及具体实现。针对不同的数据来源,InputStream和OutputStream存在三种实现:一种是基于内存的ByteArrayInputStream/ByteArrayOutputStream,一种是基于磁盘文件的FileInputStream/FileOutputStream,还有一种是基于网络的SocketInputStream/SocketOutputStream。
FileInputStream/FileOutputStream
读取写入的源是操作系统的文件使用native方法进行底层文件的读取
try
{
//使用FileInputStream和FileOutputStream进行文件复制
FileInputStream fis=new FileInputStream("a.txt");
FileOutputStream fos=new FileOutputStream("b.txt");
int read;
//read=fis.read();
byte b[]=new byte[1024];
//读取文件,存入字节数组b,返回读取到的字符数,存入read,默认每次将b数组装满
read=fis.read(b);
while(read!=-1)
{
fos.write(b,0,read);
read=fis.read(b);
//read=fis.read();
}
fis.close();
fos.close();
}
catch (IOException e)
{
e.printStackTrace();
}
ByteArrayInputStream/ByteArrayOutputStream
读取写入的源是内存的一个数组,比较少见
SocketInputStream/SocketOutputStream
读取写入远程服务器的数据流使用native方法进行底层文件的读取
除上述3种以外还有别的实现如下
StringBufferInputStream/StringBufferOutputStream
据指定串创建一个读取数据的输入流串
PipedInputStream/PipedOutputStream
实现了pipe的概念,主要在线程中使用,管道输入流是指一个通讯管道的接收端。
一个线程通过管道输出流发送数据,而另一个线程通过管道输入流读取数据,这样可实现两个线程间的通讯。
SequenceInputStream/SequenceOutputStream
把多个In(Out)putStream合并为一个In(Out)putStream,“序列输入(出)流”类允许应用程序把几个输入(出)流连续地合并起来,
并且使它们像单个输入(出)流一样出现。每个输入(出)流依次被读取,直到到达该流的末尾。
然后“序列输入(出)流”类关闭这个流并自动地切换到下一个输入(出)流。
扩展二:对I/O流的扩展
FilterInputStream/FilterOutputStream
FilterInputStream是InputStream一个特殊的子类,它内部有一个很重要的变量如下
protected volatile InputStream in;
protected FilterInputStream(InputStream in) {
this.in = in;
}
这说明了FilterInputStream在实例化的时候,要传一个InputStream类的对象进来。
这个类的特殊之处,就是包含了一个InputStream,使得可以在这个InputStream基础上进行多种封装,从而达到装饰的目的。
这个类本身作用不大,但是他的子类比较有作用
DataInputStream/DataOutputStream
这两个类继承了FilterInputStream/FilterOutputStream,用来实现将java基本类型转换成二进制来进行读写操作,readInt,readFloat,readDouble...这样可以直接从stream中读取基本类型的数据
BufferedInputStream/BufferedOutputStream
继承了FilterInputStream,实现了输入流处理中的缓冲的功能。底层的流会先被读取到一个字节数组中,用户使用BufferedInputStream读取数据的时候,会先读取字节数组中的数据,读完了才会调用底层的流进行进一步的读取。这种方法可以提升读取的性能。继承了FilterOutputStream,实现了输出流处理中的缓冲功能。当用户写入数据的时候,其实是先写入到BufferedOutputStream的一个字节数组中,当这个字节数组满了,才会真正调用底层的输出流执行输出动作。这种方法可以提升写入的性能。在使用BufferedOutputStream的写入功能时,一定要使用flush,因为缓冲数组不满的时候是不会写入底层流的,在写入最后一点数据的时候,缓冲数据不一定被填满,这时候就需要调用flush进行强制刷新。
PrintStream
继承FilterOutputStream,这个类的print和println方法可以把java的一些基本类型数据转换成字节写入到底层输出流,但是PrintStream对String的转换是平台相关的,不同的平台会有不同的编码,所以写入到底层的字节也不同,因此PrintStream只适合于测试输出,不适合于一般的I/O操作,特别是网络流。
PushbackInputStream
继承了FilterInputStream,提供了一种回退的机制,可以实现unread,本质是使用缓冲数组实现了,也就是说,回退的范围是有限的。
二.以字符为向导的Stream Reader/Writer
InputStream和OutputStream是面向字节的,而人类的习惯是面向字符,因此InputStream和OutputStream对于程序猿的用户体验不是太好,于是就需要提供一些面向字符的流。由于DataInputStream/DataOutputStream在跨平台的情况下存在问题,因此,java设计者干脆仿照InputStream和OutputStream重新设计了一套面向字符的I/O,也就是Reader/Writer
Reader
基本的字符输入流,是个抽象类
方法与InputStream对应
Writer
基本的字符输出流,是个抽象类
方法与OutputStream对应
扩展一:对I/O流的继承
CharArrayReader/CharArrayWriter
与前面ByteArrayInputStream/ByteArrayOutputStream对应
StringReader/StringWriter
与前面StringBufferInputStream/StringBufferOutputStream对应
FileReader/FileWriter
与前面FileInputStream/FileOutputStream对应
PipedReader/PipedWriter
与前面PipedInputStream/PipedOutputStream对应
扩展二:对I/O流的扩展
FilterReader/FilterWriter
BufferedReader/BufferedWriter
PushbackReader
PrintWriter
三.两种不同导向的Stream之间的转换
由于计算机只识别字节,所以Reader/Writer的数据来源最终还是字节,而他们无法直接和字节打交道,这时候就需要一个中介者将Reader/Writer和InputStream和OutputStream进行打通,于是就有了InputStreamReader和OutputStreamWriter
InputStreamReader 类是从字节流到字符流的桥梁:它读入字节,并根据指定的编码方式,将之转换为字符流。
使用的编码方式可能由名称指定,或平台可接受的缺省编码方式。
InputStreamReader 的 read() 方法之一的每次调用,可能促使从基本字节输入流中读取一个或多个字节。
为了达到更高效率,考虑用 BufferedReader 封装 InputStreamReader ,
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
![](/Users/admin/Documents/Life/I:O/3400180-bce9503f7c9ee657.png =500x300)