缓冲流、转换流、序列化、打印流

目录

缓冲流

1.1 概述

1.2 字节缓冲流

         构造方法

使用步骤:

字节流缓冲流练习:文件复制

1.3 字符缓冲流

         构造方法

特有方法

1.4 练习:文本排序

转换流

编码引出的问题

InputStreamReader类

构造方法

指定编码读取

OutputStreamWriter类

         构造方法

指定编码写出

转换流理解图解

练习:转换文件编码

序列化

概述

ObjectOutputStream类

构造方法

特有方法

序列化操作

ObjectInputStream类

构造方法

反序列化操作1

反序列化操作2

练习:序列化集合

案例分析

案例实现

打印流

概述

PrintStream类

构造方法

特有方法

改变打印流向


缓冲流

在基本的流对象基础之上创建而来的,就像穿上铠甲的武士一样,相当于是对基本流对象的一种增强。

1.1 概述

缓冲流,也叫高效流,是对4个基本的 FileXxx 流的增强,所以也是4个流,按照数据类型分类:

  • 字节缓冲流BufferedInputStream BufferedOutputStream
  • 字符缓冲流BufferedReader BufferedWriter

缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO

次数,从而提高读写的效率。

1.2 字节缓冲流

构造方法

  • public BufferedInputStream(InputStream in) :创建一个 新的缓冲输入流。
  • public BufferedInputStream(InputStream in,int size) :创建一个指定大小的缓冲输入流。
  • public BufferedOutputStream(OutputStream out) : 创建一个新的缓冲输出流。
  • public BufferedOutputStream(OutputStream out,int size) : 创建一个指定大小的缓冲输出流。

使用步骤:

  • 字节输出流使用步骤(重点)

        1.创建FileOutputStream对象,构造方法中绑定要输出的目的地

        2.创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象对象,提高FileOutputStream对象效率

        3.使用BufferedOutputStream对象中的方法write,把数据写入到内部缓冲区中

        4.使用BufferedOutputStream对象中的方法flush,把内部缓冲区中的数据,刷新到文件中

        5.释放资源(会先调用flush方法刷新数据,第4部可以省略)

  • 字节输入流使用步骤(重点):

        1.创建FileInputStream对象,构造方法中绑定要读取的数据源

        2.创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率

        3.使用BufferedInputStream对象中的方法read,读取文件

        4.释放资源

 

字节流缓冲流练习:文件复制

1. 使用read() 和 write() 每次读取一个字节

    public static void main(String[] args) {
        // 记录开始时间
        long start = System.currentTimeMillis();
        try(
                // 1. 创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象对象,提高FileOutputStream对象效率
                // 2. 创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率
                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("I://a.pdf"));
                BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D://a.pdf"));
                )
        {
            // 3. 使用read()每一次读取一个字节
            int len = 0;
            while ((len=bis.read())!=-1){
                // 4. 使用write() 每次写入一个字节
                bos.write(len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 记录结束时间
        long end = System.currentTimeMillis();
        System.out.println(end-start);
        
    }

所用时间:73ms

2. 使用数组的方式(自定义数组缓冲区)

   public static void main(String[] args) {
        // 记录开始时间
        long start = System.currentTimeMillis();
        try(
                // 1. 创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象对象,提高FileOutputStream对象效率
                // 2. 创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率
                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("I://a.pdf"));
                BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D://a.pdf"));
                )
        {
            // 定义一个数组缓冲区
            byte[] buff = new byte[1024];
            // 3. 使用read(byte[] buff) 每次读取数组大小的字节放入数组缓冲区
            int len = 0;
            while ((len=bis.read(buff))!=-1){
                // 4. 使用write(byte[] buff) //每次将数组大小的字节写入文件
                bos.write(buff,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 记录结束时间
        long end = System.currentTimeMillis();
        System.out.println(end-start);

    }

所用时间:21ms

1.3 字符缓冲流

构造方法

  • public BufferedReader(Reader in) :创建一个 新的缓冲输入流。
  • public BufferedWriter(Writer out) : 创建一个新的缓冲输出流。

特有方法

字符缓冲流的基本方法与普通字符流调用方式一致,不再阐述,我们来看它们具备的特有方法。

  • BufffferedReaderpublic String readLine() : 读一行文字。
  • BufffferedWriterpublic void newLine() : 写一行行分隔符,由系统属性定义符号。

readLine 方法演示,代码如下:

    public static void main(String[] args) throws IOException {
        // 1. 创建缓冲输入流
        BufferedReader br = new BufferedReader(new FileReader("a.txt"));

        // 2. 定义字符串,保存每次读取的一行数据
        String line = null;
        // 3. 使用readLine();读取一行数据
        while ((line = br.readLine())!=null){
            System.out.println(line);
        }

        // 4. 释放资源
        br.close();
    }

newLine 方法演示,代码如下:

    public static void main(String[] args) throws IOException {
        // 1. 创建字符缓冲输出流
        BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));

        // 2. 写入数据到文件,是用newLine();换行
        for (int i = 0; i < 5; i++) {
            bw.write("哈哈哈");
            bw.newLine();
        }
        // 4. 释放资源
        bw.close();
    }

1.4 练习:文本排序

  • 练习        

        对文本的内容进行排序,按照(1,2,3....)顺序排序

  • 分析:

        1.创建一个TreeMap集合对象,可以:存储每行文本的序号(1,2,3,..);value:存储每行的文本
        2.创建字符缓冲输入流对象,构造方法中绑定字符输入流
        3.创建字符缓冲输出流对象,构造方法中绑定字符输出流
        4.使用字符缓冲输入流中的方法readline,逐行读取文本
        5.对读取到的文本进行切割,获取行中的序号和文本内容
        6.把切割好的序号和文本的内容存储到TreeMap集合中(key序号是有序的,会自动排序1,2,3,4..)
        7.遍历TreeMap集合,获取每一个键值对
        8.把每一个键值对,拼接为一个文本行
        9.把拼接好的文本,使用字符缓冲输出流中的方法write,写入到文件中
        10.释放资源

    public static void main(String[] args) throws IOException {
        // 1. 创建字符缓冲流
        BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt"));
        BufferedReader br = new BufferedReader(new FileReader("in.txt"));

        // 2. 创建一个treemap 集合用来存取读取的行,key:行号,value:内容
        TreeMap<String, String> map = new TreeMap<String,String>();

        // 3. 读取一行内容,进行分割
        String line = null;
        while ((line = br.readLine())!=null ){
            // 分割数据
            String[] split = line.split("\\.");
            // 将数据存入map 中
            map.put(split[0], split[1]);
        }
        // 4. 遍历map 集合
        for (String key:map.keySet()) {
            String value = map.get(key);

            // 5. 将key 和 value 进行拼接
            String newLine = key+value;
           // 6. 将拼接好的数据写入文件
            bw.write(newLine);
            bw.newLine();// 写入换行
        }

        // 关闭资源
        bw.close();
        br.close();


    }

转换流

编码引出的问题

在IDEA中,使用 FileReader 读取项目中的文本文件。由于IDEA的设置,都是默认的 UTF-8 编码,所以没有任何

问题。但是,当读取Windows系统中创建的文本文件时,由于Windows系统的默认是GBK编码,就会出现乱码。

    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("D://a.txt");

        int len = 0;
        while ((len= fr.read())!=-1){
            System.out.print((char)len);  // 结果:�?���ʡ�Ϸ�����˸��������ķ�
        }

        fr.close();
    }

InputStreamReader

转换流 java.io.InputStreamReader ,是Reader的子类,是从字节流到字符流的桥梁它读取字节,并使用指定

的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集

构造方法

  • InputStreamReader(InputStream in) : 创建一个使用默认字符集的字符流。
  • InputStreamReader(InputStream in, String charsetName) : 创建一个指定字符集的字符流。

指定编码读取

/*
    java.io.InputStreamReader extends Reader
    InputStreamReader:是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。(解码:把看不懂的变成能看懂的)

     使用步骤:
        1.创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
        2.使用InputStreamReader对象中的方法read读取文件
        3.释放资源
     注意事项:
        构造方法中指定的编码表名称要和文件的编码相同,否则会发生乱码
 */
public class Demo2 {
    public static void main(String[] args) throws IOException {
        //read_gbk();
        read_utf8();
    }



    /*
        使用InputStreamReader读取GBK格式的文件
     */
    private static void read_gbk() throws IOException {
        //1.创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
        InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk_s.txt"), "gbk");
        //2.使用InputStreamReader对象中的方法read读取文件
        int len = 0;
        while ((len = isr.read())!=-1){
            System.out.print((char)len);
        }
        //3.释放资源
        isr.close();
    }

     /*
        使用InputStreamReader读取UTF-8格式的文件
     */
     private static void read_utf8() throws IOException {
         //1.创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
         InputStreamReader isr = new InputStreamReader(new FileInputStream("utf8_s.txt"), "utf-8");
         //2.使用InputStreamReader对象中的方法read读取文件
         int len = 0;
         while ((len = isr.read())!=-1){
             System.out.print((char)len);
         }
         //3.释放资源
         isr.close();
     }
}

OutputStreamWriter

构造方法

  • OutputStreamWriter(OutputStream in) : 创建一个使用默认字符集的字符流。
  • OutputStreamWriter(OutputStream in, String charsetName) : 创建一个指定字符集的字符流。

指定编码写出

/*
    java.io.OutputStreamWriter extends Writer
    OutputStreamWriter: 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。(编码:把能看懂的变成看不懂)
    使用步骤:
        1.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
        2.使用OutputStreamWriter对象中的方法write,把字符转换为字节存储缓冲区中(编码)
        3.使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
        4.释放资源
 */
public class Demo3 {
    public static void main(String[] args) throws IOException {
       //  write_gbk();
        write_utf_8();
    }
  /*
    使用转换流OutputStreamWriter 写utf-8格式的文件
    * */
    private static void write_gbk() throws IOException {
        //1.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk_s.txt"), "gbk");
        //2.使用OutputStreamWriter对象中的方法write,把字符转换为字节存储缓冲区中(编码)
        osw.write("我喜欢你");
        // 3.使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
        osw.flush();

        // 4. 关闭资源
        osw.close();
    }
    /*
      使用转换流OutputStreamWriter 写GBK格式的文件
      * */
    private static void write_utf_8() throws IOException {
        //1.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf8_s.txt"), "utf-8");
        //2.使用OutputStreamWriter对象中的方法write,把字符转换为字节存储缓冲区中(编码)
        osw.write("我喜欢你");
        // 3.使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
        osw.flush();

        // 4. 关闭资源
        osw.close();
    }

}

转换流理解图解

练习:转换文件编码

GBK编码的文本文件,转换为UTF-8编码的文本文件。

    public static void main(String[] args) throws IOException {
       // 1. 创建转换流
        InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk_s.txt"), "gbk");
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf-8.txt"),"utf-8");

        // 2. 创建数组缓冲区
        char[] buff = new char[1024];

        // 3. 读取文件
        int len = 0;
        while ((len=isr.read(buff))!=-1){
            // 4. 写入文件
            osw.write(buff,0,len);
        }
        // 5. 关闭流
        osw.close();
        isr.close();

    }

序列化

概述

Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该 对象的数据 对象的

类型 对象中存储的属性 等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。

反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化对象的数据 对象的类型 对象中

存储的数据 信息,都可以用来在内存中创建对象。

ObjectOutputStream

java.io.ObjectOutputStream 类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。

构造方法

  • public ObjectOutputStream(OutputStream out) : 创建一个指定OutputStreamObjectOutputStream

特有方法

  • public final void writeObject(Object obj):将指定的对象写入 ObjectOutputStream

序列化操作

1. 一个对象要想序列化,必须满足两个条件:

  • 该类必须实现 java.io.Serializable 接口, Serializable 是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出 NotSerializableException
  • 该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用 transient 关键字修饰。
import java.io.Serializable;

public class Person implements Serializable {
    private String name;
    private int age;
    private transient String addr;  // 不可被序列化

    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 String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", addr='" + addr + '\'' +
                '}';
    }
}

2.写出对象方法

  • public final void writeObject (Object obj) : 将指定的对象写出。
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

/**
 java.io.ObjectOutputStream extends OutputStream
    ObjectOutputStream:对象的序列化流
    作用:把对象以流的方式写入到文件中保存
 使用步骤:
    1.创建ObjectOutputStream对象,构造方法中传递字节输出流
    2.使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中
    3.释放资源
 */
public class Demo1 {
    public static void main(String[] args) throws IOException {

        // 创建person对象
        Person p = new Person();
        p.setName("小明");
        p.setAge(20);
        p.setAddr("beijing");
        //1.创建ObjectOutputStream对象,构造方法中传递字节输出流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt"));
        //2.使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中
        oos.writeObject(p);
        //3.释放资源
        oos.close();
    }
}

ObjectInputStream

ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。

构造方法

  • public ObjectInputStream(InputStream in) : 创建一个指定InputStreamObjectInputStream

反序列化操作1

如果能找到一个对象的class文件,我们可以进行反序列化操作,调用 ObjectInputStream 读取对象的方法:

  • public final Object readObject () : 读取一个对象。
import java.io.*;

/*
    java.io.ObjectInputStream extends InputStream
    ObjectInputStream:对象的反序列化流
    作用:把文件中保存的对象,以流的方式读取出来使用

    使用步骤:
        1.创建ObjectInputStream对象,构造方法中传递字节输入流
        2.使用ObjectInputStream对象中的方法readObject读取保存对象的文件
        3.释放资源
        4.使用读取出来的对象(打印)

     readObject方法声明抛出了ClassNotFoundException(class文件找不到异常)
     当不存在对象的class文件时抛出此异常
     反序列化的前提:
        1.类必须实现Serializable
        2.必须存在类对应的class文件
 */
public class Demo2 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
       // 1.创建ObjectInputStream对象,构造方法中传递字节输入流
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.txt"));
        // 2.使用ObjectInputStream对象中的方法readObject读取保存对象的文件
        Object obj = ois.readObject();
        // 3.释放资源
        ois.close();
        // 4.使用读取出来的对象(打印)
        System.out.println(obj);  // Person{name='小明', age=20, addr='null'} addr 没有被序列化
    }
}

反序列化操作2

另外,当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操 作也会失败,抛出一个 InvalidClassException 异常。发生这个异常的原因如下:

  • 该类的序列版本号与从流中读取的类描述符的版本号不匹配
  • 该类包含未知数据类型
  • 该类没有可访问的无参数构造方法

Serializable 接口给需要序列化的类,提供了一个序列版本号。 serialVersionUID 该版本号的目的在于验证序列化的对象和对应类是否版本匹配

public class Person implements Serializable {

    // 加入序列版本号
    private static final long serialVersionUID = 11L;
    private String name;
    public int age;
    private transient String addr;  // 不可被序列化
    

    ...
}

练习:序列化集合

1. 将存有多个自定义对象的集合序列化操作,保存到 list.txt 文件中。

2. 反序列化 list.txt ,并遍历集合,打印对象信息。

案例分析

1. 把若干学生对象 ,保存到集合中。

2. 把集合序列化。

3. 反序列化读取时,只需要读取一次,转换为集合类型。

4. 遍历集合,可以打印所有的学生信息

案例实现

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //1. 把若干学生对象 ,保存到集合中。
        ArrayList<Person> list = new ArrayList<>();
        Person p1 = new Person("lisi1",20,"beijing");
        Person p2 = new Person("lisi2",40,"shanghai");
        Person p3 = new Person("lisi3",30,"xian");
        list.add(p1);
        list.add(p2);
        list.add(p3);
        //2. 把集合序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("list.txt"));
        oos.writeObject(list);
        //3. 反序列化读取时,只需要读取一次,转换为集合类型。
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("list.txt"));
        ArrayList<Person> obj = (ArrayList)ois.readObject();
        //4. 遍历集合,可以打印所有的学生信息
        for (Person p : obj){
            System.out.println(p);
        }
    }
}

打印流

概述

平时我们在控制台打印输出,是调用 print 方法和 println 方法完成的,这两个方法都来自于 java.io.PrintStream 类,该类能够方便地打印各种数据类型的值,是一种便捷的输出方式。

PrintStream

构造方法

  • public PrintStream(String fileName) : 使用指定的文件名创建一个新的打印流。
  • public PrintStream(File file):输出的目的地是一个文件
  • public PrintStream(OutputStream out):输出的目的地是一个字节输出流

特有方法

  • public void print(任意类型的值)
  • public void println(任意类型的值并换行)
import java.io.FileNotFoundException;
import java.io.PrintStream;

/*
    java.io.PrintStream:打印流
        PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。
    PrintStream特点:
        1.只负责数据的输出,不负责数据的读取
        2.与其他输出流不同,PrintStream 永远不会抛出 IOException
        3.有特有的方法,print,println
            void print(任意类型的值)
            void println(任意类型的值并换行)
    注意:
        如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表 97->a
        如果使用自己特有的方法print/println方法写数据,写的数据原样输出 97->97
 */
public class Demo1 {
    public static void main(String[] args) throws FileNotFoundException {
        //创建打印流PrintStream对象,构造方法中绑定要输出的目的地
        PrintStream ps = new PrintStream("ss.txt");
        //如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表 97->a
        ps.write(97);
        //如果使用自己特有的方法print/println方法写数据,写的数据原样输出 97->97
        ps.println(97);

        // 释放资源
        ps.close();

        // 结果 : a97
    }
}

改变打印流向

System.out 就是 PrintStream 类型的,只不过它的流向是系统规定的,打印在控制台上。不过,既然是流对象,

我们就可以玩一个"小把戏",改变它的流向。

    public static void main(String[] args) throws FileNotFoundException {
        // 调用系统的打印流,控制台直接输出97
        System.out.println(97);

        // 创建打印流指定文件名称
        PrintStream ps = new PrintStream("ps.txt");

        // 设置系统打印流的流向,输出到ps.txt
        System.setOut(ps);

        // 调用系统的打印流,ps.txt中输出97
        System.out.println(97);
    }

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值