シリアライズ(直列化)とは?
「Serializableを実装してね。」
「シリアライズすると保存できます。」
Javaを書いているとよく見るが
そもそもシリアライズとは何なのかを整理してみる。
シリアライズとは?
シリアライズ(Serialize)は、
オブジェクトの状態を保存・送信できる形に変換すること。
逆に、保存されたデータから
オブジェクトを復元することをデシリアライズという。
Javaでは以下のようにSerializableを実装すると
シリアライズ可能になる。
import java.io.Serializable;
public class User implements Serializable {
private String name;
public User(String name) {
this.name = name;
}
}なぜシリアライズが必要?
シリアライズとは?みたいな形で調べると、よく上記のような説明を受けるが
保存するとき・送信するときもバイナリになるんじゃないの?
と疑問だったので深掘ってみた。
Javaのオブジェクトは
基本的にメモリ上にしか存在しない。
User user = new User("Taro");このuserはプログラム終了時に消える。
また、そのままでは別PCに送信もできない。
なぜなら、オブジェクトは単なる「メモリ上の構造」(アドレス、ポインタ等)であり
そのままでは保存や通信に適した形ではないため。
そのため、
- ファイルに保存できる形
- 通信できる形
に変換する必要がある。
それがシリアライズ。
オブジェクトってバイナリじゃないの?
ここが混乱しやすいポイントだった。
確かにオブジェクトはメモリ上ではバイナリとして存在している。
ただし、そのまま 保存・送信 はできない。
理由としては以下。
メモリ上のデータはそのまま使えない
例えば以下のオブジェクト。
User user = new User("Taro");メモリ上では、
- クラス情報
- メモリアドレス
- JVM内部情報
- 参照情報
なども含めた形で保持されている。
つまり、
このメモリの0x123456にnameがあるのような状態。
しかし、
- 別PC
- 次回起動時
- 別JVM
ではメモリアドレスが変わる。
つまり、メモリ上の状態をそのままコピーしても
復元できない。
そのため、
User
└ name = "Taro"のように
「オブジェクトの状態だけ」を取り出して
保存可能な形式へ変換する必要がある。
これがシリアライズ。
シリアライズの用途
よくある用途は以下。
1. ファイル保存
オブジェクトの状態をファイルへ保存できる。
個人的にイメージしやすかったのはゲームのセーブデータ。
Player
├ HP = 100
├ Level = 10
└ Items = [...]を保存しておけば、
次回起動時に復元できる。
2. 通信
オブジェクトを別コンピュータへ送信できる。
例えばAPI通信やSocket通信。
Client → Userオブジェクト送信 → Serverのようなことを行う際、
通信可能なデータ形式へ変換する必要がある。
3. キャッシュ
ここは私的にはイメージがなかった。
Redisなどへオブジェクトを保存する際にも使われる模様。
メモリ上のJavaオブジェクトは
そのままRedisへ置けないためシリアライズ化して保存するようになっている。
Javaではどう保存される?
Java標準のシリアライズでは
バイナリ形式で保存される。
ObjectOutputStream out =
new ObjectOutputStream(
new FileOutputStream("sample.dat"));
out.writeObject(user);保存された.datファイルを開くと
読めないバイナリデータになっている。
JSONとの違い
ファイルで保存と言えば、JSONに変換もするような…
と思ったので調べてみた。
最近はJava標準シリアライズより
JSON変換のほうがよく使われる模様。
例えば以下。
{
"name": "Taro"
}JSONも広い意味では
「オブジェクトを保存・通信可能な形へ変換している」ため
シリアライズの一種とのこと。
ただし、
Java標準シリアライズ
→ Java専用のバイナリ形式JSON
→ 人間が読めるテキスト形式
という違いがある。
注意点
Java標準シリアライズは便利だが
以下の問題もある。
- クラス変更に弱い
- Java依存
- セキュリティ事故の原因になりやすい
そのため、最近は
- JSON
- Protocol Buffers
- MessagePack
などを使うことも多い模様。
まとめ
シリアライズとは
オブジェクトを保存・通信可能な形式へ変換すること。
オブジェクトはメモリ上ではバイナリだが、
そのままでは
- メモリアドレス
- JVM依存情報
などを含むため
保存や通信には使えない。
そのため、
「オブジェクトの状態のみ」を取り出して
保存できる形式へ変換する必要がある。
ゲームのセーブデータや
ネットワーク通信などで利用される重要な仕組み。