目录
一 介绍
一个io流表示一个输入源或输出目的,这些源和目的包括磁盘、设备、其他程序和内存。流的模型是一个数据的序列。
二 类继承结构
流分为字节流和字符流,分别用InputStream、OutputStream和Reader、Writer表示。下面是它们的类继承关系:
java.lang.Object
- java.io.InputStream (implements java.io.Closeable)
- java.io.ByteArrayInputStream
- java.io.FileInputStream
- java.io.FilterInputStream
- java.io.BufferedInputStream
- java.io.DataInputStream (implements java.io.DataInput)
- java.io.LineNumberInputStream
- java.io.PushbackInputStream
- java.io.ObjectInputStream (implements java.io.ObjectInput, java.io.ObjectStreamConstants)
- java.io.PipedInputStream
- java.io.SequenceInputStream
- java.io.StringBufferInputStream
- java.io.OutputStream (implements java.io.Closeable, java.io.Flushable)
- java.io.ByteArrayOutputStream
- java.io.FileOutputStream
- java.io.FilterOutputStream
- java.io.BufferedOutputStream
- java.io.DataOutputStream (implements java.io.DataOutput)
- java.io.PrintStream (implements java.lang.Appendable, java.io.Closeable)
- java.io.ObjectOutputStream (implements java.io.ObjectOutput, java.io.ObjectStreamConstants)
- java.io.PipedOutputStream
- java.io.Reader (implements java.io.Closeable, java.lang.Readable)
- java.io.BufferedReader
- java.io.LineNumberReader
- java.io.CharArrayReader
- java.io.FilterReader
- java.io.PushbackReader
- java.io.InputStreamReader
- java.io.FileReader
- java.io.PipedReader
- java.io.StringReader
- java.io.Writer (implements java.lang.Appendable, java.io.Closeable, java.io.Flushable)
- java.io.BufferedWriter
- java.io.CharArrayWriter
- java.io.FilterWriter
- java.io.OutputStreamWriter
- java.io.FileWriter
- java.io.PipedWriter
- java.io.PrintWriter
- java.io.StringWriter
三 缓存流
InputStream的实现类FileInputStream可以将文件视为输入流,查看它的源码,会发现read方法是通过本地方法实现的。类似的,和文件操作有关的流都会使用本地方法实现。
FileInputStream一般每次都是直接从文件中读取一小块数据,每次都触发磁盘访问,导致程序效率低下。每次都读一大块,这样磁盘访问次数会少点,这样效率就搞了,但是一般程序不会一下子处理一大块程序,于是需要自己管理这一大块数据,因此让程序复杂了点。于是就出现了InputStream(不是针对某一个具体的输入流,而是所有的输入流)的包装类BufferedInputStream。
BufferedInputStream将InputStream作为成员,一次性通过InputStream读取一大块数据放入到内存缓存中,之后程序员使用的读取操作都是对这块缓存进行的。如果缓存读完了会再次触发一次读取操作。将一大块数据交由BufferedInputStream管理这就解决了上面的问题。
类似的,字符缓存流BufferedInputStream的原理也是类似的。
如果查看源码,发现BufferedInputStream的fill()方法是通过InputStream.read(byte[] b, int off, int len)实现的,而该方法又是通过read()一个一个读取实现的。这和上面的所说的一次读取一块冲突呀!实际上,在使用缓存流读取文件时,传入的流为FileInputStream,缓存类fill()方法执行的read(byte[],int,int)方法其实是FileInputStream的方法(继承覆盖),该方法是个本地方法,因此确实是一次读取一大块。
四 字符流的编码
字节流一次可以读一个字节,而字符流一次可以读入一个字符,那么就存在一个解码的过程。在内存中使用Unicode字符集(它的编码方式忘了,反正16个字节存一个字符就是了,不要混淆字符集和字符编码的关系),而输入字符流一般情况下使用系统默认编码(我们的GBK编码方式)解码输入字符流,比如FileReader,强制使用系统编码来解码文件流。
InputStreamReader代表一个文件流,可以通过它设置编码。而FileReader继承InputStreamReader,如果查看它的源码,会发现就是写了几个构造函数,将默认编码写死了,也就是强制使用默认编码。
注意的是,即使一个继承自字节流的流,如果对字符进行操作时都会使用默认编码。所以说啊,,并不是字节流就不操作字符了。。。。但是字符流应该可以设置编码。
五 其他流
流嘛,有输入就有输出,但是都差不多,下面只介绍其中一种。
5.1 Scanner
从简单文本中解析出一个一个基本类型和字符串,解析出来的叫做token。token由分界符分隔,默认分隔符为空白字符,但是可以用正则表达式作为分隔符。next***方法可以从输入流中获得对应类型的数据,比如next返回字符串、nextDouble返回double类型数据,hasNext***方法可以判断是否下一个token为该类型数据。
5.2 PrintStream
和PrintWriter类似,没啥用,就是格式化文本输出的(输出字符,尽管是字节流。。)。我们经常用的System.out对象就是PrintStream类型的,通过print、printf、format进行格式化输出到输出流。
5.3 流与命令行
在使用命令行时System.in(InputStream)、System.out(PrintStream)、System.err(PrintStream)都会绑定控制台的流。但是也可以使用Console提供输入输出流,但是在eclipse中运行时是获取不到console的。
5.4 DataOutputStream
与PrintStream类似,但是不是输出字符,而是二进制,比如可以将基本类型的数据的二进制写入输出流中,使用write***写入。writeUTF可以写入字符串,但是将每个字符用utf编码再写入输出流。
5.5 ObjectOutputStream
包含DataOutputStream的功能,但是还能够写入对象到输出流中,对象需要实现Serializable接口。写入对象时,对象属性引用的对象也会被写入到流中。一个流中只能含有一个对象的副本,但是可以含有多个该对象的引用(写入了多次)。
六 文件IO
jdk7中引入了新io,在nio包中,并且建议不要使用io包中的File对象操作文件或文件夹,而是使用nio包中的Path和Files。Path代表路径,有对路径操作的方法;Files含有对文件和文件夹的操作。这里给出了两者完整的比较:Legacy File I/O Code
七 其他
使用例子
public class TestAll {
@Test
public void test() {
try(BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(new FileInputStream("D:\\bookticket.sql"),"utf-8"))) {
String str;
while((str=bufferedReader.readLine()) != null)
System.out.println(str);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
写的有点简单,以后补充。。。
参考
https://docs.oracle.com/javase/tutorial/essential/io/index.html