[Java]serialVersionUIDのテスト

Pocket

 serialVersionUIDについて色々とテストしてみた。


スポンサードリンク


はじめに分かったことを・・・

 永続化オブジェクトの互換性チェック(readするタイミングに行われる)で例外が投げられたのは・・・

  • serialVersionUIDを変更した場合
  • →java.io.InvalidClassException: Data; local class incompatible: stream classdesc serialVersionUID = -1826836446123557654, local class serialVersionUID = 1

  • インスタンス変数のデータ型(getter/setterの引数/戻り値も)を変更した場合
  • →java.io.InvalidClassException: Data; incompatible types for field b
 変数名やメソッド名を変えたくらいじゃ正常に動作してしまった。
 今回のケースだとデータ型を変更というあまり行わないような改修でしたが、ドキュメントにも「予期しない InvalidClassException が発生する可能性があります。」って書いてあるし・・・。永続化対象としているオブジェクトのクラスに変更を加えたら、serialVersionUIDもいちいち変更するようにしないと忘れた頃の改修時に思わぬところでハマりそうです。


 救いは出力された例外にエラー内容が記載されていることでしょうか。

テストコード

※各テストで変更した場所は、別のテスト時には元に戻しながら実行しました。
 JUnit使えばよかったと思いましたが、テスト結果が想定できない場合でも使うものなんでしょうか?
  1. Data.java
  2. public class Data implements Serializable{
    
    	// implements Serializableを記載するとWarrningが出てくるので
    	// Ctrl + 1を押下し「生成シリアル・バージョンIDの追加」を選択すると
    	// 勝手に番号が振られる
    	private static final long serialVersionUID = -1826836446123557654L;
    
    	private String a;
    
    	private int b;
    
    	public String getA() {
    		return a;
    	}
    
    	public void setA(String a) {
    		this.a = a;
    	}
    
    	public int getB() {
    		return b;
    	}
    
    	public void setB(int b) {
    		this.b = b;
    	}
    }
    
  3. Write.java
  4. import java.io.FileOutputStream;
    import java.io.ObjectOutputStream;
    
    public class Write {
    	public static void main(String[] args){
    		try{
    			new Write().execute();
    		}catch(Exception ex){
    			ex.printStackTrace();
    		}
    	}
    
    	private void execute() throws Exception{
    		String file = "./test.dat";
    
    		Data data = new Data();
    		data.setA("aaa");
    		data.setB(111);
    
    
    		ObjectOutputStream oos = 
    			new ObjectOutputStream(new FileOutputStream(file));
    		oos.writeObject(data);
    
    		oos.close();
    	}
    
    }
    
  5. Read.java
  6. import java.io.FileInputStream;
    import java.io.ObjectInputStream;
    
    
    public class Read {
    	public static void main(String[] args) {
    		try {
    			new Read().execute();
    		} catch (Exception ex) {
    			ex.printStackTrace();
    		}
    	}
    
    	private void execute() throws Exception {
    		String file = "./test.dat";
    
    		ObjectInputStream ois = 
    			new ObjectInputStream(new FileInputStream(file));
    
    		Data data = (Data)ois.readObject();
    
    		System.out.println(data.getA());
    		System.out.println(data.getB());
    
    		ois.close();
    	}
    
    }
    

    テスト:正常系

     上記のテストコードをWrite→Readの順に実行した結果。
    aaa
    111
    
     正常に書き込み/読み込みできた。


    テスト:serialVersionUIDはそのままでクラスに変更を加えた場合

     上記のテストコードのWriteを実行し、Data.javaの変数bの型をStringに変更。
    	private String b;
    
    	public String getB() {
    		return b;
    	}
    
    	public void setB(String b) {
    		this.b = b;
    	}
    
     上記の変更によってコンパイルエラーとなるRead.javaとWrite.javaの箇所を編集(割愛)してReadを実行。
    java.io.InvalidClassException: Data; incompatible types for field b
    	at java.io.ObjectStreamClass.matchFields(ObjectStreamClass.java:2205)
    	at java.io.ObjectStreamClass.getReflector(ObjectStreamClass.java:2100)
    	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:600)
    	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1582)
    	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1495)
    	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1731)
    	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
    	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
    	at Read.execute(Read.java:19)
    	at Read.main(Read.java:8)
    
     InvalidClassExceptionが出た。
     その他、以下のケースを試してみました。

    • データ型は変更せずに変数名とgetter/setterメソッド名を変更
    •  →正常にaとbの値が取得できた

    • データ型は変更せずに変数bとgetter/setterメソッドを削除
    •  →正常にaの値が取得できた(当たり前ですが変数bは取得しません)

    • 新しい変数cとgetter/setterメソッドを追加
    •  →正常にaとbの値が取得できた(cの存在は考慮してない?)


    テスト:java.io.Serializableインタフェースを実装しなかった場合

     Data.javaのimplements SerializableをコメントアウトしてWrite→Readの順に実行した結果。
    java.io.NotSerializableException: Data
    	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1164)
    	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:330)
    	at Write.execute(Write.java:22)
    	at Write.main(Write.java:7)
    
     NotSerializableExceptionが出た。


    テスト:serialVersionUIDを変更した場合

     上記のテストコードのWriteを実行し、Data.javaのserialVersionUIDを適当に変更してReadを実行。
    java.io.InvalidClassException: Data; local class incompatible: stream classdesc serialVersionUID = -1826836446123557654, local class serialVersionUID = 1
    	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:560)
    	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1582)
    	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1495)
    	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1731)
    	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
    	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
    	at Read.execute(Read.java:19)
    	at Read.main(Read.java:8)
    
     InvalidClassExceptionが出た。


 終わり。


お役に立てましたか?

ブックマークをどうぞ!

スポンサード リンク

コメントを残す