Javaのシリアライズ(直列化)メモ
今更だが、Javaのインスタンスをファイルなどに保存するのに便利なシリアライズ(直列化)についてのメモ。
シリアライズとデシリアライズ
シリアライズ(直列化)
デシリアライズ
何が嬉しいのか
この機能を使うと、簡単にインスタンスを外部記憶装置などに保存し、インスタンスの情報を永続化することができる。
何か特別な処理をしたり、ある形式に沿ってインスタンスを保存したりするような面倒なことを省くことができる。
java.io.Serializableインターフェース
シリアライズを行いたいインスタンスの元となるクラスでは、java.io.Serializableインタフェースを実装する必要がある。
この、Serializableインタフェースには、フィールド及びメソッドが存在せず、ただただ、シリアライズ(直列化)可能ですよということを示すことのみを提供する。
参考 : Serializable (Java Platform SE 8 )
ObjectOutputStreamクラスとObjectInputStreamクラス
ObjectOutputStreamクラス
ObjectOutputStream は、基本データ型と Java オブジェクトのグラフを OutputStream に書き込みます。
引用元 : ObjectOutputStream (Java Platform SE 6)
=> 出力用のStreamに基本データ型とオブジェクトのグラフを書き込む
ObjectIntputStreamクラス
ObjectOutputStreamを使って作成されたプリミティブ・データとプリミティブ・オブジェクトを直列化復元します
引用元 : ObjectInputStream (Java Platform SE 8 )
=> ObjectOutputStreamを使って書き込んだものを復元する
serialVersionUID
あるクラスをシリアライズ化して、ファイルとして永続をした後、そのクラスのフィルールドなど変更したりした時に、変更後のJavaのクラスとシリアライズされたバイト列から再度でシリアライズされた際に復元されるインスタンスは、矛盾が生じる可能性がある。
そうならないために、このserialVersionUIDにバージョンとして値を与えて、コードに変更があった時には、この値を毎回変えるというふうにする。
そうすることで、復元元と復元先のこのserialVersionUIDの値が一緒だったら、同じ世代のインスタンスなので、矛盾が生じることなく、復元を行うことができると保証されるわけである。
よく、何かのシステムを作ったりする時や、プログラミング言語でもバージョニングということを行うことがあると思うが、それに近い。
例えば、Java1.8ならStream APIは存在するけど、それ未満だと存在しないよみたいな感じで。
参考 : 難解なSerializableという仕様について俺が知っていること、というか俺の理解 - 都元ダイスケ IT-PRESS
参考 : 中山 清喬(2014/9/22)『スッキリわかるJava入門 実践編 第2版 スッキリわかるシリーズ』 インプレスジャパン
実装してみた
全体の流れ
- Languageクラスをインスタンス科する
- ファイル名を元にFileOutputStreamクラスのインスタンスを生成する
- FileOutputStreamクラスを元に、ObjectOutputStreamクラスを生成する
- ObjectOutputStream.writeObjectでファイルに書き込みを行う
- ファイル名を元にFileInputStreamクラスのインスタンスを生成する
- FileInputStreamクラスを元に、ObjectInputStreamクラスを生成する
- ObjectInputStream.readObjectでインスタンスを復元する
図にするとこんな感じ
コード
Main.java
package com.company; import java.io.*; /** * メインクラス */ public class Main { /** * メインメソッド * * @param args コマンドライン引数 */ public static void main(String args[]) { String fileName = "lang.txt"; // Languageクラスをインスタンス化 Language language = new Language("Java", "Static"); try { Main.Serialize(language, fileName); } catch (IOException e) { e.printStackTrace(); } try { Language deSirializedLanguage = Main.DeSerialize(fileName); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } System.out.println("デシリアライズされた、Languageの名前は" + language.getName() + "タイプは" + language.getType()); } /** * オブジェクトのシリアライズを行う * * @param language Languageクラスのインスタンス * @param fileName ファイル名 * @throws IOException IO例外 */ private static void Serialize(Language language, String fileName) throws IOException { // わかりやすくするために冗長に書いている // ファイルを指定して、FileOutputStreamを生成 FileOutputStream fileOutputStream = new FileOutputStream(fileName); // OutputStreamに基本データ型とオブジェクトのグラフをOutputStreamに書き込む // 今回は、FileOutputStreamに書き込む ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); // 特定のクラスのオブジェクトの状態をストリームに書き込む objectOutputStream.writeObject(language); objectOutputStream.flush(); objectOutputStream.close(); } /** * オブジェクトのデシリアライズを行う * * @return Languageクラスのインスタンス */ private static Language DeSerialize(String fileName) throws IOException, ClassNotFoundException { // わかりやすくするために冗長に書いている // ファイルを指定して、FileInputStreamを生成 FileInputStream fileInputStream = new FileInputStream(fileName); // 事前にObjectOutputStreamを使って作成されたプリミティブ・データとプリミティブ・オブジェクトを直列化復元する ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); // 対応するwriteObjectメソッドによってストリームに書き込まれたデータを使用する特定のクラスのオブジェクトの状態を読み込み、復元する Language language = (Language) objectInputStream.readObject(); objectInputStream.close(); return language; } }
Language.java
package com.company; import java.io.Serializable; /** * プログラミング言語を表す * Serializableインターフェースを実装する */ public class Language implements Serializable { /** * シリアルバージョンUID */ private static final long serialVersionUID = 1L; /** * 言語の名前 */ private String name; /** * 言語のタイプ */ private String type; Language(String name, String type) { this.name = name; this.type = type; } /** * 名前 * * @return nam 言語の名前 */ public String getName() { return name; } /** * typeのゲッター * * @return type 言語のタイプ */ public String getType() { return type; } }
実行結果
デシリアライズされた、Languageの名前はJavaタイプはStatic
参考文献
中山 清喬(2014/9/22)『スッキリわかるJava入門 実践編 第2版 スッキリわかるシリーズ』 インプレスジャパン
Serializable (Java Platform SE 8 )
ObjectOutputStream (Java Platform SE 6)
ObjectInputStream (Java Platform SE 8 )
難解なSerializableという仕様について俺が知っていること、というか俺の理解 - 都元ダイスケ IT-PRESS
【Java】Serializableの基本(シリアライズ・直列化) - TASK NOTES
オブジェクトのシリアライズとデシリアライズ - Java 入門
【Java】Serializableの基本(シリアライズ・直列化) - TASK NOTES
※ Qiitaでも同一の投稿を行っている
qiita.com