目录
一、流的定义
流是个抽象的概念,java中对于数据的输入/输出都是以“流”的方式进行;
流具有方向性,程序为参考,如果数据的流向是程序至设备,我们成为输出流,反之我们称为输入流。
作用:为数据源和目的地建立一个输送通道。
1.1 流的分类
1.1.1 根据流的方向
- 输入流:
从别的地方(本地文件,网络上的资源等)获取资源 输入到 我们的程序中;
- 输出流
从我们的程序中 输出到 别的地方(本地文件), 将一个字符串保存到本地文件中,就需要使用输出流;
1.1.2 根据处理的数据单位
- 字符流:
每次输入/输出两个字节(Byte)的,使用该流可以正确传输显示中文;
- 字节流
每次输入/输出一个字节,但传输中有中文时,会出现乱码;
1.1.3 根据功能
- 节点流:
以从或向一个特定的地方(节点)读写数据。如FileInputStream
- 处理流
是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如BufferedReader。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装;
1.2 抽象流类型
Java所有的流类位于java.io包中,都分别继承字以下四种抽象流类型。
输入流 | 输出流 | |
---|---|---|
字符流 | Reader | Writer |
字节流 | InputStream | OutStream |
-
继承自Reader/Writer的流都是用于向程序中输入/输出数据,且数据的单位都是字符(2byte=16bit),如图,深色的为节点流,浅色的为处理流。
-
继承自InputStream/OutputStream的流都是用于向程序中输入/输出数据,且数据的单位都是字节(byte=8bit),如图,深色的为节点流,浅色的为处理流。
1.3 IO流的特性
1、先进先出:
最先写入输出流的数据最先被输入流读取到。
2、顺序存取:
可以一个接一个地往流中写入一串字节,读出时也将按写入顺序读取一串字节,不能随机访问中间的数据。(RandomAccessFile可以从文件的任意位置进行存取(输入输出)操作)
3、功能唯一:
每个流只能是输入流或输出流的一种,不能同时具备两个功能,。在一个数据传输通道中,如果既要写入数据,又要读取数据,则要分别提供两个流。
二、IO流对象
IO体系的基类为:InputStream/Reader、OutputStream/Writer
字节流和字符流的操作方式基本一致,只是操作的数据单元不同——字节流的操作单元是字节,字符流的操作单元是字符。所以字节流和字符流就整理在一起了。
2.1 File
对指定目录的文件进行操作(该类虽然是在IO包下,但是并不继承自四大基础类);
2.2 InputStream
字节输入流的所有类的超类(抽象类)。
ByteArrayInputStream:
字节数组输入流,该类的功能就是从字节数组(byte[])中进行以字节为单位的读取,将资源文件都以字节的形式存入到该类中的字节数组中;
PipedInputStream
管道字节输入流,该类用于实现多线程间的管道通信,常常和PipedOutputStream一起使用;
FilterInputStream
用来封装其他的输入类,提供额外的功能,常用的输入类为:BufferInputStream和DataInputStream;
BufferInputStream
缓冲字节输入流,FilterInputStream流的派生类,提供优化读取方式的功能,提高读取效率。
读取方式 | 读取效率 | |
---|---|---|
基础输入流 | 逐字节读取 | 低 |
BufferInputStream | 先读取到缓存流(内存中),然后一次性从内存中读取多个字符 | 高 |
DataInputStream
数据字节输入流,允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型
FileInputStream
文件输入类,通常对文件进行读取操作;
ObjectInputStream
对象输入流,用来提供对“基本数据或对象”的持久存储,即直接传输对象;(反序列化中使用)
2.3 OutputStream
输出字节流的所有类的超类(抽象类);
ByteArrayOutputStream
字节数组输出流,向Byte数组写入数据;
PipedOutputStream
管道字节输出流,向与其它线程共用的管道中写入数据;
FilterOutputStream
用来封装其他的输出类,提供额外的功能,常用的输入类为:BufferOutputStream和DataOutputStream、PrintStream;
BufferOutputStream
字节缓冲输出流,提高输出效率;
DataOutputStream
数据字节输出流,允许应用程序以与机器无关方式将Java基本数据类型写到底层输出流。
PrintStream
为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。值得注意的是:
- PrintStream流永远不会抛出异常;
- 有自动刷新机制,例如当向PrintStream流中写入一个字节数组后自动调用flush()方法.
FileOutputStream
文件字节输出流,向本地文件写入数据;
ObjectOutputStream
对象输出流,将一个对象写出(序列化中使用);
2.4 Reader
字符输入流的所有类的超类(抽象类)。
CharArrayReader
字符数组输入流,从char数组中读取数据;
PipedReader
管道字符输入流,从与其它线程共用的管道中读取数据;
FilterReader
所有自定义具体字符装饰流的父类;
BufferReader
缓冲字符流,提到读取效率;
InputStreamReader
InputStreamReader是连接字节流和字符流的桥梁,它将字节流转变为字符流。
FileReader
文件字符输入流,对本地文件进行读取操作,是一个常用的工具类;
2.5 Writer
字符输出流的所有类的超类(抽象类)
CharArrayWriter
字符数组输出流,向char数组中写入数据;
PipedWriter
管道字符输出流,向与其它线程共用的管道中写入数据;
FilterWriter
所有自定义具体字符装饰流的父类;
BufferWriter
缓冲字符流,为Writer 提供缓冲功能;
OutputStreamWriter
OutputStreamWriter是从字符流到字节流的桥梁。,它将字节流转变为字符流。
FileWriter
文件字符输出流,将数据写入到本地文件中,是一个常用的工具类;
三、IO流的方法
3.1 输入流(InputStream/Reader)
InputStream和Reader是所有输入流的抽象基类,本身并不能创建实例来执行输入,但它们将成为所有输入流的模板,所以它们的方法是所有输入流都可使用的方法。
在InputStream里包含以下三个方法:
- int read()
从输入流中读取数据的下一个字节。
- int read(byte[] b)
从输入流中读取一定数量的字节,并将其存储到缓冲数组b中
- nt read(byte[] b,int off,int len)
从输入流中最多读取len个字节的数据,并将其存储在数组b中,放入数组b中时,并不是从数组起点开始,而是从off位置开始,返回实际读取的字节数。
- void close()
关闭此输入流并释放与该流关联的所有系统资源。
示例:
public class InputStreamDemo{
public static void read() throws IOException{//抛出异常不处理
//使用File类找到源文件
File file = new File("C:\\Users\\ss\\Desktop\\test.txt");
//通过子类实例化
InputStream input = new FileInputStream(file);
//所有内容存贮在数组中
byte[] b = new byte[1024];
//进行读操作
input.read(b);
//关闭流
input.close();
System.out.println("内容为:"+new String(b));
}
}
程序里面打开的文件IO资源不属于内存的资源,垃圾回收机制无法回收该资源,所以应该显示地关闭打开的IO资源。Java 7改写了所有的IO资源类,它们都实现了AntoCloseable接口,因此都可以通过自动关闭资源的try语句来关闭这些Io流。
在Reader中包含以下三个方法:
- int read()
从输入流中读取单个字符,返回所读取的字符数据(字节数据可直接转换为int类型)。
- int read(char[] b)
从输入流中最多读取b.length个字符的数据,并将其存储在字节数组b中,返回实际读取的字符数。
- int read(char[] b,int off,int len)
从输入流中最多读取len个字符的数据,并将其存储在数组b中,放入数组b中时,并不是从数组起点开始,而是从off位置开始,返回实际读取的字符数。
示例如下:
public static void read() throws IOException{
/*********************逐个字符读取文件*************************/
FileReader fileReader1 = null;
try{
//创建字符输入流
fileReader1 = new FileReader("C:\\Users\\ss\\Desktop\\test.txt");
//字符存储
int ch = fileReader1.read();
//逐字符读取
while (ch!=-1){
System.out.println((char)ch);
ch = fileReader1.read();
}
}catch (Exception e){
e.printStackTrace();
}finally {
fileReader1.close();
}
/*********************使用数组读取本地文件*************************/
FileReader fileReader2 = null;
try{
//创建字符输入流
fileReader2 = new FileReader("C:\\Users\\ss\\Desktop\\test.txt");
//创建一个用来存储的字符数组
char c[] = new char[1024];
int num = 0;
//循环读取内容
while((num=fileReader2.read(c))!=-1){
//取出字符,将字符数组转换成字符串输出
System.out.println(new String(c,0,num));
}
}catch (IOException e){
e.printStackTrace();
}finally {
fileReader2.close();
}
/*********************用缓冲区读取文本文件*************************/
FileReader fileReader3 = null;
BufferedReader bufferedReader = null;
try{
fileReader3 = new FileReader("C:\\Users\\ss\\Desktop\\test.txt");
bufferedReader = new BufferedReader(fileReader3);
String line = null;
//BufferedReader提供了按行读取文本文件的方法readLine()
//readLine()返回行有效数据,不包含换行符,未读取到数据返回null
while ((line=bufferedReader.readLine())!=null){
System.out.println(line);
}
}catch (Exception e){
e.printStackTrace();
}finally {
bufferedReader.close();
}
}
3.2 输出流(OutputStream和Writer)
OutputStream和Writer的用法也非常相似,两个流都提供了如下三个方法::
- 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的字符输出到指定输出流中。
OutputStream示例如下:
public static void write() throws IOException {
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
try {
//创建字节输入流
fileInputStream = new FileInputStream("C:\\Users\\ss\\Desktop\\test.txt");
//创建字节输出流
fileOutputStream = new FileOutputStream("C:\\Users\\ss\\Desktop\\test2.txt");
byte[] b = new byte[1024];
int hasread = 0;
//循环读取数据
while ((hasread=fileInputStream.read(b))>0){
//每次读多少,写多少
fileOutputStream.write(b,0,hasread);
}
}catch (Exception e){
e.printStackTrace();
}finally {
fileInputStream.close();
fileOutputStream.close();
}
}
使用java的io流执行输出时,不要忘记关闭输出流,关闭输出流除了可以保证流的物理资源被回收之外,可能还可以将输出流缓冲区中的数据flush到物理节点中里(因为在执行close()方法之前,自动执行输出流的flush()方法)。java很多输出流默认都提供了缓存功能,其实我们没有必要刻意去记忆哪些流有缓存功能,哪些流没有,只有正常关闭所有的输出流即可保证程序正常。
Writer示例如下:
public static void write() throws IOException {
File file = null;
FileWriter fileWriter = null;
try{
file = new File("C:\\Users\\ss\\Desktop\\test2.txt");
fileWriter = new FileWriter(file);
fileWriter.write("这是输出的内容");
}catch (Exception e){
e.printStackTrace();
}finally {
fileWriter.close();
}
}
四、字节流与字符流的区别
类型 | 字节流 | 字符流 |
---|---|---|
处理单位 | 字节(8bit) | 字符(一次可读写多个字符) |
处理对象 | 所有类型的数据(如视频、图片) | 字符类型的数据 |
有无缓冲区 | 无 | 有 |
缓冲区的有无对IO流的影响如下:
- 字节流:不需要调用close()方法,就可以输出信息;
- 字符流:只要调用close()方法时关闭缓冲区时,才会输出信息;如果想要关闭关闭缓冲区前输出信息,就需要手动调用flush()方法;
五、转换流
IO流中提供了两种用于将字节流转换为字符流的转换流InputStream和OutputStream;使用转换流可以在一定程度上避免乱码,还可以指定输入输出所使用的字符集。
5.1 InputStreamReader
用于将字节输入流转换为字符输入流。
InputStreamReader含有方法如下:
- close():关闭此流;
- getEncoding():获取此流使用的字符编码的名称;
- ready():判断此流是否已经准备好用于读取;
- read():读取单个字符。;
- read(char[] c,int offset ,int length):将字符读入数组的某一部分;
创建InputStreamReader时如果不指定字符集,就使用默认字符集创建InputStreamReader。转换后可以不关闭InputStream,示例如下:
public static void read() throws IOException{
InputStreamReader inputStreamReader = null;
try {
//使用File类找到源文件
File file = new File("C:\\Users\\ss\\Desktop\\test2.txt");
//通过FileInputStream实例化
InputStream inputStream = new FileInputStream(file);
//指定字符集转换字节流
inputStreamReader = new InputStreamReader(inputStream,"UTF-8");
//使用字符数组存储数据
char[] c= new char[1024];
int count = 0;
while((count=inputStreamReader.read(c,0,c.length))!=-1){
System.out.println(new String(c,0,c.length));
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(inputStreamReader!=null){
inputStreamReader.close();
}
}
}
5.2 OutputStreamWriter
用于将字节输出流转换为字符输出流。
OutputStreamWriter含有方法如下:
- flsh():刷新该流的缓冲;
- close():关闭此流,关闭前需要刷新;
- getEncoding():获取此流使用的字符编码的名称;
- write():write(char[] c ,int offset ,int length),写入字符数组的某一部分;
- write(String str,int offset ,int length):写入字符串的某一部分;
- write(String str):写入字符传;
创建OutputStreamWriter如果不指定字符集,就使用默认字符集创建OutputStreamWriter。转换之后可以不用关闭OutputStream,示例如下:
public static void writer(){
try{
String str = "八月中秋白露,路上行人凄凉。 小桥流水桂花香,日夜千思万想。 心中不得宁静,清早览罢文章。 十年寒苦在书房,方显才高志广。";
//使用File类找到源文件
File file = new File("C:\\Users\\ss\\Desktop\\test2.txt");
//通过FileOutputStream实例化
OutputStream outputStream = new FileOutputStream(file);
//使用指定字符集转换字节流
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
//写入数据
outputStreamWriter.write(str);
outputStreamWriter.flush();
outputStreamWriter.close();
}catch (Exception e){
e.printStackTrace();
}
}