Skip to content

day12【IO 流】

第一章 转换流

1.1 编码引出的问题

在 IDEA 中,使用FileReader 读取项目中的文本文件。由于 IDEA 的设置,都是默认的UTF-8编码,所以没有任何问题。但是,当读取 Windows 系统中创建的文本文件时,由于 Windows 系统的默认是 GBK 编码,就会出现乱码。

1641785672955

java
public class ReaderDemo {
    public static void main(String[] args) throws IOException {
        FileReader fileReader = new FileReader("E:\\a.txt");
        int read;
        while ((read = fileReader.read()) != -1) {
            System.out.print((char)read);
        }
        fileReader.close();
    }
}
输出结果:
���

那么如何读取 GBK 编码的文件呢?

1641786222376

1641786355084

1.2 InputStreamReader 类

转换流java.io.InputStreamReader,是 Reader 的子类,是 FileReader 的父类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。

java
InputStreamReader(InputStream in,String charset)//可以设置编码
InputStreamReader(InputStream in) //使用默认的编码集  FileReader跟他一样,而且还短。

1641786638172

1641787622904

构造方法

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

构造举例,代码如下:

java
InputStreamReader isr = new InputStreamReader(new FileInputStream("in.txt"));
InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt") , "GBK");

指定编码读取

java
package com.itheima.changeStream;

import java.io.FileInputStream;
import java.io.FileReader;
import java.io.InputStreamReader;

public class ChangeStreamDemo02 {
    public static void main(String[] args) throws Exception {
        //在idea当前的默认utf-8环境下  使用普通的字符输入流读取 Windows中的GBK文件
        // 使用 InputStreamReader

//        InputStreamReader fr = new InputStreamReader(底层字节流,"要解析的文件的字符编码");
        //先创建字节流
        FileInputStream in = new FileInputStream("E:\\a.txt");
        InputStreamReader isr = new InputStreamReader(in, "GBK");


        int read;
        while ((read = isr.read()) != -1) {
            System.out.print((char)read);
        }
        isr.close();
        in.close();
    }
}

什么时候用 FIleReader 什么时候用 InputStreamReader?

如果我们不涉及 编码不同情况,直接使用 FileReader。

如果读取的文件跟我们当前项目不一致,那么就是用 InputStreamReader.

1.3 OutputStreamWriter 类

转换流java.io.OutputStreamWriter ,是 Writer 的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。

构造方法

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

构造举例,代码如下:

java
OutputStreamWriter isr = new OutputStreamWriter(new FileOutputStream("out.txt"));
OutputStreamWriter isr2 = new OutputStreamWriter(new FileOutputStream("out.txt") , "GBK");

指定编码写出

java
package com.itheima.changeStream;

import java.io.FileOutputStream;
import java.io.OutputStreamWriter;

public class ChangeStreamDemo03 {

    /*
    FileWriter  写出去文件 都是默认的 编码 UTF-8
      我想让写出去的文件  是 GBK编码的呢?
      它的父类
         OutputStreamWriter
             OutputStreamWriter(OutputStream out,String charset) //按照指定编码写数据
             OutputStreamWriter(OutputStream out) //按照默认编码写数据
     */
    public static void main(String[] args)throws Exception {
        //创建 字节输出流
        FileOutputStream fos = new FileOutputStream("day12\\gbk_file.txt");
        OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");

        //写中文
        osw.write("你好");
        osw.close();
        fos.close();

    }
}

转换流理解图解

字符流=字节流+编码表

java
InputStreamReader 是字节流通向字符流的桥梁  解码  看不懂字节--看得懂字符
OutputStreamWriter 是字符流通向字节流的桥梁: 编码  看得懂字符--看不懂的字节

转换流是字节与字符间的桥梁!

第二章 Properties 集合

2.1 概述

java.util.Properties 继承于 Hashtable ,来表示一个持久的属性集。它使用键值结构存储数据,每个键及其对应值都是一个字符串。该类也被许多 Java 类使用,比如获取系统属性时,System.getProperties 方法就是返回一个Properties对象。

2.2 Properties 类

构造方法

  • public Properties() :创建一个空的属性列表。

与流相关的方法

  • public void load(InputStream inStream): 从字节输入流中读取键值对。
  • public void load(Reader reader): 从字符输入流中读取键值对。

参数中使用了字节输入流,通过流对象,可以关联到某文件上,这样就能够加载文本中的数据了。文本数据格式:

properties
filename=a.txt
length=209385038
location=D:\a.txt

加载代码演示:

java
package com.itheima.properties;

import java.io.FileReader;
import java.io.FileWriter;
import java.util.Properties;

public class Demo {

    public static void main(String[] args) throws Exception{
        //有个文件  db.properties 放着数据库的 用户名和密码
        //读取这个文件
        FileReader fr = new FileReader("day12\\db.properties");

        //只需要 创建Properties  通过load方法 就可以把流中读取到的 键值对进行解析 存到该集合中
        Properties pp = new Properties();
        pp.load(fr);

        System.out.println(pp);

        // 写出去--开发中不做 知道有
        FileWriter fw = new FileWriter("day12\\cc.properties");
        pp.store(fw,"this is database username and password");

        fw.close();
        fr.close();

    }
}

第三章 序列化

3.1 概述

Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据对象的类型对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。

反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化对象的数据对象的类型对象中存储的数据信息,都可以用来在内存中创建对象。

java
序列化,就是把我们的对象转换为一个字节序列写出去。这叫对象序列化。
反序列化,就是把字节序列重构成一个对象,这叫对象的反序列化。
想要完成序列化 需要 对象输出流 ObjectOutputStream 也可以叫做序列化流。
反之  ObjectInputStrean 可以叫做 对象输入流 反序列化流。

3.2 ObjectOutputStream 类

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

构造方法

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

构造举例,代码如下:

java
FileOutputStream fileOut = new FileOutputStream("employee.txt");
ObjectOutputStream out = new ObjectOutputStream(fileOut);

序列化操作

  1. 一个对象要想序列化,必须满足两个条件:
  • 该类必须实现java.io.Serializable 接口,Serializable 是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
  • 该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient 关键字修饰。
java
public class Employee implements java.io.Serializable {
    public String name;
    public String address;
    public transient int age; // transient瞬态修饰成员,不会被序列化
    public void addressCheck() {
      	System.out.println("Address  check : " + name + " -- " + address);
    }
}
java
package com.itheima.objectstream;

import java.io.Serializable;

public class Student implements Serializable {

    private String name;
    private int age;

    public Student() {
    }

    public Student(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 "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

2.写出对象方法

  • public final void writeObject (Object obj) : 将指定的对象写出。
java
package com.itheima.objectstream;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Demo01 {

    public static void main(String[] args) throws IOException {
        /*
           ObjectOutputStream(OutputStream out)

         */
        //创建字节输出流   因为最终是按照字节
        FileOutputStream fos = new FileOutputStream("day12\\student.stu");
        //创建序列化流 --对象输出流
        ObjectOutputStream oos= new ObjectOutputStream(fos);

        Student stu = new Student("jack马",60);
        //实现一个对象序列化
        oos.writeObject(stu);

        oos.close();
        fos.close();

    }
}

1641800602330

3.3 ObjectInputStream 类

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

构造方法

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

反序列化操作 1

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

  • public final Object readObject () : 读取一个对象。
java
package com.itheima.objectstream;

import java.io.*;

public class Demo02 {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        /*
           ObjectInputStream(InputStream in)

         */
        //创建字节输入流
        FileInputStream fis = new FileInputStream("day12\\student.stu");
       //创建 反序列化流 对象 输入流
        ObjectInputStream ois = new ObjectInputStream(fis);

        //什么叫反序列化  把字节文件中的字节序列 重构回对象
        Object o = ois.readObject();

        System.out.println(o);

    }
}

1641801561747

1641801691481

对于 JVM 可以反序列化对象,它必须是能够找到 class 文件的类。如果找不到该类的 class 文件,则抛出一个 ClassNotFoundException 异常。

反序列化操作 2

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

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

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

java
package com.itheima.objectstream02;

import java.io.Serializable;

public class Student implements Serializable {

   //序列化版本号
    private static final long serialVersionUID = 1L;
    //  transient 瞬态关键字 那个属性不需要被序列化 就加这个修饰符
    public  String name;
    public transient int age;

    public Student() {
    }

    public Student(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 "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

如果做复制请使用 字节缓冲流。

如果做图片的上传处理,用字节流。

如果读取文本数据,使用字符流。写文本数据,字符流。

如果我们进行操作的时候,涉及的不同编码文件读写,用转换流。

如果我们要直接读取属性集文件用 Properties.

如果我们要实现对象的序列化 使用序列化流(一般我们不做,但是可能在源码中看到)。

Released under the MIT License.