对象有状态和行为两种属性。行为存在于类中,而状态存在于个别的对象中。存储状态的选择有很多种,这可能要看你如何使用存储下来的状态而决定。下面我们讨论两种选项:
如果只有自己写的Java程序会用到这些数据:用序列化(Serialization)
如果数据需要被其他程序引用:写一个纯文本文件。用其他程序可以解析的特殊字符写到文件中。譬如:tab
序列化的文件是很难让一般人阅读的,但它比纯文本文件更容易让程序恢复对象的状态,也比较安全。
当然还有其他的选择。你可以将数据存进任何格式中。
对象序列化的步骤:
1.创建出FileOutputStream
FileOutputStream fileStream = new FileOutputStream("MyGame.ser");
2.创建出ObjectOutputStream
ObjectOutputStream os = new ObjectOutputStream(fileStream);
3.写入对象
os.writeObject(characterOne);
os.writeObject(characterTwo);
os.writeObject(characterThree);
4.关闭ObjectOutputStream
os.close();
Java的输入/输出API带有
连接类型的串流,它代表来源与目的地之间的连接,连接串流将串流与其他串流连接起来。
一般来说,串流要两两连接才能做出有意义的事情--其中一个表示连接,另一个则是要被调用方法的。为何要两个?因为连接的串流通常都是很低层的。以FileOutputStream为例,它有可以写入字节的方法。但我们通常不会直接写字节,而是以对象层次的观点来写入,所以需要高层的连接串流。那为何不以单一的串流来执行呢?这就要考虑到良好的面向对象设计了。每个类只要做好一件事。FileOutputStream把字节写入文件。ObjectOutputStream把对象转换成可以写入串流的数据。当我们调用ObjectOutputStream的writeObject方法时,对象会被打成串流送到FileOutputStream来写入文件。
对象序列化的时候发生了什么事?
对象实例变量的值及Java虚拟机所需的信息被保存下来。
对象的状态是什么?有什么需要保存?
当对象被序列化时,被该对象引用的实例变量也会被序列化。且所有被引用的对象也会被序列化……最棒的是,这些操作都是自动的。
如果要让类能够被序列化,就实现Serializable接口
Serializable接口又被称为marker或tag类的标记用接口,因为此接口并没有任何方法需要实现的[
备注:该接口没有任何变量及方法]。它的唯一目的就是声明实现它的类是可以被序列化的。[关于该接口的介绍:
http://gceclub.sun.com.cn/Java_Docs/html/zh_CN/api/java/io/Serializable.html]
例子:
import java.io.*;
public class Box
implements Serializable{
}
整个对象版图都必须正确地序列化,不然就得全部失败。如果某个实例变量不能或不应该被序列化,就把它标记为transient(瞬时)的。
解序列化:还原对象
序列化的反向操作。
步骤如下:
1.创建FileInputStream
FileInputStream fileStream = new FileInputStream("MyGame.ser");
2.创建ObjectInputStream
ObjectInputStream os = new ObjectInputStream(fileStream);
3.读取对象
Object one = os.readObject();
Object two = os.readObject();
Object three = os.readObject();
4.转换对象类型
GameCharacter elf = (GameCharacter) one;
GameCharacter troll = (GameCharacter) two;
GameCharacter magician = (GameCharacter) three;
5.关闭
os.close();
解序列化时发生了什么?
当对象被解序列化时,Java虚拟机会通过尝试在堆上创建新的对象,让它维持与被序列化时有相同的状态来恢复对象的原态。但这当然不包括transient的变量,它们不是null便是使用primitive主数据类型的默认值。
具体过程如下:
1.对象从Stream中读出来
2.Java虚拟机通过存储的信息判断出对象的class类型
3.Java虚拟机尝试查找和加载对象的类。如果找不到或者无法加载该类,则会抛出异常。
4.新的对象会被配置在堆上,但构造函数不会执行。
5.如果该对象在继承树上有个不可序列化的祖先类,则该不可序列化类以及在它之上的类的构造函数就会执行。
6.对象的实例变量会被还原成序列化时点的状态值。
静态变量会被序列化么?[no]
下面列出一个完整例子来展示序列化以及反序列化:
1package serial;
2
3import java.io.Serializable;
4
5/** *//**
6 *
7 * @author Administrator
8 */
9public class GameCharacter implements Serializable {
10
11 int power;
12 String type;
13 String[] weapons;
14
15 public GameCharacter(int p, String t, String[] w) {
16 this.power = p;
17 this.type = t;
18 this.weapons = w;
19 }
20
21 public int getPower() {
22 return power;
23 }
24
25 public String getType() {
26 return type;
27 }
28
29 public String getWeapons() {
30 String weaponsList = "";
31 for (int i = 0; i < weapons.length; i++) {
32 weaponsList += weapons[i] + " ";
33 }
34 return weaponsList;
35 }
36}
37
1package serial;
2
3import java.io.FileInputStream;
4import java.io.FileOutputStream;
5import java.io.IOException;
6import java.io.ObjectInputStream;
7import java.io.ObjectOutputStream;
8
9/** *//**
10 *
11 * @author Administrator
12 */
13public class GameSaverTest {
14
15 public static void main(String[] args) {
16 GameCharacter one = new GameCharacter(50, "Elf", new String[]{"bow", "sword", "dust"});
17 GameCharacter two = new GameCharacter(50, "Troll", new String[]{"bare hands", "big ax"});
18 GameCharacter three = new GameCharacter(50, "Magician", new String[]{"spells", "invisibility"});
19
20 try {
21 //将one, two, three序列化到storage.ser
22 FileOutputStream fileStream = new FileOutputStream("storage.ser");
23 ObjectOutputStream os = new ObjectOutputStream(fileStream);
24 os.writeObject(one);
25 os.writeObject(two);
26 os.writeObject(three);
27 os.close();
28 } catch (IOException ex) {
29 ex.printStackTrace();
30 }
31 one = null;
32 two = null;
33 three = null;
34 try {
35 //从storage.ser中按照序列化的顺序读取出one, two, three
36 FileInputStream fileInputStream = new FileInputStream("storage.ser");
37 ObjectInputStream is = new ObjectInputStream(fileInputStream);
38 one = (GameCharacter) is.readObject();
39 two = (GameCharacter) is.readObject();
40 three = (GameCharacter) is.readObject();
41 System.out.println(one.type);
42 System.out.println(two.type);
43 System.out.println(three.type);
44 is.close();
45
46 } catch (Exception ex) {
47 ex.printStackTrace();
48 }
49 }
50}
51
52
此处为了简洁起便,抛出最顶层异常。
运行结果如下:
run:
Elf
Troll
Magician
成功生成(总时间:1 秒)
在工程目录下,能看到“storage.ser”这个文件。用记事本打开,可看到以下内容:
sr serial.GameCharacter恃83rV I powerL typet Ljava/lang/String;[ weaponst [Ljava/lang/String;xp 2t Elfur [Ljava.lang.String;V玳{G xp t bowt swordt dustsq ~ 2t Trolluq ~ t
bare handst big axsq ~ 2t Magicianuq ~ t spellst invisibility