การทำงานของตัวแปร serialVersionUID

ในการบันทึกสถานะของ Object ลง Storage หรือส่งผ่านทาง Network สิ่งที่ต้องทำคือ ให้คลาสนั้นทำการ implements Serializable อันนี้รู้กัน และสิ่งที่ควรจะทำคือ ประกาศตัวแปรที่ชื่อว่า serialVersionUID เป็นตัวแปรประเภท long คำถามคือ ทำไมต้องมีตัวแปรนี้ และจะกำหนดค่าอะไร ยังไง ลองมาดูตัวอย่างที่ผมจะนำเสนอต่อไปนี้ น่าจะช่วยไขข้อข้องใจได้

เหตุการณ์สมมุติของผมคือ ผมต้องสร้างระบบ save ข้อมูลตัวละครของเกมส์ลงบน harddisk เพื่อให้สามารถปิดเครื่องแล้วกลับมาเล่นต่อโดยข้อมูลนั้นยังอยู่เหมือนตอนก่อนหน้านี้

  1. ผมสร้างคลาส Character ขึ้นมา หน้าตาดังนี้
    package test;
    
    import java.io.Serializable;
    
    public class Character implements Serializable{
    
    	private static final long serialVersionUID = 1L;
    	public String name;
    	public int score;
    }
    
  2. สร้างคลาสเพื่อใช้ save เกมส์และ load เกมส์ดังนี้
    package test;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    
    public class Main {
    
    	public static void main(String[] args) {
    		Character character = new Character();
    		character.name = "magicalcyber";
    		character.score = 108;
    //		saveCharacter(character);
    //		loadCharacter();
    	}
    
    	public static void saveCharacter(Character character) {
    		try {
    			System.out.println("Saving...");
    			System.out.println("name: " + character.name + "\tScore: " + character.score);
    			ObjectOutputStream oos = new ObjectOutputStream(
    					new FileOutputStream("c:/save.dat"));
    			oos.writeObject(character);
    		} catch (FileNotFoundException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    
    	public static void loadCharacter() {
    		try {
    			ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
    					"c:/save.dat"));
    			Character character = (Character) ois.readObject();
    			System.out.println("Loading...");
    			System.out.println("name: " + character.name + "\tScore: " + character.score);
    		} catch (FileNotFoundException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		} catch (ClassNotFoundException e) {
    			e.printStackTrace();
    		}
    
    	}
    }
    
    

    โดยในเมธอด main ผมได้เตรียมข้อมูลตัวละคร และเตรียมเรียกเมธอด save และ load ไว้แล้ว

  3. เปิด comment ตอนเรียกเมธอด saveCharacter และ loadCharacter เพื่อทำการทดสอบ save ข้อมูลแล้วโหลดข้อมูลขึ้นมา
    แล้วรันทดสอบ ผลลัพท์ที่ได้ควรจะเป็นดังนี้

    Saving…
    name: magicalcyber    Score: 108
    Loading…
    name: magicalcyber    Score: 108

    save ได้ load ได้ไม่มีปัญหา

  4. คราวนี้ลองเปลี่ยนค่าของตัวแปร serialVersionUID เป็นค่าอื่น ในที่นี้ผมเปลี่ยนเป็น 2
    และ comment บรรทัดที่เรียกเมธอด saveCharacter เพื่อที่จะไม่ให้มีการ save ค่า ทับของเดิม เพื่อจำลองว่า เรามีการอัพเดทเกมส์แล้วอยากจะโหลด save เวอร์ชันเก่า จากนั้นลองรันใหม่อีกรอบ ควรจะได้ผลลัพท์ดังนี้

    java.io.InvalidClassException: test.Character; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
    at java.io.ObjectStreamClass.initNonProxy(Unknown Source)
    at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source)
    at java.io.ObjectInputStream.readClassDesc(Unknown Source)
    at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
    at java.io.ObjectInputStream.readObject0(Unknown Source)
    at java.io.ObjectInputStream.readObject(Unknown Source)
    at test.Main.loadCharacter(Main.java:38)
    at test.Main.main(Main.java:17)

    วิธีแก้
    save ใหม่ด้วยค่า 2
    หรือเปลี่ยนค่าเป็น 1 แล้วอ่านใหม่

เพราะฉะนั้น ตัวเลขจะมีความหมายเมื่อมีการ save object นี้ไปไว้ที่อื่นๆ
แล้วต้องมีการโหลดจากสถานะถูก serial กลับมาเป็น object เหมือนเดิม

ปล. สาเหตุที่ต้องเป็น long เพราะ มันเก็บ id นี้เป็นจำนวนเต็มแบบ 64 บิต

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s