Java 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();
                }
            }
        }
    }
}

在这里插入图片描述


抽象基类节点流缓冲流(处理流的一种)
InputStreamFileInputStreamBufferedInputStream
OutputStreamFileOutputStreamBufferedOutPutStream
ReaderFileReaderBufferedReader
WriterFileWriterBufferedWriter

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,不能乱套,否则人穿梭过去可能变成🐕。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

过期动态

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值