Io概述
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输介质将流抽象为各种类,方便更直观的进行数据操作。说白了这些类就是用来读取和写入数据的,由于可以读取或写入的媒介非常多,针对每一种媒介jdk提供了单独的类来读和写数据。同时根据读写单位又分为读取按字节读写和按字符读取(根据不同的编码方案,一个字符可能按多个字节读取)。大致可以分为如下
从读写介质的角度来看
读写介质 | 字节输入 | 字节输出 | 字符输入 | 字符输出 |
---|---|---|---|---|
Basic | InputStream | OutputStream | Reader, InputStreamReader | Writer, OutputStreamReader |
Arrays | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter |
File | FileInputStream, RandomAccessFile | FileOutputStream, RandomAccessFile | FileReader | FileWriter |
Pipes | PipedInputStream | PipedOutputStream | PipedReader | PipedWriter |
Buffering | BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWriter |
Filtering | FilterInputStream | FilterOuptStream | FilterReader | FilterWriter |
Parsing | PushbackInputStream, StreamTokenizer | PushbackReader, LineNumberReader | ||
String | StringReader | StringWriter | ||
Data | DataInputStream | DataOutputStream | ||
Data-Formatterd | PrintStream | PrintWriter | ||
Objects | ObjectInputStream | ObjectOutputStream | ||
Utilities | SequenceInputStream |
从功能和继承关系的角度看
基本方法
InputStream类(不是接口)提供的基本方法
OutputStream类(不是接口)提供的基本方法
其他InputStream和OutputStream的子类都根据各自的功能重写了上述的方法或者提供了另外的函数
RandomAccessFile类提供的
PipedInputStream类和PipedOutputStream类提供的
|
DataInputStream类提供的一系列方法,DataOutputStream提供的方法也类似。
|
PushbackInputStream提供的unread()方法其实很简单,使用一个buf数组,将unread的字节放入buf,下次read的时候从buf里面读。PushbackReader原理类似
|
SequenceInputStream把一个或者多个InputStream整合起来,形成一个逻辑连贯的输入流。当读取SequenceInputStream时,会先从第一个输入流中读取,完成之后再从第二个输入流读取… 以此类推。程序内部将多个流放入到一个Enumeration中,在read的时候判断需不需要切换到下一个流
|
PrintStream允许你把格式化数据写入到底层OutputStream中。比如,写入格式化成文本的int,long以及其他原始数据类型到输出流中,而非它们的字节数据。通过其内部的构造方法可以看出,这个流内部其实用了Writer来写int ,long 类型的数据。
|
从PrintWriter的最终的构造方法可以看出,PrintWriter内部只使用Wrtier来作为最终的输出流。它提供的方法和PrintStream基本一样
|
LineNumberReader类通过getLineNumber()方法获取当前行号,通过setLineNumber()方法设置当前行数。流的读取依然是顺序进行,不能通过setLineNumber()实现流的跳跃读取。使用这个流可以快速定位流读取过程中出现的错误。这个流的本质就是在读取的时候使用判断读到的字符是否是行结束符,如果是就lineNumber++(文件的行结束符视操作系统而定)。这个流没有LineNumberInputStream一说。
|
StreamTokenizer通过循环调用nextToken()可以遍历底层输入流的所有符号。在每次调用nextToken()之后,StreamTokenizer有一些变量可以获取读取到的符号的类型和值。这些变量是:
- ttype 读取到的符号的类型(字符(TT_WORD),数字(TT_NUMBER),或者行结尾符(TT_EOL))
- sval 如果读取到的符号是字符串类型,该变量的值就是读取到的字符串的值
- nval 如果读取到的符号是数字类型,该变量的值就是读取到的数字的值
以此类推,只要在需要的什么样的流的时候,使用对应的io流对象就行了。说到io,大家都会想到装饰器模式,其实装饰器模式很好理解。下面三个类就是io中一个简单的装饰器模式体现。使用io流的时候,主要注意的问题是几个对象之间能不能互相装饰。
如果想使用DataInputStream 同时使用BufferedInputStream的mark()方法,可以如下使用
DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(“filepath”)));
需要注意的点
任何关于文件本身或者目录的操作在File类里面
int read()和 int read(byte[] b)方法返回值的区别
read()放回读取到的字节,read(byte[] b)返回读取到的字节数量。System.out System.in. System.err 可以通过System.setErr()等重定向到任意输出流,
流关闭问题的两种解决方法,一统一写一个模板类,二使用try-with-resource风格编程
BufferedInputStream和BufferedOutputStream缓冲区大小设置为1024整数倍效率更高
ObjectInputStream和ObjectOutputStream所读写的对象对应的类必须实现了java.io.Serializable接口
InputStreamReader和OutputStreamWriter是一种字符流,主要目的是将字节流转化成字符流,使用字符流的形式来处理数据。
FilterInputStream和和FilterOuFilterReader和FilterReader是过滤流的基类, 都只是简单的覆盖了其父类的方法,主要使用它们的子类来处理读写。