Java IO流专题
- 一、IO流是什么?
-
- 1、IO流概念
- 2、IO流原理及流的分类
- 二、输入流 使用代码示例
-
- 1、FileInputStream 字节流
- 2、FileReader 字符流
- 3、BufferedInputStream 字节处理流
- 4、BufferedReader 字符处理流
- 二、输出流 使用代码示例
-
- 1、FileOutputStream 字节流
- 2、FileWriter 字符流
- 3、BufferedOutputStream 字符处理流
- 4、BufferedWriter 字符处理流
- 三、实现拷贝功能
- 四、对象处理流
-
- 1、ObjectOutputStream
- 2、ObjectOutputStream
- 五、转换处理流
-
- 1、InputStreamReader
- 2、InputStreamReader
- 六、打印处理流
- 七、节点流(低级流)和处理流(包装流)区别和联系
一、IO流是什么?
1、IO流概念
IO(Input Output)用于实现对数据的输入与输出操作,Java把不同的输入/输出源(键盘、文件、网络等)抽象表述为流(Stream),简单来说就是对于数据的输入/输出操作以流的方式进行。流是从起源到接收的有序数据,有了它程序就可以采用同一方式访问不同的输入/输出源。
Java中I/O操作主要是指使用java.io包下的内容,进行输入、输出操作。
2、IO流原理及流的分类
①、按照数据流向,可以将流分为输入流和输出流,其中输入流只能读取数据、不能写入数据,而输出流只能写入数据、不能读取数据。
输入流(input):读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
输出流(output):将程序(内存)数据输出到硬盘、光盘等存储设备中。
②、按照数据类型,可以将流分为字节流和字符流,其中字节流操作的数据单元是8位的字节,而字符流操作的数据单元是16位的字符。
字节流:可以用于读写二进制文件及任何类型文件。
字符流:可以用于读写文本文件。
③、按照处理功能,可以将流分为节点流(低级流)和处理流(高级流),其中节点流可以直接从/向一个特定的IO设备(磁盘、网络等)读/写数据,也称为低级流,而处理流是对节点流的连接或封装,用于简化数据读/写功能或提高效率,也称为高级流。
节点流:可以从或向一个特定的地方(节点)读写数据。如FileInputStream,FileReader。
处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如BufferedReader、BufferedWriter。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。
节点流是直接作用在文件上的流,可以理解为一个管道,文件在管道中传输。
处理流是作用在已有的节点流基础上,是包在节点流的外面的管道(可多层),其目的是让管道内的流可以更快的传输。
Java提供了大量的类来支持IO操作,下表给大家整理了其中比较常用的一些类。其中,黑色字体的是抽象基类,其他所有的类都继承自它们。红色字体的是节点流,蓝色字体的是处理流。
根据命名很容易理解各个流的作用:
-
以File开头的文件流用于访问文件;
-
以ByteArray/CharArray开头的流用于访问内存中的数组;
-
以Piped开头的管道流用于访问管道,实现进程之间的通信;
-
以String开头的流用于访问内存中的字符串;
-
以Buffered开头的缓冲流,用于在读写数据时对数据进行缓存,以减少IO次数;
-
InputStreamReader、InputStreamWriter是转换流,用于将字节流转换为字符流;
-
以Object开头的流是对象流,用于实现对象的序列化;
-
以Print开头的流是打印流,用于简化打印操作;
-
以Pushback开头的流是推回输入流,用于将已读入的数据推回到缓冲区,从而实现再次读取;
以Data开头的流是特殊流,用于读写Java基本类型的数据。
二、输入流 使用代码示例
1、FileInputStream 字节流
FileInputStream文件输入流。它通常用于对文件进行读取操作。
注意:这里读取中文会发生乱码,所以有用到读取中文(字符),可以使用字符流来操作。
//1、读取文件的路径
String filePath = "D:\\hwx.txt";
FileInputStream fileInputStream = null;
byte[] bytes = new byte[8];
int read = 0;
try {
//2、创建对象FileInputStream用来读取文件内容
fileInputStream = new FileInputStream(filePath);
//3、如果返回-1,表示文件读取完毕
while ((read = fileInputStream.read(bytes))!=-1){
System.out.print(new String(bytes,0,read));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//4、关闭流
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
2、FileReader 字符流
//1、读取文件的路径
String filePath = "D:\\hwxs.txt";
FileReader fileReader = null;
int read = 0;
char[] bytes =new char[1024];
try {
//2、创建对象FileReader用来读取文件内容
fileReader = new FileReader(filePath);
//3、如果返回-1,表示文件读取完毕
while ((read = fileReader.read(bytes))!=-1){
System.out.print(new String(bytes,0,read));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//4、关闭流 这样用if判断,是因为如果没有创建FileReader成功,去关闭会报错
if(fileReader!=null) {
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3、BufferedInputStream 字节处理流
//1、读取文件的路径
String filePath = "D:\\hwxs.txt";
BufferedInputStream bufferedInputStream = null;
byte[] bytes = new byte[8];
int read = 0;
try {
//2、创建对象BufferedInputStream用来读取文件
bufferedInputStream = new BufferedInputStream(new FileInputStream(filePath));
//3、如果返回-1,表示文件读取完毕
while ((read = bufferedInputStream.read(bytes)) != -1) {
System.out.print(new String(bytes, 0, read));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//4、关闭流,注意只需要关闭bufferedInputStream处理流,底层会自动关闭节点流。
if(bufferedInputStream!=null){
bufferedInputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
4、BufferedReader 字符处理流
//1、读取文件的路径
String filePath = "D:\\hwxs.txt";
BufferedReader bufferedReader = null;
char[] bytes = new char[8];
int read = 0;
String line = null;//按行读取
try {
//2、创建对象FileInputStream用来读取文件内容
bufferedReader = new BufferedReader(new FileReader(filePath));
//3、如果返回-1,表示文件读取完毕
while ((read = bufferedReader.read(bytes)) != -1) {
System.out.print(new String(bytes, 0, read));
}
//3、如果返回null,表示文件读取完毕,按行读取(效率高)
while ((line = bufferedReader.readLine())!=null){
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//4、关闭流,注意只需要关闭bufferedReader处理流,底层会自动关闭节点流。
if(bufferedReader!=null){
bufferedReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
提示:关闭流,注意只需要关闭bufferedReader处理流,因为底层会自动关闭节点流。
二、输出流 使用代码示例
1、FileOutputStream 字节流
//1、写入文件的路径
String filePath = "D:\\hwxs.txt";
FileOutputStream fileOutputStream = null;
try {
//根据项目需求,二选一
//2、创建对象FileOutputStream用来写入文件内容
fileOutputStream = new FileOutputStream(filePath);//会覆盖原来内容
fileOutputStream = new FileOutputStream(filePath,true);//在原来的内容后面进行追加
//3
//3.1、写入一个字节
fileOutputStream.write('A');
//3.2、写入字符串
fileOutputStream.write("JavaIo".getBytes());
//3.3、将len字节从位于偏移量off的指定字节数组写入此文件输出流
//write(byte b[], int off, int len)
fileOutputStream.write("JavaIo".getBytes(),0,"JavaIo".length());
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//4、关闭流
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
2、FileWriter 字符流
//1、写入文件的路径
String filePath = "D:\\hwxs.txt";
FileWriter fileWriter = null;
try {
//2、创建对象FileOutputStream用来写入文件内容(根据需求2选其一)
fileWriter = new FileWriter(filePath);//会覆盖原来内容
fileWriter = new FileWriter(filePath,true);//在原来的内容后面进行追加
//3
//3.1、写入一个字节
fileWriter.write('A');
//3.2、写入字符串
fileWriter.write("JavaIo流我最爱");
//3.3、将len字节从位于偏移量off的指定字节数组写入此文件输出流
//write(byte b[], int off, int len) 写入字符串指定部位
fileWriter.write("JavaIo流我最爱".toCharArray(),0,"JavaIo流我最爱".length());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//4、关闭流
if (fileWriter != null) {
fileWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3、BufferedOutputStream 字符处理流
//1、写入文件的路径
String filePath = "D:\\hwxs.txt";
//2、创建对象BufferedOutputStream用来写入文件
BufferedOutputStream bufferedOutputStream = null;
try {
//根据项目需求,二选一
bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(filePath));//会覆盖原来内容
// bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(filePath,true));//在原来的内容后面进行追加
//3.1、写入一个字节
bufferedOutputStream.write('A');
//3.2、写入字符串
bufferedOutputStream.write("JavaIo".getBytes());
//3.3、将len字节从位于偏移量off的指定字节数组写入此文件输出流
//write(byte b[], int off, int len) 写入字符串指定部位
bufferedOutputStream.write("JavaIo".getBytes(),0,"JavaIo".length());
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(bufferedOutputStream!=null){
bufferedOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
4、BufferedWriter 字符处理流
//1、写入文件的路径
String filePath = "D:\\hwxs.txt";
//2、创建对象BufferedWriter用来写入文件内容
BufferedWriter bufferedWriter = null;
try {
//根据项目需求,二选一
//会覆盖原来内容
bufferedWriter = new BufferedWriter(new FileWriter(filePath));
//在原来的内容后面进行追加
bufferedWriter = new BufferedWriter(new FileWriter(filePath,true));
bufferedWriter.write("我是第一行");
//插入一个换行符
bufferedWriter.newLine();
bufferedWriter.write("我是第二行");
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(bufferedWriter!=null){
bufferedWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
三、实现拷贝功能
提示:根据上面学到的输入、输出流的使用,我们接下来结合一下输入和输出流,做一个拷贝的功能,比如我需要把C盘的一张照片拷贝到D盘去,因为照片是二进制文件,所以我们需要使用到字节流。我将使用BufferedInputStream以及BufferedOutputStream来完成。
参考代码如下:
//1、读取文件的路径
String filePath = "C:\\io.png";
//1、输出文件的路径
String filePathOut = "D:\\io.png";
BufferedInputStream bufferedInputStream = null;
BufferedOutputStream bufferedOutputStream = null;
byte[] bytes = new byte[8];
int read = 0;
try {
//2、创建对象BufferedInputStream用来读取文件
bufferedInputStream = new BufferedInputStream(new FileInputStream(filePath));
//2、创建对象BufferedInputStream用来写入文件
bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(filePathOut));
//3、如果返回-1,表示文件读取完毕
while ((read = bufferedInputStream.read(bytes)) != -1) {
bufferedOutputStream.write(bytes,0,read);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//4、关闭流,注意只需要关闭bufferedInputStream和bufferedOutputStream处理流,底层会自动关闭节点流。
if(bufferedInputStream!=null){
bufferedInputStream.close();
}
if(bufferedOutputStream!=null){
bufferedOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
四、对象处理流
序列化机制可以将对象转换成字节序列,这些字节序列可以保存在磁盘上,也可以在网络中传输,并允许程序将这些字节序列再次恢复成原来的对象。其中,对象的序列化(Serialize),是指将一个Java对象写入IO流中,对象的反序列化(Deserialize),则是指从IO流中恢复该Java对象。
若对象要支持序列化机制,则它的类需要实现Serializable接口,该接口是一个标记接口,它没有提供任何方法,只是标明该类是可以序列化的,Java的很多类已经实现了Serializable接口,如包装类、String、Date等。
若要实现序列化,则需要使用对象流ObjectInputStream和ObjectOutputStream。其中,在序列化时需要调用ObjectOutputStream对象的writeObject()方法,以输出对象序列。在反序列化时需要调用ObjectInputStream对象的readObject()方法,将对象序列恢复为对象。
1、ObjectOutputStream
序列化对象代码如下:
//1、序列号文件的路径
String filePath = "D:\\data";
//2、创建对象BufferedOutputStream用来写入文件
ObjectOutputStream objectOutputStream = null;
try {
objectOutputStream = new ObjectOutputStream(new FileOutputStream(filePath));
objectOutputStream.writeInt(66);// int ->Integer(实现了Serializable)
objectOutputStream.writeObject(new Person("大雄有哆啦梦",21));
} catch (IOException e) {
e.printStackTrace();
}finally {
if(objectOutputStream!=null){
try {
objectOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//记得序列号
class Person implements Serializable {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
2、ObjectOutputStream
反序列化代码如下:
//1、反序列号文件的路径
String filePath = "D:\\data";
//2、创建对象BufferedOutputStream用来写入文件
ObjectInputStream objectInputStream = null;
//3、注意:取出的数据需要与保存的数据位置一致
try {
objectInputStream = new ObjectInputStream(new FileInputStream(filePath));
// 66
System.out.println(objectInputStream.readInt());
try {
//Person{name='大雄有哆啦梦', age=21}
System.out.println(objectInputStream.readObject());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(objectInputStream!=null){
try {
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意事项:
- 读写顺序要一致(上述代码示例中有提醒)
- 若对象要支持序列化机制,则它的类需要实现Serializable接口
- 序列化的类建议添加SerialVersionUID,那为什么要添加呢?
serialVersionUID代表序列化的版本,通过定义类的序列化版本,在反序列化时,只要对象中所存的版本和当前类的版本一致,就允许做恢复数据的操作,否则将会抛出序列化版本不一致的错误。
如果不定义序列化版本,在反序列化时可能出现冲突的情况,例如:
-
创建该类的实例,并将这个实例序列化,保存在磁盘上;
-
升级这个类,例如增加、删除、修改这个类的成员变量;
-
反序列化该类的实例,即从磁盘上恢复修改之前保存的数据。
在第3步恢复数据的时候,当前的类已经和序列化的数据的格式产生了冲突,可能会发生各种意想不到的问题。增加了序列化版本之后,在这种情况下则可以抛出异常,以提示这种矛盾的存在,提高数据的安全性。
- 序列化对象时,默认将里面所有属性一起序列化了,除了static或者transient修饰的成员
- 序列化对象时,要求里面的属性的类型也需要实现序列号接口
- 序列化具备可继承性,也就是如果某类已经实现了序列化,则它的所有子类也默认已经实现了序列化
五、转换处理流
简单介绍:
- InputStreamReader与OutputStream:可以将字节流转换成字符流
- 当处理纯文本数据时,如果使用字符流效率更高,并且可以有效解决乱码问题,所有建议将字节流转换成字符流(支持指导格式编码:比如utf-8、gbk等等)
1、InputStreamReader
代码示例如下:
//1、读取文件的路径
String filePath = "D:\\hwxs.txt";
InputStreamReader inputStreamReader = null;
BufferedReader bufferedReader = null;
String line = null;
try {
//2、把 FileInputStream(filePath) 转换成InputStreamReader,指定了编码
inputStreamReader = new InputStreamReader(new FileInputStream(filePath),"gbk");
//3、把inputStreamReader 传入 BufferedReader(读取字符效率高)
bufferedReader = new BufferedReader(inputStreamReader);
//4、如果返回null,表示文件读取完毕
while ((line = bufferedReader.readLine()) != null) {
System.out.print(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//5、关闭流bufferedReader 关闭最外层流就可以了
if (bufferedReader != null) {
bufferedReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2、InputStreamReader
代码示例如下:
//1、写入文件的路径
String filePath = "D:\\hwxs.txt";
OutputStreamWriter outputStreamWriter = null;
String line = null;
try {
//2、把 FileInputStream(filePath) 转换成InputStreamReader,指定了编码
outputStreamWriter = new OutputStreamWriter(new FileOutputStream(filePath),"utf-8");
//3、写入操作
outputStreamWriter.write("Java IO从入门到精通");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//4、关闭流outputStreamWriter 关闭最外层流就可以了
if (outputStreamWriter != null) {
outputStreamWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
六、打印处理流
System.out.println();是不是对这题语句特别熟悉,看看下面的代码你就豁然开朗了!
//从源码中得出 public final static PrintStream out = null;
PrintStream out = System.out;
//在默认情况下,PrintStream 输出的位置是 标准输出 即显示器
out.print("标准输出");
//System.out.println(); 底层用的是BufferedWriter的write(字节流)进行输出
out.write("标准输出".getBytes());
out.close();
//可以指定输出位置
System.setOut(new PrintStream("D:\\f1.txt"));
System.out.println("指定输出位置D:\\f1.txt");
七、节点流(低级流)和处理流(包装流)区别和联系
1、节点流是底层流,直接和数据源相接。
2、处理流包装节点流,即可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入和输出。
3、处理流对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连。
处理流的功能主要体现在以下两个方面:
1、性能提高:主要以增加缓冲的方式来提高输入输出的效率。
2、操作的便捷:处理流可以提供一系列便捷的方法来一次输入输出大批量的数据,使用更加的灵活。
Java IO流的内容结束啦!如果对你有帮助的话还请给个三连~~感谢!