各自努力,最高处见!加油!
IO流(四)
一、节点流和处理流
- 节点流可以从一个特定的数据源读写数据,如FileReader、FileWriter。
- 处理流(也叫包装流)是链接已存在的流(节点流或处理流)之上,为程序提供更强大的读写功能,如:BufferedReader、BufferedWriter。
BufferedReader类中,有属性Reader,即可以封装一个节点流,该节点流可以是任意的,只要是Reader子类。
二、节点流和处理流的区别和联系
- 节点流是底层流,直接跟数据源相接。
- 处理流(包装流)包装节点流,既可以消除不同节点流的现实差异(对文件操作,对管道操作,对数组操作),也可以提供更方便的方法来完成输入输出。
- 处理流(也叫包装流)对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连。
处理流的功能主要体现在以下两个方面:
- 性能的提高:主要以增加缓冲的方式来提高输入输出的效率。
- 操作的便捷:处理流可能提供一系列便捷的方法来一次输入输出大批量数据,使用更加灵活方便。
三、BufferedReader和BufferedWriter
- BufferedReader和BufferedWriter属于字符流,是按照字符来读取数据的(尽量不要用来读二进制文件)。关闭处理流的时候,只需要关闭外层流即可。
BufferedReader示例代码:
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReader_ {
public static void main(String[] args) throws IOException {
String filePath="D:\\Java_code\\LearnPlus\\news2.txt";
BufferedReader bufferedReader=new BufferedReader(new FileReader(filePath));
String line;
// line=bufferedReader.readLine();//到达流的末尾,返回空
while ((line=bufferedReader.readLine())!=null){
System.out.println(line);
}
bufferedReader.close();//只关闭处理流就行,底层源码会自动关闭节点流。
}
}
BufferedWriter示例代码:
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedWriter_ {
public static void main(String[] args) throws IOException {
String filePath="D:\\Java_code\\LearnPlus\\news3.txt";
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));
//BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath,true));//表示以追加的方式写入,在原来文件的基础上写入内容,相当于本次操作的光标位于文件最末端。
bufferedWriter.write("helloWorld");
bufferedWriter.newLine();//插入一个和系统相关的换行
bufferedWriter.write("helloworld");
bufferedWriter.close();
}
}
使用BufferedReader和BufferedWriter完成文本拷贝
注意:BufferedReader和BufferedWriter是安装字符操作,不要去操作二进制文件【声音、视频、doc、PDF等等】,可能造成文件损坏。
示例代码:
import java.io.*;
public class BufferedCopy {
public static void main(String[] args) {
String srcFilePath="D:\\Java_code\\LearnPlus\\news2.txt";
String dstFilePath="D:\\Java_code\\LearnPlus\\news3.txt";
BufferedReader bufferedReader=null;
BufferedWriter bufferedWriter=null;
String line;
try {
bufferedReader=new BufferedReader(new FileReader(srcFilePath));
bufferedWriter=new BufferedWriter(new FileWriter(dstFilePath));
while ((line=bufferedReader.readLine())!=null){
bufferedWriter.write(line);
bufferedWriter.write("\n");
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
bufferedReader.close();
bufferedWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("拷贝完毕!");
}
}
}
四、BufferedInputStream和BufferedOutputStream
- BufferedInputStream是字节流,在创建BufferedInputStream时会创建一个内部缓冲数组。
- BufferedOutputStream是字节流,实现缓冲的输出流,可以将多个字节写入底层输出流中,而不必对每次字节写入调用底层系统。
示例代码:使用BufferedInputStream和BufferedOutputStream拷贝视频文件
import java.io.*;
public class BufferedInputStream_ {
public static void main(String[] args) {
String srcFilePath="D:\\桌面\\lzm1.mp4";
String dstFilePath="D:\\Java_code\\LearnPlus\\lzm.mp4";
BufferedOutputStream bufferedOutputStream=null;
BufferedInputStream bufferedInputStream=null;
byte[] buff=new byte[1024];//字节读取都是用byte读取
int len;
try {
bufferedInputStream=new BufferedInputStream(new FileInputStream(srcFilePath));
bufferedOutputStream=new BufferedOutputStream(new FileOutputStream(dstFilePath));
while ((len=bufferedInputStream.read(buff))!=-1){
bufferedOutputStream.write(buff,0,len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bufferedInputStream.close();
bufferedOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
五、对象流ObjectInputStream和ObjectOutputStream
先看开发中的要求:
- 将int num=100这个int数据保存到文件中,注意不是100这个数据,而是int 100 。并且,能够从文件中直接恢复int 100。
- 将Dog dog=new Dog90(“小黄”,3)这个dog对象保存到文件中,并且能够从文件恢复。
- 上面的要求,就是能够将基本数据类型或者对象进行序列化和反序列化操作。
序列化和反序列化
- 序列化就是在保存数据时,保存数据的值和数据类型。
- 反序列化就是在恢复数据时,恢复数据的值和数据类型。
- 需要让某个对象支持序列化机制,则必须让其类是可序列化的。
- 为了让某个类是可序列化的,该类必须实现如下两个接口之一:
A、Serializable//这是一个标记接口:一个空接口,没有任何方法。
B、Exernalizable//继承了Serializable,并且有方法要实现。 - ObjectOutputStream提供序列化功能,ObjectInputStream提供反序列化功能。
类图
示例代码:
序列化输出代码
/**
序列化输出代码
*/
import ObjectInputStreamAndObjectOutputStream.Dog;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class ObjectOutStream_ {
public static void main(String[] args) {
//序列化后,保存的文件格式,不是存文本,而是按照他的格式来存。
String filePath="D:\\Java_code\\LearnPlus\\data.txt";
ObjectOutputStream objectOutputStream =null;
try {
objectOutputStream=new ObjectOutputStream(new FileOutputStream(filePath));
objectOutputStream.writeInt(100);//int -->Integer(Serializable)
objectOutputStream.writeBoolean(true);
objectOutputStream.writeChar('a');
objectOutputStream.writeDouble(9.5);
objectOutputStream.writeUTF("helloWorld");//字符串
objectOutputStream.writeObject(new Dog("老黄",5));//要想实现序列化接口,必须实现接口。
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
objectOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
反序列化读入代码
/**
反序列化读入代码
*/
import ObjectInputStreamAndObjectOutputStream.Dog;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class ObjectInputStream_ {
public static void main(String[] args) {
String filePath="D:\\Java_code\\LearnPlus\\data.txt";
ObjectInputStream objectInputStream=null;
Object o=null;
try {
objectInputStream=new ObjectInputStream(new FileInputStream(filePath));
System.out.println(objectInputStream.readInt());
System.out.println(objectInputStream.readBoolean());
System.out.println(objectInputStream.readChar());
System.out.println(objectInputStream.readDouble());
System.out.println(objectInputStream.readUTF());
try {
o=objectInputStream.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("运行类型:"+o.getClass());//底层Object-->Dog
System.out.println("dog信息:"+o);
//读取对象的信息可以不需要知道Dog类的详细信息,但是想要向下转型的话必须要导入Dog类的本体
Dog dog=(Dog)o;
System.out.println(dog.getName());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
objectInputStream.close();//关闭流
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("程序执行完毕");
}
}
对象实体,要声明为public,让序列化程序和反序列化程序都能读到该类
/**
对象实体,要声明为public,让序列化程序和反序列化程序都能读到该类
*/
import java.io.Serializable;
public class Dog implements Serializable {
private String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = 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;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
本例需注意:读取对象的信息可以不需要知道Dog类的详细信息,但是想要向下转型的话必须要类的本体。由图中序列化后的打开文件,文件中已经存有类的相关信息,所以在反序列化的时候必须要用同一个类才能反序列化成功,否则会抛出异常。
细节说明:
- 序列化的读写顺序要一致。
- 要求序列化或反序列化对象,需要在类中实现Serializable接口。
- 序列化的类中建议添加SerialVersonUID,为了提高版本兼容性。
private static final long serialVersionUID=1L;
- 序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员。
- 序列化对象时,要求里面属性的类型也要实现序列化接口。
- 序列化具备可继承性,也就是如果某类已经实现了序列化,则他的所有子类也已经默认实现了序列化。
六、标准输入输出流
七、转换流InputStreamReader和OutputStreamWriter(把字节流转成一个字符流)
中文在文件中大多默认按照UTF-8编码,流读取数据也默认采用UTF-8格式,但是这样在读取ANSI(gbk国标码)中文的时候会出现乱码。
类图
介绍
- InputStreamReader:Reader的子类,可以将InputStream(字节流)包装成Reader(字符流)。
- OutputStreamWriter:Writer的子类,可以将OutputStream(字节流)包装成Writer(字符流)。
- 当处理纯文本数据时,使用字符流效率更高,并且可以有效解决中文问题,所以建议将字节流转换成字符流。
- 可以在使用指定格式编码格式(比如UTF-8、gbk、gb2312、ISO8859-1等)。
InputStreamReader用法介绍
InputStreamReader只是作为一个转换的中间工具。
示例代码:
import java.io.*;
public class InputStreamReader_ {
public static void main(String[] args) throws IOException {
String filePath="D:\\Java_code\\LearnPlus\\news5.txt";
//news5.txt为ANSI编码文件,按照字符编码FileInputStream读取会乱码
//把FileStreamReader转成InputStreamReader
InputStreamReader inputStreamReader=new InputStreamReader(new FileInputStream(filePath),"gbk");
//把InputStreamReader传入BufferedReader
BufferedReader bufferedReader=new BufferedReader(inputStreamReader);
//读取
String str=bufferedReader.readLine();
System.out.println(str);
bufferedReader.close();//关闭最外层的流
}
}
OutputStreamWriter用法介绍
示例代码:
import java.io.*;
public class OutputStreamWriter_ {
public static void main(String[] args) throws IOException {
//把FileOutputStream字节流转成字符流OutputStreamWriter 只定编码gbk/utf-8/utf8
String filePath="D:\\Java_code\\LearnPlus\\news6.txt";
OutputStreamWriter outputStreamWriter=new OutputStreamWriter(new FileOutputStream(filePath),"gbk");
outputStreamWriter.write("老色批");
outputStreamWriter.close();
}
}
八、打印流PrintStream和PrintWriter
打印流只有输出流,没有输入流。
类图
PrintStream用法介绍
PrintStream是字节流
PrintWriter用法介绍
PrintWriter是字符流
九、总结:字符读取和字节读取的区别
- 字符读取用String对象接收数据,多用于操作文本文件,不能操作二进制文件,会出现乱码。
- 字节读取用byte数组接收数据,多用于操作二进制文件,也能操作字符文件,但是处理效率较低。
- 常用的二进制文件有:图片、视频、doc、PDF等等。