IO流中的处理流
1,处理流之一:缓冲流的使用
缓冲流(内部提供了一个缓冲区)能有效提高文件读写效率。
但要注意缓冲流不能直接作用于文件上,能够直接作用于文件上的是节点流(FileInputStream、FileOutputStream、FileReader、FileWriter是节点流)。处理流是套接在节点流上进行处理的流。
缓冲流有:
- 处理字节:BufferedInputStream、BufferedOutPutStream
- 处理字符:BufferedReader、BufferedWriter
2.1,缓冲流实现非文本文件的复制
使用BufferedInputStream、BufferedOutPutStream
package com.atguigu.java;
import org.junit.Test;
import java.io.*;
/**
* 缓冲流(处理流之一)
*/
public class BufferedTest {
/*实现非文本文件的复制*/
@Test
public void BufferedStreamTest1(){
BufferedInputStream bis= null; //处理流包住节点流
BufferedOutputStream bos= null;
try {
//1,造File对象
File srcFile=new File("img.png");
File destFile=new File("img3.png"); //复制出一个img3.png
//2,造节点流
FileInputStream fis=new FileInputStream(srcFile);
FileOutputStream fos=new FileOutputStream(destFile);
//3,造处理流(此处是造缓冲流)
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//4,复制的细节:读,写
byte[] buffer=new byte[10]; //byte对应字节流
int len;
while ((len=bis.read(buffer))!=-1){
bos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//5,资源关闭(上面共四个流,关外层的流的时候会顺带关闭内层的流)
if (bos!=null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bis!=null){
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
可以改写成方法,然后进行调用:
package com.atguigu.java;
import org.junit.Test;
import java.io.*;
/**
* 缓冲流(处理流之一)
*/
public class BufferedTest {
//实现文件复制方法
public void copyFileWithBuffered(String srcPath,String destPath){
BufferedInputStream bis= null; //处理流包住节点流
BufferedOutputStream bos= null;
try {
//1,造File对象
File srcFile=new File(srcPath);
File destFile=new File(destPath);
//2,造节点流
FileInputStream fis=new FileInputStream(srcFile);
FileOutputStream fos=new FileOutputStream(destFile);
//3,造处理流(此处是造缓冲流)
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//4,复制的细节:读,写
byte[] buffer=new byte[1024]; //byte对应字节流
int len;
while ((len=bis.read(buffer))!=-1){
bos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//5,资源关闭(上面共四个流,关外层的流的时候会顺带关闭内层的流)
if (bos!=null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bis!=null){
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
public void testCopyWithBuffered(){
long start = System.currentTimeMillis();
String srcPath="F:\\programming\\1-视频.avi";
String descPath="F:\\programming\\010-视频-副本.avi";
copyFileWithBuffered(srcPath,descPath);
long end = System.currentTimeMillis();
System.out.println("复制操作花费的时间为:"+(end-start));
}
}
复制成功:
此处的缓冲流花费时间为549ms,而在上期我们使用节点流(FileInputStream、FileOutputStream)测试耗时2000ms。显然使用缓冲流比使用节点流复制速度要快很多。
缓冲流(内部提供了一个缓冲区)能有效提高文件读写效率。
2.2,缓冲流实现文本文件的复制
使用BufferedReader、BufferedWriter(不能处理非文本文件)
①首先在当前module day09下建立一个sanguo.txt的文件,里面随意插入一些文本内容
②编写程序
package com.atguigu.java;
import org.junit.Test;
import java.io.*;
/**
* 缓冲流(处理流之一)
*/
public class BufferedTest {
@Test //使用BufferedReader和BufferedWriter实现文本文件的复制
public void test(){
BufferedReader br= null;
BufferedWriter bw= null;
try {
//1,熟练之后可以使用匿名方式:造文件和造流合并为一步
br = new BufferedReader(new FileReader(new File("sanguo.txt")));
bw = new BufferedWriter(new FileWriter(new File("zhaoyun.txt")));
//2,读写操作
//方式一:
//char[] cbuf=new char[1024]; //字符型用char
//int len;
//while((len=br.read(cbuf))!=-1){
// bw.write(cbuf,0,len);
//}
//方式二:使用String搭配readline()
//readline():一次读一行。返回当前读到的一行的数据(string型),不包含换行符;如果到末尾则返回null
String data;
while ((data=br.readLine())!=null){
bw.write(data+"\n"); //手动换行
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//3,关闭资源
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (br!=null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
抽象基类 | 节点流 | 缓冲流(处理流的一种) |
---|---|---|
InputStream | FileInputStream | BufferedInputStream |
OutputStream | FileOutputStream | BufferedOutPutStream |
Reader | FileReader | BufferedReader |
Writer | FileWriter | BufferedWriter |
2,处理流之二:转换流的使用
要明确的是:两个转换流是字符流。看后缀
测试转换流的使用:
package com.atguigu.java;
import org.junit.Test;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class TransformTest {
@Test
public void test1(){
InputStreamReader isr= null;
try {
FileInputStream fis=new FileInputStream("sanguo.txt");
isr = new InputStreamReader(fis,"UTF-8");
//InputStreamReader()中第参数1是一个字节的输入流,参数2指明字符集(不写则使用系统默认字符集utf-8)
char[] cbuf=new char[20];
int len;
while ((len=isr.read(cbuf))!=-1){
String str=new String(cbuf,0,len); //输出到控制台
System.out.print(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (isr!=null){
try {
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
综合使用InputStreamReader和OutputStreamWriter:
如实现:给文件sanguo.txt字符集换为GBK:
package com.atguigu.java;
import org.junit.Test;
import java.io.*;
public class TransformTest {
@Test
public void test1() {
InputStreamReader isr= null;
OutputStreamWriter osw= null;
try {
File file1=new File("sanguo.txt");
File file2=new File("sanguo_gbk.txt");
FileInputStream fis=new FileInputStream(file1);
FileOutputStream fos=new FileOutputStream(file2);
isr = new InputStreamReader(fis,"utf-8");
osw = new OutputStreamWriter(fos,"gbk");
char[] cbuf=new char[20];
int len;
while ((len=isr.read(cbuf))!=-1){
osw.write(cbuf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (isr!=null){
try {
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (osw!=null){
try {
osw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
成功生成编码为gbk的sanguo_gbk.txt文件:
3,其他处理流(非重点)
3.1,标准输入、输出流(了解)
3.2,打印流(了解)
3.3,数据流(了解)
4,处理流之三:对象流(重点)
4.1,对象序列化机制的引入(要会描述)
4.2,对字符串对象进行序列化和反序列化操作:
演示序列化(ObjectOutputStream)和反序列化(ObjectInputStream)
package com.atguigu.java;
import org.junit.Test;
import java.io.*;
/**
* 对象流的使用: ObjectInputStream、ObjectOutputStream
*/
public class ObjectInputOutputStreamTest {
/*序列化过程(ObjectOutputStream):将内存中的java对象保存到磁盘中,或通过网络传输出去*/
@Test
public void test1(){ //运行test1后,可以在当前module下生成一个名为object.dat的文件
ObjectOutputStream oos= null;
try {
oos = new ObjectOutputStream(new FileOutputStream("object.dat")); //此处的文件object.dat只是一个中间媒介,不必过多在意
oos.writeObject(new String("我爱北京天安门")); //把此字符串信息持久化起来
oos.flush(); //刷新操作
} catch (IOException e) {
e.printStackTrace();
} finally {
if (oos!=null){
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/*反序列化过程(ObjectInputStream):将磁盘文件中的对象还原为内存中的一个对象*/
@Test
public void test2(){ //运行test2后,控制台输出“我爱北京天安门”
ObjectInputStream ois= null;
try {
ois = new ObjectInputStream(new FileInputStream("object.dat"));
Object obj = ois.readObject(); //返回的实际是个字符串
String str= (String)obj;
System.out.println(str);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (ois!=null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
String 类内部本身就实现了serializable接口,可以序列化
4.3,自定义类的序列化和反序列化操作
注意:
要想使一个自定义类的Java对象可序列化需要满足相应的要求:
(否则不能序列化成功)
- ①实现Serializable接口
- ②当前类需要提供一个全局常量:serialVersionUID
- ③除了当前Person类需要实现Serializable接口,还要保证当前类内部所有属性必须是可序列化的。(默认情况下基本数据类型是可序列化的,要注意自定义的属性)
上面的序列化一个String类型的对象没有报错,原因是String内部已经做了相关操作。而我们的自定义类则需要手动去完成这三个需求。
代码演示:
①自定义Person类:
package com.atguigu.java;
import java.io.Serializable;
/**
* 自定义类:Person
* Person类需要满足如下要求才可以实现序列化机制:
* ①实现Serializable接口
* ②当前类需要提供一个全局常量:serialVersionUID
* ③保证当前类内部所有属性必须是可序列化的(默认情况下基本数据类型是可序列化的。
*/
public class Person implements Serializable { //其实Serializable接口内部啥也没有,只起到标识作用,表明相应类的对象可序列化
public static final long serialVersionUID=6523563535L; //序列版本号,随便写一个值
private String name;
private int age;
private Account acct; //Account是自定义的,所以该属性也应该设置为可序列化的。否则不能序列化成功
public Person(){
}
public Person(String name,int age){
this.name=name;
this.age=age;
}
public Person(String name, int age, Account acct) {
this.name = name;
this.age = age;
this.acct = acct;
}
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 "Person{" +
"name='" + name + '\'' +
", age=" + age +
", acct=" + acct +
'}';
}
}
class Account implements Serializable{ //Accountu而要满足可序列化的三个要求
public static final long serialVersionUID=6553637658L;
private double balance;
public Account(double balance) {
this.balance = balance;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
@Override
public String toString() {
return "Account{" +
"balance=" + balance +
'}';
}
}
②测试代码:
package com.atguigu.java;
import org.junit.Test;
import java.io.*;
/**
* 对象流的使用: ObjectInputStream、ObjectOutputStream
*/
public class ObjectInputOutputStreamTest {
/*序列化过程(ObjectOutputStream):将内存中的java对象保存到磁盘中,或通过网络传输出去*/
@Test
public void test1(){ //运行test1后,可以在当前module下生成一个名为person.dat的文件。成功持久化对象
ObjectOutputStream oos= null;
try {
oos = new ObjectOutputStream(new FileOutputStream("person.dat"));
oos.writeObject(new Person("喜羊羊",23)); //把自定义类对象持久化起来
oos.flush(); //刷新操作
oos.writeObject(new Person("懒羊羊",23,new Account(5000)));
oos.flush(); //刷新操作
} catch (IOException e) {
e.printStackTrace();
} finally {
if (oos!=null){
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/*反序列化过程(ObjectInputStream):将磁盘文件中的对象还原为内存中的一个对象*/
@Test
public void test2(){
ObjectInputStream ois= null;
try {
ois = new ObjectInputStream(new FileInputStream("person.dat"));
Object obj1 = ois.readObject();
Person p1= (Person) obj1;
Object obj2 = ois.readObject();
Person p2= (Person) obj2;
System.out.println(p1); //默认调用相应的toString方法
System.out.println(p2); //默认调用相应的toString方法
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (ois!=null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
4.4,对象流总结,场景类比
想象在2090年,小明想带一条🐕,去世界某个地方旅游,不需要交通工具。只需要小明(对象)进入一个时空门,然后就变为一个二进制流(序列化)。目的地的一端使用时空门接受此二进制流,然后再转换为小明实体(反序列化);但不是所有东西都可以任意转化为二进制流,必须贴上一个Serialable(接口)的标签,指明有此转化功能。并且有一个唯一的序号serialVersionUID;人有人的serialVersionUID,🐕有🐕的serialVersionUID,不能乱套,否则人穿梭过去可能变成🐕。