Javase-day19-IO流(缓冲流,转换流,序列化、transient关键字、打印流)

一、缓冲流

1、概述

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

  • 字节缓冲流BufferedInputStreamBufferedOutputStream
  • 字符缓冲流BufferedReaderBufferedWriter

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

2、字节缓冲输出流(BufferedOutputStream)

java.io.BufferedOutputStream extends OutputStream

继承自父类的共性成员方法:

  • public void close() :关闭此输出流并释放与此流相关联的任何系统资源。
  • public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。
  • public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。
  • public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
  • public abstract void write(int b) :将指定的字节输出流。

构造方法:

  • BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
  • BufferedOutputStream(OutputStream out, int size) 创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流。

参数:

  • OutputStream out: 字节输出流,我们可以传递FileOutputStream, 缓冲流会给FileOutputStream增加一个缓冲区, 提高FileOutputStream的写入效率
  • int size: 指定缓冲流内部缓冲区的大小, 不写就是默认的大小

字节缓冲输出流的使用步骤:

  • 创建FileOutputStream对象, 构造方法中绑定要输出的目的地
  • 创建BufferedOutputStream对象, 构造方法中传递FileOutputStream对象对象, 提高FileOutputStream对象效率
  • 使用BufferedOutputStream对象中的方法write, 把数据写入到内部缓冲区中
  • 使用BufferedOutputStream对象中的方法flush, 把内部缓冲区中的数据, 刷新到文件中
  • 释放资源(会先调用flush方法刷新数据, 第4部可以省略)
public static void main(String[] args) throws IOException {
    // 1.创建FileOutputStream对象,构造方法中绑定要输出的目的地
    FileOutputStream fos = new FileOutputStream("day10-code-缓冲流\\a.txt");
    // 2.创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象对象,提高FileOutputStream对象效率
    BufferedOutputStream bos = new BufferedOutputStream(fos);
    // 3.使用BufferedOutputStream对象中的方法write,把数据写入到内部缓冲区中
    bos.write("我把数据写入到内部缓冲区中".getBytes());
    // 4.使用BufferedOutputStream对象中的方法flush,把内部缓冲区中的数据,刷新到文件中
    // bos.flush();		// 调用了close方法,那么省略flush方法
    // 5.释放资源(会先调用flush方法刷新数据,第4部可以省略)
    bos.close();
}

3、字节缓冲输入流(BufferedInputStream)

java.io.BufferedInputStream extends InputStream

继承自父类的成员方法:

  • int read():从输入流中读取数据的下一个字节。
  • int read(byte[] b): 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
  • void close(): 关闭此输入流并释放与该流关联的所有系统资源。

构造方法:

  • BufferedInputStream(InputStream in) 创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用
  • BufferedInputStream(InputStream in, int size) 创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。

参数:

  • InputStream in:字节输入流,可以传递FileInputStream,缓冲流会给FileInputStream增加一个缓冲区,提高FileInputStream的读取效率
  • int size: 指定缓冲流内部缓冲区的大小, 不指定默认

字节缓冲输入流使用步骤:

  • 创建FileInputStream对象, 构造方法中绑定要读取的数据源
  • 创建BufferedInputStream对象, 构造方法中传递FileInputStream对象, 提高FileInputStream对象的读取效率
  • 使用BufferedInputStream对象中的方法read, 读取文件
  • 释放资源
public static void main(String[] args) throws IOException {
   // 1.创建FileInputStream对象,构造方法中绑定要读取的数据源
   FileInputStream fis = new FileInputStream("day10-code-缓冲流\\a.txt");
   // 2.创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率
   BufferedInputStream bis = new BufferedInputStream(fis);
   // 3.使用BufferedInputStream对象中的方法read, 读取文件
   // int read(byte[] b): 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
   byte[] bytes = new byte[1024];  // 存储每次读取的数据
   int len = 0;    // 记录每次读取的有效字节个数
   while ((len = bis.read(bytes)) != -1) {
       System.out.println(new String(bytes, 0, len));
   }
   // 4.释放资源
   bis.close();
}

4、字符缓冲输出流(BufferedWriter)

java.io.BufferedWriter extends Writer

继承自父类的共性成员方法:

  • void write(int c):写入单个字符。
  • void write(char[] cbuf):写入字符数组。
  • abstract void write(char[] cbuf, int off, int len):写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
  • void write(String str):写入字符串。
  • void write(String str, int off, int len): 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
  • void flush():刷新该流的缓冲。
  • void close(): 关闭此流,但要先刷新它。

构造方法:

  • BufferedWriter(Writer out) 创建一个使用默认大小输出缓冲区的缓冲字符输出流。
  • BufferedWriter(Writer out, int sz) 创建一个使用给定大小输出缓冲区的新缓冲字符输出流。

参数:

  • Writer out:字符输出流,可以传递FileWriter, 缓冲流会给FileWriter增加一个缓冲区, 能提高FileWriter的写入效率
  • int sz: 指定缓冲流内部缓冲区的大小, 不写就是默认的大小

特有的成员方法:
void newLine(): 写入一个行分隔符。会根据不同的操作系统, 获取不同的行分隔符(换行符)

System.out.println();中的println()的换行就是调用的newLine()

行分隔符: 换行符号

windows: \r\n,linux: /n,mac: /r

使用步骤:

  • 创建字符缓冲输出流BufferedWriter对象,,构造方法中传递字符输出流
  • 调用字符缓冲输出流中的write方法,把数据写入到内存缓冲区中
  • 调用字符缓冲输出流中的flush方法,把内存缓冲区中的数据, 刷新到文件中
  • 释放资源
public static void main(String[] args) throws IOException {
    // 1.创建字符缓冲输出流对象,构造方法中传递字符输出流
    BufferedWriter bw = new BufferedWriter(new FileWriter("10_IO\\c.txt"));
    // 2.调用字符缓冲输出流中的方法write, 把数据写入到内存缓冲区中
    for (int i = 0; i < 10; i++) {
        bw.write("啦啦啦啦");
        // bw.write("\r\n");  // 之前用这个换行
        bw.newLine();        // 现在,用BufferedWriter类中这个特有的成员方法来换行
    }
    // 3.调用字符缓冲输出流中的方法flush,把内存缓冲区中的数据,刷新到文件中
    bw.flush();
    // 4.释放资源
    bw.close();
}

5、字符缓冲输入流(BufferedReader)

java.io.BufferedReader extends Reader

继承自父类的共性成员方法:

  • int read(): 读取单个字符并返回。
  • int read(char[] cbuf): 一次读取多个字符,将字符读入数组。
  • void close(): 关闭该流并释放与之关联的所有资源。

构造方法:

  • BufferedReader(Reader in) 创建一个使用默认大小输入缓冲区的缓冲字符输入流。
  • BufferedReader(Reader in, int sz) 创建一个使用指定大小输入缓冲区的缓冲字符输入流。

参数:

  • Reader in:字符输入流, 可以传递FileReader, 缓冲流会给FileReader增加一个缓冲区, 提高FileReader的读取效率

特有的成员方法:
String readLine(): 读取一个文本行( 读取一行数据 )

行的终止符号: 通过下列字符之一即可认为某行已终止:换行 (’\n’)、回车 (’\r’) 或 回车后直接跟着换行(\r\n)。

该方法的返回值: 包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null

使用步骤:

  • 创建字符缓冲输入流对象,构造方法中传递字符输入流
  • 使用字符缓冲输入流对象中的方法read/readLine读取文本
  • 释放资源
public static void main(String[] args) throws IOException {
    // 1.创建字符缓冲输入流对象,构造方法中传递字符输入流
    BufferedReader br = new BufferedReader(new FileReader("day10-code-缓冲流\\c.txt"));

    // 2.使用字符缓冲输入流对象中的方法read/readLine读取文本
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
    // 3.释放资源
    br.close();
}

6、文本排序练习

二、转换流

1、概述

编码: 字符 --> 字节
解码: 字节 --> 字符
字符集 Charset:也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。
在这里插入图片描述

  • ASCII字符集
  • ISO-8859-1字符集
  • GBK字符集两个字节存储一个中文
  • Unicode字符集: UTF-8编码方案,可以用来表示Unicode标准中任何字符,它是电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。国际标准编码表(UTF-8)使用三个字节存储一个中文

转换流:

  • InputStreamReader类:是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。
  • OutputStreamWriter类:是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。

2、OutputStreamWriter类

java.io.OutputStreamWriter extends Writer:是字符流通向字节流的桥梁,可使用指定的 charset(字符集)将要写入流中的字符编码成字节。

继续自父类的共性成员方法:

  • void write(int c) :写入单个字符。
  • void write(char[] cbuf):写入字符数组。
  • abstract void write(char[] cbuf, int off, int len):写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
  • void write(String str):写入字符串。
  • void write(String str, int off, int len): 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
  • void flush():刷新该流的缓冲。
  • void close() :关闭此流,但要先刷新它。

构造方法:

  • OutputStreamWriter(OutputStream out):创建使用默认字符编码的 OutputStreamWriter。参数 OutputStream out:字节输出流,可以用来写转换之后的字节到文件中
  • OutputStreamWriter(OutputStream out, String charsetName): 创建使用指定字符集的 OutputStreamWriter。参数 String charsetName:指定的编码表名称,不区分大小写,可以是utf-8/UTF-8、gbk/GBK…不指定默认使用UTF-8

使用步骤:

  • 创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
  • 使用OutputStreamWriter对象中的方法write,把字符转换为字节存储缓冲区中(编码)
  • 使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
  • 释放资源
private static void write_utf_8() throws IOException {
    // 1.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
    // OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("10_IO\\gbk.txt"),"gbk");
    
    OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("10_IO\\utf_8.txt"));
    // 不指定默认使用UTF-8
    
    // 2.使用OutputStreamWriter对象中的方法write,把字符转换为字节存储缓冲区中(编码)
    osw.write("你好");
    // 3.使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
    osw.flush();
    // 4.释放资源
    osw.close();
}

3、InputStreamReader类

java.io.InputStreamReader extends Reader:是字节流通向字符流的桥梁,使用指定的 charset 读取字节并将其解码为字符。

继承自父类的共性成员方法:

  • int read() :读取单个字符并返回。
  • int read(char[] cbuf):一次读取多个字符,将字符读入数组。
  • void close() :关闭该流并释放与之关联的所有资源。

构造方法:

  • InputStreamReader(InputStream in): 创建一个使用默认字符集的 InputStreamReader。参数 InputStream in: 字节输入流, 用来读取文件中保存的字节
  • InputStreamReader(InputStream in, String charsetName): 创建使用指定字符集的 InputStreamReader。参数 String charsetName:指定的编码表名称,不区分大小写,可以是utf-8/UTF-8、gbk/GBK…不指定默认使用UTF-8

使用步骤:

  • 创建InputStreamReader对象, 构造方法中传递字节输入流和指定的编码表名称
  • 使用InputStreamReader对象中的方法read读取文件
  • 释放资源

注意事项:

  • 构造方法中【指定的编码表名称】要和【文件的编码】相同, 否则会发生乱码
private static void read_utf_8() throws IOException {
    // 1.创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
    // InputStreamReader isr = new InputStreamReader(new FileInputStream("10_IO\\utf_8.txt"), "UTF-8");
    
    InputStreamReader isr = new InputStreamReader(new FileInputStream("10_IO\\utf_8.txt"));	
    // 不指定默认使用UTF-8
    // 2.使用InputStreamReader对象中的方法read读取文件
    int len = 0;
    while ((len = isr.read()) != -1) {
        System.out.println((char) len);
    }
    // 3.释放资源
    isr.close();
}

三、序列化与反序列化

1、什么是序列化与反序列化?

  序列化: 把堆内存中的 Java 对象数据,通过某种方式存储到磁盘文件或者传递给其他网络节点(在网络上传输)。这个过程称为序列化。通俗来说,就是将数据结构或对象转换成二进制串的过程。

  反序列化: 把磁盘文件中的对象数据或者把网络节点上的对象数据,恢复成Java对象模型的过程。也就是,将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。

2、Serializable接口

由于没有在定义的Person类上【实现】Serializable接口,在【序列化】和【反序列化】的时候, ,会抛出 NotSerializableException——“没有序列化异常”

类通过实现 java.io.Serializable 接口以启用其序列化功能, 未实现此接口的类将无法使其任何状态序列化或反序列化

Serializable接口也叫标记型接口,要进行序列化和反序列化的类必须实现Serializable接口,就会给类添加一个标记。

当我们进行序列化和反序列化的时候, 就会检测类上是否有这个标记:

  • 有这个标记: 就可以序列化和反序列化
  • 没有这个标记: 就会抛出 异常——NotSerializableException
定义的Person类(要序列化)
public class Person implements Serializable {
    private String name;
    public int age;

    public Person() {}		// 无参构造
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

3、对象序列化流(ObjectOutputStream类)

java.io.ObjectOutputStream extends OutputStream:对象序列化,把对象以流的方式写入到文件中保存

构造方法:

  • ObjectOutputStream(OutputStream out): 创建写入指定 OutputStream 的 ObjectOutputStream。参数 OutputStream out: 字节输出流

特有的成员方法:

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

使用步骤:

  • 创建ObjectOutputStream对象,构造方法中传递字节输出流
  • 使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中
  • 释放资源
public class Demo01ObjectOutputStream {
    public static void main(String[] args) throws IOException {
        // 1.创建ObjectOutputStream对象, 构造方法中传递字节输出流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("10_IO\\person.txt"));
        // 2.使用ObjectOutputStream对象中的方法writeObject, 把对象写入到文件中
        oos.writeObject(new Person("迪丽热巴", 18));
        // 3.释放资源
        oos.close();
    }
}

4、对象的反序列化流(ObjectInputStream类)

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

构造方法:

  • ObjectInputStream(InputStream in) :创建从指定 InputStream 读取的 ObjectInputStream。参数 InputStream in:字节输入流

特有的成员方法:

  • Object readObject(): 从 ObjectInputStream 读取对象。

使用步骤:

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

readObject方法声明抛出了ClassNotFoundException——class文件找不到异常,当不存在对象的class文件时会抛出此异常。

反序列化的前提

  • 类必须实现Serializable
  • 必须存在类对应的class文件
public static void main(String[] args) throws IOException, ClassNotFoundException {
    // 1.创建ObjectInputStream对象, 构造方法中传递字节输入流
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("10_IO\\person.txt"));
    // 2.使用ObjectInputStream对象中的方法readObject读取保存对象的文件
    Object o = ois.readObject();
    // 3.释放资源
    ois.close();
    // 4.使用读取出来的对象(打印)
    System.out.println(o);

	// 强转为Person类型
    Person p = (Person) o;
    System.out.println(p.getName() + p.getAge());
}

反序列化操作2

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

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

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

public class Person implements Serializable {
	// 加入序列版本号
    private static final long serialVersionUID = 1L;
    
    private String name;
    // private static int age;
    // private transient int age;
    public int age;
    ......

5、transient关键字:瞬态关键字

static:静态关键字

  • 静态优先于非静态加载到内存中(静态优先于对象进入到内存中),被static修饰的成员变量不能被序列化(因为序列化的都是对象,静态不属于对象,而是被所有对象所共享)

transient:瞬态关键字

  • 被transient修饰成员变量, 不能被序列化

transient和static关键字修饰的成员变量,都不能被序列化,功能差不多,但是transient关键字没有静态的含义。

四、打印流(PrintStream类)

java.io.PrintStream extends FileOutputStream extends OutputStream:PrintStream也是一个字节流,为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。

1、PrintStream概述

PrintStream特点:

  • 只负责数据的输出, 不负责数据的读取
  • 与其他输出流不同,PrintStream 永远不会抛出 IOException
  • 有特有的方法:print、println、void print(任意类型的值)、void println(任意类型的值并换行)

构造方法:

  • PrintStream(File file):输出的目的地是一个文件
  • PrintStream(OutputStream out):输出的目的地是一个字节输出流
  • PrintStream(String fileName):输出的目的地是一个文件路径

继承自父类的成员方法:

  • public void close() :关闭此输出流并释放与此流相关联的任何系统资源。
  • public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。
  • public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。
  • public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
  • public abstract void write(int b) :将指定的字节输出流。

注意:

  • 如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表 97 -> a
  • 如果使用自己特有的方法【print() / println()】方法写数据,写的数据按原来的样子样输出 97 -> 97
public class Demo01PrintStream {
    public static void main(String[] args) throws FileNotFoundException {
        // 创建打印流PrintStream对象, 构造方法中绑定要输出的目的地
        PrintStream ps = new PrintStream("day10-code-缓冲流\\print.txt");
        // 如果使用【继承自父类的write方法】写数据, 那么查看数据的时候会查询编码表 97->a
        ps.write(97);        // 打印出 a
        // 如果使用【自己特有的方法print/println方法】写数据, 写的数据原样输出 97->97
        ps.println(97);         // 打印出 97
        ps.println('a');		// 打印出 a
        ps.println("HelloWorld");
        ps.println(true);       // 打印出 true

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

2、改变打印流的流向

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

使用 System.setOut(PrintStream out)方法 可以将输出语句的目的地,改为参数中传递的打印流的目的地。

static void setOut(PrintStream out):重新分配“标准”输出流

public class Demo02PrintStream {
    public static void main(String[] args) throws FileNotFoundException {
        System.out.println("我是在控制台输出的");

        PrintStream ps = new PrintStream("day10-code-缓冲流\\目的地是打印流.txt");
        System.setOut(ps);  // 把输出语句的目的地改变为打印流的目的地
        System.out.println("我是在指定的打印流目的地中输出的");

        ps.close();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值