シリアル化


シリアル化


オブジェクトをコンピュータに格納してから取り出して書き込むか、ネットワークを介してコンピュータ間でオブジェクトを交換できますか?可能にしたのはシリアル化です.

シリアル化とは?


シリアル化とは、オブジェクトをデータストリームにすることです.データを連続データに変換してストリームに書き込みます.逆に、ストリームからデータを読み出すことでオブジェクトを作成することを逆シーケンス化と呼びます.
オブジェクトを格納または格納するには、もちろんそうするしかありません.オブジェクトは、クラスで定義されたインスタンス変数の集合です.オブジェクトにはクラス変数またはメソッドは含まれず、インスタンス変数のみから構成されます.各インスタンス変数には異なる値が必要であるため、追加のメモリ領域が必要ですが、メソッドは変化するのではなく、メモリを浪費し、各インスタンスに同じ内容のコードを含める理由はありません.
したがって、ストレージオブジェクトは、ストレージオブジェクトのすべてのインスタンス変数の値に等しい.インスタンス変数が基本変数の場合、これは簡単ですが、参照変数の場合は簡単ではありません.インスタンス変数のタイプが配列である場合、配列に格納されている値もすべて保存する必要があります.しかし、私たちは悩む必要はありません.ObjectInpuStreamとObjectOutputStreamを使用してオブジェクトをシーケンス化する方法を理解するだけです.

ObjectInputStreamとObjectOutputStream


シリアル化はObjectOutputStreamを使用し、逆シリアル化はObjctInputStreamを使用します.OutputStreamとInputStreamはそれぞれ継承されますが、ベースストリームの補助Streamが必要です.したがって、オブジェクトを作成するときは、出力を入力するストリームを指定する必要があります.
ObjectInputStream(InputStream in)
ObjectOutputStream(OutputStream out)

シリアルクラスの作成(Serializable,transient)


シリアル化可能なクラスを作成する方法は、シリアル化したいクラスにシリアル化インタフェースを実現させることです.
public class UserInfo implements java.io.Serializable{
    String name;
    String password;
    int age;
}
シリアルインタフェースは、何も定義されていない空のインタフェースであるが、シリアル化のために作成されたクラス판단하는 기준である.
また、祖先クラスがシリアル化されて継承されている場合、サブクラスもシリアル化できます.
public class SuperUserInfo implements Serializable{
    String name;
    String password;
}
public class UserInfo extends SuperUserInfo{
    int age;
}
ただし、上記の祖先クラスSuperUserInfoクラスがシリアル化されておらず、サブクラスUserInfoでのみシリアル化されている場合、祖先クラスで定義されたnameとpasswordはシリアル化ターゲットから除外されます.
祖先クラスで定義されたインスタンス変数nameとpasswordをシリアル化ターゲットに含めるには、祖先クラスを処理してシリアル化またはUserInfoを実現し、祖先のインスタンス変数をシリアル化するコードを直接追加する必要があります.
public class UserInfo implements Serializable{
    String name;
    String password;
    int age;
    
    Object obj = new Object();	//Object 객체는 직렬화할 수 없다.
}
すべてのクラスの最高の祖先として、Objectはシリアル化されていないため、シリアル化できません.インスタンス変数objのタイプはシリアル化不可能なオブジェクトであるが、実際に格納されているオブジェクトがシリアル化可能なデフォルトタイプであればシリアル化可能である.
public class UserInfo implements Serializable{
    String name;
    String password;
    int age;
    
    Object obj = new String("1234");	//변수 타입은 Object지만 실제 저장되는 객체는 String
}
インスタンス変数のタイプではなく、実際に接続されているオブジェクトのタイプによってシリアル化できるかどうかを覚えておいてください.

transient


シーケンス化するオブジェクトのクラスにシーケンス化できないオブジェクトへの参照が含まれている場合、またはpasswordなどのセキュリティ上のシーケンス化できない値については、シーケンス化ターゲットから除外する制御子transientを追加できます.
public class UserInfo implements Serializable{
    String name;
    transient String password;
    int age;
}
クラスをシリアル化し、コンテンツを確認するために逆シリアル化すると、passwordの値はnullに逆シリアル化されます.

実際のシリアルと逆シリアルコードの作成


まずはUserInfo.Javaファイルの作成
public class UserInfo implements Serializable {
    String name;
    String password;
    int age;

    public UserInfo(String name, String password, int age) {
        this.name = name;
        this.password = password;
        this.age = age;
    }

    @Override
    public String toString() {
        return "UserInfo{" +
                "name='" + name + '\'' +
                ", password='" + password + '\'' +
                ", age=" + age +
                '}';
    }
}
次に、実際にシリアル化を実行するコードを作成します.
public static void main(String[] args){
    try {
        String fileName = "UserInfo.ser";
        FileOutputStream fos = new FileOutputStream(fileName);
        //보조 스트림 생성
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        //직렬화 스트림 생성
        ObjectOutputStream out = new ObjectOutputStream(bos);
        
        UserInfo u1 = new UserInfo("man", "1234", 28);
        UserInfo u2 = new UserInfo("woman", "4321", 25);

        ArrayList<UserInfo> list = new ArrayList<>();
        list.add(u1);
        list.add(u2);
        
        out.writeObject(u1);
        out.writeObject(u2);
        out.writeObject(list);
        //직렬화 스트림의 close 호출로 상위 스트림들 모두 flush 및 close
        out.close();
        System.out.println("직렬화 종료");
    }catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
実行結果

実際に正常に動作し、ファイルを生成します.
次に、逆シーケンス化によってファイルを再び読み込みます.
public static void main(String[] args){
    try {
        String fileName = "UserInfo.ser";
        FileInputStream fis = new FileInputStream(fileName);
        //보조 스트림 생성
        BufferedInputStream bis = new BufferedInputStream(fis);
        //역직렬화 스트림 생성
        ObjectInputStream in = new ObjectInputStream(bis);

        //객체를 읽을 때는 출력한 순서와 일치하게 읽어야한다!
        UserInfo u1 = (UserInfo) in.readObject();
        UserInfo u2 = (UserInfo) in.readObject();
        ArrayList list = (ArrayList) in.readObject();

        System.out.println("u1 = " + u1);
        System.out.println("u2 = " + u2);
        System.out.println("list = " + list);
        in.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}
実行結果

toString()で定義された形式で出力され、すべてのオブジェクトが読み込まれていることを確認します.

シーケンス化されていない親サブクラスでシーケンス化

class SuperUserInfo{
    String name;
    String password;

    public SuperUserInfo(String name, String password) {
        this.name = name;
        this.password = password;
    }
}
public class UserInfo extends SuperUserInfo implements Serializable {
    int age;

    public UserInfo(String name, String password) {
        super(name, password);
    }

    public UserInfo(String name, String password, int age) {
        super(name, password);
        this.age = age;
    }

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

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.writeUTF(name);
        out.writeUTF(password);
        out.defaultWriteObject();
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{
        name = in.readUTF();
        password = in.readUTF();
        in.defaultReadObject();
    }
}
祖先クラスでシリアル化されていない場合は、以下に示すように、子クラスにwriteObject()およびreadObject()を直接追加できます.これらのメソッドは、シリアル化および逆シリアル化操作時に自動的に呼び出されます.また、両方のメソッドのアクセス制御者がprivateであることはおかしいかもしれませんが、これは特定のルールであり、規定のルールに従えばよいだけです.

シリアルクラスのバージョンの管理


オブジェクトをシーケンス化する場合は、シーケンス化時と同じクラスを使用します.ただし、クラス名が同じでコンテンツが変更された場合、逆シーケンス化は失敗し、InvalidClassExceptionが生成されます.
オブジェクトがシーケンス化されると、クラスで定義されたメンバーの情報を使用して、SerialVersionUIDというクラスバージョンが自動的に生成され、シーケンス化されたコンテンツに含まれます.逆シーケンス化時にクラスバージョンを比較することで、シーケンス化時にクラスのバージョンと一致するかどうかを確認します.したがって,コンテンツが変化するとバージョンが変化し,異常を引き起こす.ただし、静的変数、定数、または過渡項を持つインスタンス変数は、変更後もシリアル化に影響しないため、相関はありません.
シーケンス化されたオブジェクトをネットワークに送信する場合、送信側と受信側は同じバージョンのクラスを持つ必要がありますが、クラスの内容が少し変更されている限り、クラスを再配置するのは難しいです.この場合、クラスのバージョンを手動で管理する必要があります.
class Test implements Serializable{
    static final long serialVersionUIID = 3125661234231231555L;	//정수값이면 어떤 값이든 가능
}
SerialVersionUIDを次のように定義すると、クラスの内容が変更されても自動的にクラスバージョンが生成されず、固定値として指定されます.整数値の場合は任意の値を入力できますが、通常javaではserialverです.通常exeで生成される値が使用されます.