目录
一 分类
字节流和字符流:
- 字节流:以字节为单位,每次次读入或读出是8位数据。可以读任何类型数据。
- 字符流:以字符为单位,每次次读入或读出是16位数据。其只能读取字符类型数据。
输出流和输入流:
- 输出流:从内存读出到文件。只能进行写操作。
- 输入流:从文件读入到内存。只能进行读操作。
注意: 这里的出和入,都是相对于系统内存而言的。
节点流和处理流:
- 节点流:直接与数据源相连,读入或读出。
- 处理流:与节点流一块使用,在节点流的基础上,再套接一层,套接在节点流上的就是处理流。
为什么要有处理流?直接使用节点流,读写不方便,为了更快的读写文件,才有了处理流。
1.1 按操作方式分类结构图
1.2 分类说明
1) 输入字节流InputStream
- ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据。
- PipedInputStream 是从与其它线程共用的管道中读取数据。PipedInputStream的一个实例要和PipedOutputStream的一个实例共同使用,共同完成管道的读取写入操作。主要用于线程操作。
- DataInputStream: 将基础数据类型读取出来
- ObjectInputStream 和所有 FilterInputStream 的子类都是装饰流(装饰器模式的主角)。
2)字节流OutputStream
- ByteArrayOutputStream、FileOutputStream: 是两种基本的介质流,它们分别向- Byte 数组、和本地文件中写入数据。
- PipedOutputStream 是向与其它线程共用的管道中写入数据。
- DataOutputStream 将基础数据类型写入到文件中
- ObjectOutputStream 和所有 FilterOutputStream 的子类都是装饰流。
节流的输入和输出类结构图:
3)字符输入流Reader
- FileReader、CharReader、StringReader 是三种基本的介质流,它们分在本地文件、Char 数组、String中读取数据。
- PipedReader:是从与其它线程共用的管道中读取数据
- BufferedReader :加缓冲功能,避免频繁读写硬盘
- InputStreamReader: 是一个连接字节流和字符流的桥梁,它将字节流转变为字符流。
4)字符输出流Writer
- StringWriter:向String 中写入数据。
- CharArrayWriter:实现一个可用作字符输入流的字符缓冲区
- PipedWriter:是向与其它线程共用的管道中写入数据
- BufferedWriter : 增加缓冲功能,避免频繁读写硬盘。
- PrintWriter 和PrintStream 将对象的格式表示打印到文本输出流。 极其类似,功能和使用也非常相似
- OutputStreamWriter: 是OutputStream 到Writer 转换的桥梁,它的子类FileWriter 其实就是一个实现此功能的具体类(具体可以研究一SourceCode)。功能和使用和OutputStream 极其类似。
字符流的输入和输出类结构图:
1.3 按操作对象分类结构图
1.4分类说明
1)对文件进行操作(节点流)
- FileInputStream(字节输入流)
- FileOutputStream(字节输出流)
- FileReader(字符输入流)
- FileWriter(字符输出流)
2)对管道进行操作(节点流)
- PipedInputStream(字节输入流)
- PipedOutStream(字节输出流)
- PipedReader(字符输入流)
- PipedWriter(字符输出流)
PipedInputStream的一个实例要和PipedOutputStream的一个实例共同使用,共同完成管道的读取写入操作。主要用于线程操作。
3)字节/字符数组流(节点流)
- ByteArrayInputStream
- ByteArrayOutputStream
- CharArrayReader
- CharArrayWriter
除了上述三种是节点流,其他都是处理流,需要跟节点流配合使用。
4)Buffered缓冲流(处理流)
带缓冲区的处理流,缓冲区的作用的主要目的是:避免每次和硬盘打交道,提高数据访问的效率。
- BufferedInputStream
- BufferedOutputStream
- BufferedReader
- BufferedWriter
5)转化流(处理流)
- InputStreamReader:把字节转化成字符
- OutputStreamWriter:把字节转化成字符
6)基本类型数据流(处理流)
用于操作基本数据类型值。
因为平时若是我们输出一个8个字节的long类型或4个字节的float类型,那怎么办呢?可以一个字节一个字节输出,也可以把转换成字符串输出,但是这样转换费时间,若是直接输出该多好啊,因此这个数据流就解决了我们输出数据类型的困难。数据流可以直接输出float类型或long类型,提高了数据读写的效率。
- DataInputStream
- DataOutputStream
7)打印流(处理流)
一般是打印到控制台,可以进行控制打印的地方。
- PrintStream
- PrintWriter
8)对象流(处理流)
把封装的对象直接输出,而不是一个个在转换成字符串再输出。
- ObjectInputStream,对象反序列化
- ObjectOutputStream,对象序列化
9)合并流(处理流)
- SequenceInputStream:可以认为是一个工具类,将两个或者多个输入流当成一个输入流依次读取
1.5 java输入/输出流体系中常用的流的分类表
分类 | 字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 |
---|---|---|---|---|
抽象基类 | InputStream | OutputStream | Reader | Writer |
访问文件 | FileInputStream | FileOutputStream | FileReader | FileWriter |
访问数组 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter |
访问管道 | PipedInputStream | PipedOutputStream | PipedReader | PipedWriter |
访问字符串 | StringReader | StringWriter | ||
缓冲流 | BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWriter |
转换流 | InputStreamReader | OutputStreamWriter | ||
对象流 | ObjectInputStream | ObjectOutputStream | ||
打印流 | PrintStream | PrintWriter | ||
推回输入流 | PushbackInputStream | PushbackReader | ||
特殊流 | DataInputStream | DataOutputStream |
注:表中粗体字所标出的类代表节点流,必须直接与指定的物理节点关联:斜体字标出的类代表抽象基类,无法直接创建实例。
二 常用的io流的用法
2.1 Io体系的基类
InputStream/Reader,OutputStream/Writer
InputStream和Reader是所有输入流的抽象基类,本身并不能创建实例来执行输入,但它们将成为所有输入流的模板,所以它们的方法是所有输入流都可使用的方法。
在InputStream里面包含如下3个方法。
- int read(); 从输入流中读取单个字节,返回所读取的字节数据(字节数据可直接转换为int类型)。
- int read(byte[] b)从输入流中最多读取b.length个字节的数据,并将其存储在字节数组b中,返回实际读取的字节数。
- int read(byte[] b,int off,int len); 从输入流中最多读取len个字节的数据,并将其存储在数组b中,放入数组b中时,并不是从数组起点开始,而是从off位置开始,返回实际读取的字节数。
在Reader中包含如下3个方法。
- int read(); 从输入流中读取单个字符,返回所读取的字符数据(字节数据可直接转换为int类型)。
- int read(char[] b)从输入流中最多读取b.length个字符的数据,并将其存储在字节数组b中,返回实际读取的字符数。
- int read(char[] b,int off,int len); 从输入流中最多读取len个字符的数据,并将其存储在数组b中,放入数组b中时,并不是从数组起点开始,而是从off位置开始,返回实际读取的字符数。
输入流模型图
InputStream和Reader提供的一些移动指针的方法:
- void mark(int readAheadLimit); 在记录指针当前位置记录一个标记(mark)。
- boolean markSupported(); 判断此输入流是否支持mark()操作,即是否支持记录标记。
- void reset(); 将此流的记录指针重新定位到上一次记录标记(mark)的位置。
- long skip(long n); 记录指针向前移动n个字节/字符。
OutputStream和Writer:
OutputStream和Writer的用法也非常相似,它们采用如图15.6所示的模型来执行输入,两个流都提供了如下三个方法:
- void write(int c); 将指定的字节/字符输出到输出流中,其中c即可以代表字节,也可以代表字符。
- void write(byte[]/char[] buf); 将字节数组/字符数组中的数据输出到指定输出流中。
- void write(byte[]/char[] buf, int off,int len ); 将字节数组/字符数组中从off位置开始,长度为len的字节/字符输出到输出流中。
因为字符流直接以字符作为操作单位,所以Writer可以用字符串来代替字符数组,即以String对象作为参数。Writer里面还包含如下两个方法。
- void write(String str); 将str字符串里包含的字符输出到指定输出流中。
- void write (String str, int off, int len); 将str字符串里面从off位置开始,长度为len的字符输出到指定输出流中。
2.2 Io体系的基类文件流的使用
FileInputStream/FileReader ,FileOutputStream/FileWriter
前面说过InputStream和Reader都是抽象类,本身不能创建实例,但它们分别有一个用于读取文件的输入流:FileInputStream和FileReader,它们都是节点流——会直接和指定文件关联。
2.2.1FileInputStream和FileReader示例
使用FileInputStream读取文件:
public class MyClass {
public static void main(String[] args)throws IOException{
FileInputStream fis=null;
try {
//创建字节输入流
fis=new FileInputStream("E:\\learnproject\\Iotest\\lib\\src\\main\\java\\com\\Test.txt");
//创建一个长度为1024的竹筒
byte[] b=new byte[1024];
//用于保存的实际字节数
int hasRead=0;
//使用循环来重复取水的过程
while((hasRead=fis.read(b))>0){
//取出竹筒中的水滴(字节),将字节数组转换成字符串进行输出
System.out.print(new String(b,0,hasRead));
}
}catch (IOException e){
e.printStackTrace();
}finally {
fis.close();
}
}
}
使用FileReader读取文件:
public class FileReaderTest {
public static void main(String[] args)throws IOException{
FileReader fis=null;
try {
//创建字符输入流
fis=new FileReader("E:\\learnproject\\Iotest\\lib\\src\\main\\java\\com\\Test.txt");
//创建一个长度为1024的竹筒
char[] b=new char[1024];
//用于保存的实际字节数
int hasRead=0;
//使用循环来重复取水的过程
while((hasRead=fis.read(b))>0){
//取出竹筒中的水滴(字节),将字节数组转换成字符串进行输出
System.out.print(new String(b,0,hasRead));
}
}catch (IOException e){
e.printStackTrace();
}finally {
fis.close();
}
}
}
结论:
可以看出使用FileInputStream和FileReader进行文件的读写并没有什么区别,只是操作单元不同而已。
2.2.2 FileOutputStream/FileWriter示例
FileOutputStream/FileWriter是Io中的文件输出流。
FileOutputStream的示例:
public class FileOutputStreamTest {
public static void main(String[] args)throws IOException {
FileInputStream fis=null;
FileOutputStream fos=null;
try {
//创建字节输入流
fis=new FileInputStream("E:\\learnproject\\Iotest\\lib\\src\\main\\java\\com\\Test.txt");
//创建字节输出流
fos=new FileOutputStream("E:\\learnproject\\Iotest\\lib\\src\\main\\java\\com\\newTest.txt");
byte[] b=new byte[1024];
int hasRead=0;
//循环从输入流中取出数据
while((hasRead=fis.read(b))>0){
//每读取一次,即写入文件输入流,读了多少,就写多少。
fos.write(b,0,hasRead);
}
}catch (IOException e){
e.printStackTrace();
}finally {
fis.close();
fos.close();
}
}
}
运行程序可以看到输出流指定的目录下多了一个文件:newTest.txt, 该文件的内容和Test.txt文件的内容完全相同。FileWriter的使用方式和FileOutputStream基本类似。
2.3 缓冲流的使用
BufferedInputStream/BufferedReader, BufferedOutputStream/BufferedWriter
字节缓存流的用法(字符缓存流的用法和字节缓存流一致就不介绍了)
public class BufferedStreamTest {
public static void main(String[] args)throws IOException {
FileInputStream fis=null;
FileOutputStream fos=null;
BufferedInputStream bis=null;
BufferedOutputStream bos=null;
try {
//创建字节输入流
fis=new FileInputStream("E:\\learnproject\\Iotest\\lib\\src\\main\\java\\com\\Test.txt");
//创建字节输出流
fos=new FileOutputStream("E:\\learnproject\\Iotest\\lib\\src\\main\\java\\com\\newTest.txt");
//创建字节缓存输入流
bis=new BufferedInputStream(fis);
//创建字节缓存输出流
bos=new BufferedOutputStream(fos);
byte[] b=new byte[1024];
int hasRead=0;
//循环从缓存流中读取数据
while((hasRead=bis.read(b))>0){
//向缓存流中写入数据,读取多少写入多少
bos.write(b,0,hasRead);
}
}catch (IOException e){
e.printStackTrace();
}finally {
bis.close();
bos.close();
}
}
}
可以看到使用字节缓存流读取和写入数据的方式和文件流(FileInputStream,FileOutputStream)并没有什么不同,只是把处理流套接到文件流上进行读写。
上面代码中我们使用了缓存流和文件流,但是我们只关闭了缓存流。这个需要注意一下,当我们使用处理流套接到节点流上的使用的时候,只需要关闭最上层的处理就可以了。java会自动帮我们关闭下层的节点流。### 2.4 转换流的使用 InputStreamReader/OutputStreamWriter
以获取键盘输入为例来介绍转换流的用法。java使用System.in代表输入。即键盘输入,但这个标准输入流是InputStream类的实例,使用不太方便,而且键盘输入内容都是文本内容,所以可以使用InputStreamReader将其包装成BufferedReader,利用BufferedReader的readLine()方法可以一次读取一行内容
public class InputStreamReaderTest {
public static void main(String[] args)throws IOException {
try {
// 将System.in对象转化为Reader对象
InputStreamReader reader=new InputStreamReader(System.in);
//将普通的Reader包装成BufferedReader
BufferedReader bufferedReader=new BufferedReader(reader);
String buffer=null;
while ((buffer=bufferedReader.readLine())!=null){
// 如果读取到的字符串为“exit”,则程序退出
if(buffer.equals("exit")){
System.exit(1);
}
//打印读取的内容
System.out.print("输入内容:"+buffer);
}
}catch (IOException e){
e.printStackTrace();
}finally {
}
}
}
上面程序将System.in包装成BufferedReader,BufferedReader流具有缓存功能,它可以一次读取一行文本——以换行符为标志,如果它没有读到换行符,则程序堵塞。等到读到换行符为止。运行上面程序可以发现这个特征,当我们在控制台执行输入时,只有按下回车键,程序才会打印出刚刚输入的内容。
2.5 对象流的使用
ObjectInputStream/ObjectOutputStream
写入对象:
public static void writeObject(){
OutputStream outputStream=null;
BufferedOutputStream buf=null;
ObjectOutputStream obj=null;
try {
**加粗样式** //序列化文件輸出流
outputStream=new FileOutputStream("E:\\learnproject\\Iotest\\lib\\src\\main\\java\\com\\myfile.tmp");
//构建缓冲流
buf=new BufferedOutputStream(outputStream);
//构建字符输出的对象流
obj=new ObjectOutputStream(buf);
//序列化数据写入
obj.writeObject(new Person("A", 21));//Person对象
//关闭流
obj.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
读取对象:
/**
* 读取对象
*/
public static void readObject() throws IOException {
try {
InputStream inputStream=new FileInputStream("E:\\learnproject\\Iotest\\lib\\src\\main\\java\\com\\myfile.tmp");
//构建缓冲流
BufferedInputStream buf=new BufferedInputStream(inputStream);
//构建字符输入的对象流
ObjectInputStream obj=new ObjectInputStream(buf);
Person tempPerson=(Person)obj.readObject();
System.out.println("Person对象为:"+tempPerson);
//关闭流
obj.close();
buf.close();
inputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
使用对象流的一些注意事项
- 读取顺序和写入顺序一定要一致,不然会读取出错。
- 在对象属性前面加transient关键字,则该对象的属性不会被序列化。
三 总结
https://www.cnblogs.com/hopeyes/p/9736642.html
1)明确要操作的数据是数据源还是数据目的(要读还是要写)
- 源:InputStream Reader
- 目的:OutputStream Writer
2)明确要操作的设备上的数据是字节还是文本
- 源:
字节:InputStream
文本:Reader - 目的:
字节:OutputStream
文本:Writer
3)明确数据所在的具体设备
- 源设备:
硬盘:文件 File开头
内存:数组,字符串
键盘:System.in
网络:Socket - 目的设备:
硬盘:文件 File开头
内存:数组,字符串
屏幕:System.out
网络:Socket
4)明确是否需要额外功能?
- 需要转换——转换流 InputStreamReader OutputStreamWriter
- 需要高效——缓冲流Bufferedxxx
- 多个源——序列流 SequenceInputStream
- 对象序列化——ObjectInputStream,ObjectOutputStream
- 保证数据的输出形式——打印流PrintStream Printwriter
- 操作基本数据,保证字节原样性——DataOutputStream,DataInputStream
参考:
https://blog.csdn.net/nightcurtis/article/details/51324105
https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247484946&idx=1&sn=043b054de3aef29bf3ff80eea15c16fd&source=41#wechat_redirect
https://www.cnblogs.com/hopeyes/p/9736642.html