如果你正在编写游戏,就得有存储和恢复游戏的功能。如果你编写的是创建图表的程序,也必须有存储/打开的功能。如果程序需要存储状态,有两种方式:
概述
对象有状态和行为两种属性。行为存在于类中,而状态存在于个别对象中。
如果你正在编写游戏,就得有存储和恢复游戏的功能。如果你编写的是创建图表的程序,也必须有存储/打开的功能。如果程序需要存储状态,有两种方式:
序列化
序列化
下面是将对象序列化的方法步骤。
Serializable1
1 2 3 4 5 6 7 8 9 10 11 12 13
| FileOutputStream fileStream = new FileOutputStream("MyGame.ser");
ObjectOutputStream os = new ObjectOutputStream(fileStream);
os.writeObject(characterOne); os.writeObject(characterTwo); os.writeObject(characterThree);
os.close();
|
解序列化
下面是解序列化的方法步骤。
Serializable2
1 2 3 4 5 6 7 8 9 10 11 12 13
| FileInputStream fileStream = new FileIntStream("MyGame.ser");
ObjecInputStream os = new ObjecInputStream(fileStream);
Object one = os.readObject(); Object two = os.writeObject(); Object three = os.writeObject();
os.close();
|
接下来我们通过一个实例来进一步了解序列化的用法
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| public class GameCharacter implements Serializable { private int power; private String type; private Weapon weapon; public int getPower() { return power; } public void setPower(int power) { this.power = power; } public String getType() { return type; } public void setType(String type) { this.type = type; } public Weapon getWeapon() { return weapon; } public void setWeapon(Weapon weapon) { this.weapon = weapon; } public void useWeapon(){ System.out.println("user weapon"); } @Override public String toString() { return "GameCharacter{" + "power=" + power + ", type='" + type + '\'' + ", weapon=" + weapon + '}'; } public static void main(String[] args) { GameCharacter gameCharacter = new GameCharacter(); gameCharacter.setPower(3); gameCharacter.setType("士兵"); Weapon weapon = new Weapon(1,"长矛"); gameCharacter.setWeapon(weapon); try { FileOutputStream fileStream = new FileOutputStream("d://MyGame.ser"); ObjectOutputStream os = new ObjectOutputStream(fileStream); os.writeObject(gameCharacter); os.close(); } catch (IOException e) { e.printStackTrace(); } }}
public class Weapon implements Serializable { int id; String name; public Weapon(int id, String name) { this.id = id; this.name = name; } }
|
当对象被序列化时,被该对象引用的实例 变量也会被序列化。(也就是当保存gameCharacter对象时,所有的对象都会被保存),所以如果对象被实例化时,被该对象引用的实例变量也需要实现Serializable接口。否则会抛出下面这个异常:
Serializable3
如果某实例变量不能或不应该被实例化,就把它标记为transient(瞬时)的。
Serializable4
为什么有些变量不能被序列化?
- 可能是设计者忘记实现Serializable。
- 或者动态数据只可以在执行时求出而不能或不必储存。
- VersionID: 序列化的识别(版本控制)
如果你将对象序列化,你必须有该类才能还原和使用该对象。但若你同时修改了该类会发生什么事情?假设你尝试把GameCharacter对象带回来,而非transient的变量type已经从String被改成int。这样会严重的违反java大的类型安全性。在对象被序列化之后类有了不同的serialVersionUID,则会还原失败。
Serializable5
会损害序列化的修改:
- 删除实例变量
- 改变实例变量的类型
- 将非瞬时的实例变量该为瞬时的
- 改变类的继承层次
- 将类从可序列化改成不可序列化
- 将实例变量改成静态的
通常不会有事的修改:
- 加入新的实例变量(还原时会使用默认值)
- 在继承层次中加入新的类
- 从继承层次中删除类
- 改变类的继承层次
- 不会影响解序列化程序设定变量值的层次修改
- 将实例变量从瞬时改成非瞬时(会使用默认值)
如果你认为类有可能会演化,就把版本识别ID放在类中
Serializable6
因此解决方案就是把serialVersionUID放在class中,让类在演化过程中还维持相同的ID。
但这只会在你有很小心地维护类的变动才办得到!也就是你得要对带回旧对象的任何问题负起全责。
文件输入输出
Last updated: