IO流(03)--序列化流、列印流

??魯阿豔??發表於2020-12-17

序列化流

Java提供了一種物件序列化的機制,用一個位元組序列可以表示一個物件,該位元組序列包含該物件的資料、物件的型別和物件中儲存的屬性等資訊。位元組序列寫入到檔案中後,就相當於在檔案中儲存了一個物件資訊。

反之,該位元組序列還可以從檔案讀取出來,重構物件,對它進行反序列化。物件的資料、物件的型別和物件中儲存的資料資訊,都可以用來在記憶體中建立物件。

ObjectOutputStream類

java.io.ObjectOutputStream類,將Java物件的原始資料型別寫入到檔案中,實現物件的持久化儲存。

構造方法
  • public ObjectOutputStream(OutputStream out):建立一個指定的OutputStream的ObjectOutputStream類物件
特有的獨有方法:
  • void writeObject(Object obj):將指定的物件寫入到ObjectOutputStream類物件中。
序列化操作
  1. ​ 一個物件想要能夠序列化和反序列化,必須滿足兩個條件:

    • 該類必須實現java.io.Serializable介面,Serializable介面,是一個標記型介面,如果該類沒有實現Serializable介面,將會丟擲NotSerializableException。

    • 該類的所有屬性必須是可以實現序列化或者反序列化。如果有一個屬性不想讓它參與序列化,則該屬性必須標明是瞬態的,瞬時的,這個關鍵字是transient

public class Student implements Serializable {

    private String name;
    private transient Integer age;// 不讓age屬性參與序列化
    
}    

ObjectInputStream類

java.io.ObjectInputStream類是反序列化流,將之前使用ObjectOutputStream序列化流的原始資料恢復為物件。

構造方法
  • ​ public ObjectInputStream(InputStream in):建立一個指定的InputStream的物件反序列化流物件。
特有的方法:
  • ​ public final Object readObject():從反序列化流中讀取一個物件。

對於JVM來說,能夠進行反序列的物件 ,前提條件是必須能夠找到class檔案的類,如果找不到該類的class檔案,則會丟擲一個ClassNotFoundException異常。

另外,當JVM序列化物件時,能夠找到class檔案,但是class檔案在序列化物件時,發生了修改,那麼反序列化操做會丟擲一個InvalidClassException異常。原因如下:

  • 該類的序列化版本號與從流中讀取出來描述該類的版本號不一致。

  • 該類包含了未知資料型別。

  • 該類沒有可訪問的無參構造方法。

Serializable介面給需要序列化的類,提供了一個序列化版本號,serialVersionUID 該版本號的目的就是在於驗證序列化的物件和對應的類是否是版本一致的。

程式碼演示:

// 序列化操作類
public class Demo01ObjectOutputStream {
    public static void main(String[] args) throws IOException {
        //1.建立ObjectOutputStream流物件,構造方法中傳遞指定的位元組輸出流。
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day30_IO\\student.txt"));
        //2.使用ObjectOutputStream物件中的方法writeObject,把物件寫入到檔案中。
        //2.1 先建立一個物件
        Student s = new Student("小孫", 30);
        s.score = 60;
        //2.2寫物件到檔案中
        oos.writeObject(s);
        //3.釋放資源。
        oos.close();
    }
}
// 反序列化類操作
public class Demo02ObjectInputStream {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 1. 建立一個ObjectInputStream流物件,構造方法中傳遞一個位元組輸入流物件
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day30_IO\\student.txt"));
        // 2. 使用ObjectInputStream流物件中的方法readObject,讀取儲存在檔案中的物件資料
        Object obj = ois.readObject();
        // 3.釋放資源。
        ois.close();
        // 4. 檢視物件的資料
        System.out.println(obj);// Student{name='小孫', age=30}
        if ( obj instanceof Student) {
            Student student = (Student)obj;
            System.out.println(student.getAge() + "--" + student.getName());
        } else {
            System.out.println("轉換失敗");
        }
    }
}
// 需要被序列化的類
import java.io.Serializable;
public class Student implements Serializable {
    // 可以選擇手動自定義一個序列化版本號
    private static final long serialVersionUID = 1L;
    //private static String name;
    private String name;
    private Integer age;
    private transient String address = "鄭州市";
    transient int score;// 0
}

原理分析:

練習:儲存一堆物件,實現序列化和反序列化動作。
import java.io.Serializable;

public class Student implements Serializable {
    // 可以選擇手動自定義一個序列化版本號
    private static final long serialVersionUID = 1L;
    //private static String name;
    private String name;
    private Integer age;
    private transient String address = "鄭州市";
    transient int score;// 0
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public Student() {
    }
    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
        address = "鄭州市";
    }
   @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                ", score=" + score +
                '}';
    }
}


@SuppressWarnings("unchecked")
public class DemoTestSerialize {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //1.定義多個物件,儲存在集合中
        ArrayList<Student> list = new ArrayList<>();
        list.add(new Student("小孫",30));
        list.add(new Student("小王",20));
        list.add(new Student("小趙",40));
        list.add(new Student("小劉",10));
        list.add(new Student("小麗",25));
        //2.使用序列化技術,把該集合物件寫入到檔案中
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day30_IO\\student.txt"));
        //3.寫入進去,呼叫writeObject
        oos.writeObject(list);
        //4.使用反序列化技術,把檔案中儲存的集合物件讀取出來
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day3_IO\\student.txt"));
        //5.讀取物件資料,呼叫readObject()方法
        Object obj = ois.readObject();
        //6.向下轉型
        if (obj instanceof ArrayList){
            ArrayList<Student> students = (ArrayList<Student>)obj;
            //7.遍歷該集合物件
            for (Object student:students){
                System.out.println(student);
            }
        }
    }
}

列印流

java.io.PrintStream類能夠很方便列印各種資料型別的值。

構造方法
  • public PrintStream(String filename):使用指定的檔名建立一個新的列印流物件。
改變列印流的方向

正常System.out就是PrintStream型別的,資料的流動的位置在控制檯中。改變資料的流動位置。通過System.setOut(PrintStream print)來改變流向。

 PrintStream out = System.out;
 out.print(123);// 在控制檯中
 // 構造方法建立一個列印流物件
 PrintStream printStream = new PrintStream("day30_IO\\print.txt");
 // 改變列印流的方向為"day30_IO\\print.txt"路徑
 System.setOut(printStream);
 System.out.println("我已經改變了輸出資料的位置");
 System.out.println("我想在控制檯中輸出資料");
 System.out.println("啦啦啦啦啦"); 

相關文章