[Java] シリアライズ(直列化: Serializable)
こんにちは。明月です。
この投稿はJavaのシリアライズ(直列化: Serializable)に関する説明です。
シリアライズとは割当てしたクラス(インスタンス)をバイナリ化することをシリアライズ(直列化: Serializable)といいます。
クラスのメモリ割当てはクラス内部のメンバー変数で構成しています。(関数の場合はメモリに割当てないです。)メンバー変数はプリミティブタイプやクラスになっています。
すなわち、クラスのデータをファイルやソケット通信で転送するためにはByteタイプに変換しなければならないです。
その方法でJsonタイプに変換してクラスのデータをStringタイプに変換してバイナリ化する方法もあります。
実際に、最近、その方法でよく使います。でも、クラスをJsonタイプに変換するのは限界があるし、クラス内部の見えないデータ(private)まで完全に変換されたとは言えません。
それでこのシリアライズ機能を使ってクラスをバイナリ化するとクラスをデータを完全にbyteタイプに変換するのでよいでしょう。
でも、シリアライズよりJsonをよく使うのは下記とおりの理由があります。
1.シリアライズでバイナリ化になったデータは人間がそのままに見て理解しにくい構造になっています。つまり、逆シリアライズする前にはデータが正しいかの検証ができません。
でも、Jsonタイプになったデータは人間が見やすいし、プログラムを利用せずに修正も簡単です。逆にそれがJsonよりいいことがセキュリティにはよいでしょう。
2.プラットフォームの制約があります。Java言語でシリアライズしたデータはC#やPythonなどで逆シリアライズができません。
正確には出来ないことではないですが、データ漏れが発生します。
3.クラス修正があれば、以前シリアライズしたデータを逆シリアライズする時にデータ漏れが発生する可能性があります。
シリアライズの利点はセキュリティがよいし、クラス自体をバイナリするため、クラス状態をログにして把握することでよいでしょう。
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
// クラスをシリアルするためにSerializableのインターフェースを継承しなければならない。
class Node implements Serializable {
// Serializableインターフェースを継承するとserialVersionUIDを設定しなければならない。
private static final long serialVersionUID = 1L;
// Nodeクラスのメンバー変数
private String data1;
private int data2;
// プロパティ
public void setData1(String data1) {
this.data1 = data1;
}
public void setData2(int data2) {
this.data2 = data2;
}
public void print() {
System.out.println("data1 = " + data1 + " data2 = " + data2);
}
}
public class SerializableTest {
// 実行関数
public static void main(String[] args) {
// Nodeクラスを割当て
Node node = new Node();
// data1に「Hello world」のデータを格納
node.setData1("Hello world");
// data2に「10」のデータを格納
node.setData2(10);
// ファイルを設定
File file = new File("d:/work/test.dat");
// ファイルが存在すれば削除する。
if(file.exists()) {
file.delete();
}
// シリアライズするために、ストリームを取得する。
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
// nodeインスタンスをシリアライズする。
oos.writeObject(node);
// Nodeクラスをシリアライズしたデータをバイナリタイプ(byte[])に変換する。
byte[] data = baos.toByteArray();
// バイナリ化(byte[])されたデータをファイルに格納する。
try (FileOutputStream stream = new FileOutputStream(file)) {
stream.write(data, 0, data.length);
}
}
} catch (Throwable e) {
e.printStackTrace();
}
// 逆シリアライズ
try (FileInputStream stream = new FileInputStream(file)) {
// ファイルからデータサイズを取得する。
byte[] data = new byte[(int)file.length()];
// ファイルからバイナリ(byte[])を取得する。
stream.read(data, 0, data.length);
// シリアライズするために、ストリームを取得する。
try (ByteArrayInputStream bais = new ByteArrayInputStream(data)) {
try (ObjectInputStream ois = new ObjectInputStream(bais)) {
// バイナリ(byte[])データをObjectタイプに変換する。
Object objectMember = ois.readObject();
// ObjectデータをNodeクラスにキャストする。
Node node1 = (Node) objectMember;
// Nodeクラスのprint関数を呼出す。
node.print();
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
}
上の例は「Node」クラスを割当てしてバイナリ化(byte[])にしてファイルに格納しました。
格納したデータをまた読み込んでクラスに変換しました。Jsonで一々にデータをStringタイプに変換することよりシリアライズ変換が簡単です。
上をみればシリアライズされたデータは構造把握が難しいです。
JavaでシリアライズされたことをC#で逆シリアライズしてみます。
using System;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
namespace Example
{
//シリアライズクラス
//javaで作成したNodeクラスと同じ構造で作成する。
[Serializable]
class Node
{
//データタイプと変数名まで一致する。
private string data1;
private int data2;
public void Print()
{
Console.WriteLine($"data1 = {data1}, data2={data2}");
}
}
class Program
{
static void Main(string[] args)
{
// シリアライズフォーマッターを割当する。
var formatter = new BinaryFormatter();
// Javaで生成したシリアライズファイルを読み込む
using (var stream = new FileStream(@"d:\work\test.dat", FileMode.Open, FileAccess.Read))
{
// Nodeクラスに変換する。
var node = (Node)formatter.Deserialize(stream);
// Print関数を呼出す。
node.Print();
}
Console.WriteLine("Press any key...");
Console.ReadKey();
}
}
}
クラス構造を「string data1」と「int data2」を作りましたが、データタイプが違うというエラーが発生します。
ここまでJavaのシリアライズ(直列化: Serializable)に関する説明でした。
ご不明なところや間違いところがあればコメントしてください。
- [Java] PDFを出力する方法(itextpdf)2020/03/13 00:47:31
- [Java] ログライブラリ(log4j)を使う方法2020/03/12 00:54:39
- [Java] Jsonタイプのデータを使う方法(Gsonライブラリ)2020/03/11 00:30:15
- [Java] Base64をエンコード、デコードする方法2020/03/09 10:24:01
- [Java] cmdコマンドを実行するための方法2020/03/06 18:01:10
- [Java] メール(javax.mail)を発送する方法2020/03/05 20:07:49
- [Java] クラス複製(Clonable, Reflection)2020/03/05 00:03:19
- [Java] シリアライズ(直列化: Serializable)2020/03/03 00:03:33
- [Java] StringBuilderとStringBufferの差異2020/03/02 07:52:22
- [Java] Compare関数を使う方法2020/02/29 03:00:00
- [Java] 数字フォーマット(お金表示及び小数点以下表示)2020/02/28 03:00:00
- [Java] サーブレット環境で現在の実行ディレクトリを取得する方法2020/02/27 03:00:00
- [Java] 日本語をユニコードに変換して、ユニコードから日本語に変換する方法2020/02/26 03:00:00
- [Java] コンソールからデータを受け取る方法(System.in)2020/02/25 03:00:00
- [Java] Servlet環境でWebSocket通信中、HttpSessionを取得する方法2020/02/24 07:47:20
- check2024/04/10 19:03:53
- [Java] 64.Spring bootとReactを連結する方法(Buildする方法)2022/03/25 21:02:18
- [Javascript] Node.jsをインストールしてReactを使う方法2022/03/23 18:01:34
- [Java] 63. Spring bootでcronスケジューラとComponentアノテーション2022/03/16 18:57:30
- [Java] 62. Spring bootでWeb-Filterを設定する方法(Spring Security)2022/03/15 22:16:37
- [Java] JWT(Json Web Token)を発行、確認する方法2022/03/14 19:12:58
- [Java] 61. Spring bootでRedisデータベースを利用してセッションクラスタリング設定する方法2022/03/01 18:20:52
- [Java] 60. Spring bootでApacheの連結とロードバランシングを設定する方法2022/02/28 18:45:48
- [Java] 59. Spring bootのJPAでEntityManagerを使い方2022/02/25 18:27:48
- [Java] 58. EclipseでSpring bootのJPAを設定する方法2022/02/23 18:11:10
- [Java] 57. EclipseでSpring bootを設定する方法2022/02/22 19:04:49
- [Python] Redisデータベースに接続して使い方2022/02/21 18:23:49
- [Java] Redisデータベースを接続して使い方(Jedisライブラリ)2022/02/16 18:13:17
- [C#] Redisのデータベースを接続して使い方2022/02/15 18:46:09
- [CentOS] Redisデータベースをインストールする方法とコマンドを使い方2022/02/14 18:33:07