一、Serializable接口
java的“对象序列化”能将一个实现了Serilizable接口的对象转换成一组byte,日后要用这个对象的时候,能把这些byte数据恢复出来,并据此重构那个对象。在跨网络环境下也是如此,序列化具有跨平台性。
之所以加入对象序列化是要用它来实现两个重要功能:java远程方法调用(RMI,Remote Method Invocation)能像调用自己机器上的对象那样去调用其他机器上的对象。当向远程对象传递消息的时候,就需要通过对象序列化来传送参数和返回值。对javaBean来说,Bean的状态信息通常是在设计时配置的,这些状态信息必须保存起来,供程序启动时用。对象序列化就是负责这些工作的。
要序列化对象,必须先创建一个OutputStream,然后把它嵌进ObjectOutputStream。用writeObject()方法把对象写入OutputStream。读的时候,把InputStream嵌到ObjectInputStream里,然后再调用readObject()方法,得到一个Object的引用,根据需要进行类型转换。
对象序列化不仅能保存对象的副本,还会把它引用的对象也保存起来,然后再继续跟踪那些对象的引用。涵盖的范围不仅包括对象的成员数据,而且包含数组里的引用。(这个是自动进行的,不需要我们编写代码)
序列化能实现轻量级的persistence。(persisitence是指,对象的生命周期不是由程序是否运行决定,在程序的两次调用之间对象仍然还活着)通过将序列化处理的对象写入磁盘,等到程序再次运行的时候再把它读出来,可以达到persistence的效果,之所以是“轻量级”是因为不能用像“persistent”这样的关键词来直接定义一个对象,然后让系统处理所有的细节。相反,必须明确进行序列化和解序列化(desierialize)。
二、相关说明与案例(一):
java有个特点就是序列化,简单地来说就是可以将这个类存储在物理空间(当然还是以文件的形式存在),那么当你从本地还原这个文件时,你可以将它转换为它本身。这可以极大地方便网络上的一些操作,但同时,因为涉及到安全问题,所以并不希望把类里面所有的东西都能存储(因为那样,别人可以通过序列化知道类里面的内容),那么我们就可以用上transient这个关键字,它的意思是临时的,即不会随类一起序列化到本地,所以当还原后,这个关键字定义的变量也就不再存在。
通常,我们写的程序都要求特定信息能持久存在或保存到磁盘上,以供一个程序使用或用在同一个程序的另一次运行上.这种持久性可以通过几种方式来实现,包括写到数据库中或是利用JAVA为对象序列化提供的支持.不管我们选用什么方法,类实例的持久性都是通过保存类的域的状态来完成的,保存这些状态,以便以后可以对它们进行访问或使用它们来创建相同的实例.然而,有可能并不是所有的域都需要被保存起来.当一个实例被持久化时,其内部的一些域却不需要持久化,则可以用trainsient修饰符告诉编译器指定的域不需要被持久保存.
首先,让我们看一些Java serialization的代码: public class LoggingInfo implements java.io.Serializable { private Date loggingDate = new Date(); private String uid; private transient String pwd; LoggingInfo(String user, String password) { uid = user; pwd = password; } public String toString() { String password=null; if(pwd == null) { password = "NOT SET"; } else { password = pwd; } return "logon info: n " + "user: " + uid + "n logging date : " + loggingDate.toString() + "n password: " + password; } }
现在我们创建一个这个类的实例,并且串行化(serialize)它 ,然后将这个串行化对象写如磁盘。 LoggingInfo logInfo = new LoggingInfo("MIKE", "MECHANICS"); System.out.println(logInfo.toString()); try { ObjectOutputStream o = new ObjectOutputStream( new FileOutputStream("logInfo.out")); o.writeObject(logInfo); o.close(); } catch(Exception e) {//deal with exception}
To read the object back, we can write try { ObjectInputStream in =new ObjectInputStream( new FileInputStream("logInfo.out")); LoggingInfo logInfo = (LoggingInfo)in.readObject(); System.out.println(logInfo.toString()); } catch(Exception e) {//deal with exception}
如果我们运行这段代码,我们会注意到从磁盘中读回(read——back (de-serializing))的对象打印password为"NOT SET"。这是当我们定义pwd域为transient时,所期望的正确结果。 现在,让我们来看一下粗心对待transient域可能引起的潜在问题。假设我们修改了类定义,提供给transient域一个默认值, 代码如下: public class GuestLoggingInfo implements java.io.Serializable { private Date loggingDate = new Date(); private String uid; private transient String pwd; GuestLoggingInfo() { uid = "guest"; pwd = "guest"; } public String toString() { //same as above } } 现在,如果我们穿行化GuestLoggingInfo的一个实例,将它写入磁盘,并且再将它从磁盘中读出,我们仍然看到读回的对象打印password 为 "NOT SET"。
当从磁盘中读出某个类的实例时,实际上并不会执行这个类的构造函数, 而是载入了一个该类对象的持久化状态,并将这个状态赋值给该类的另一个对象。
例:
Which three concerning the use of the java.io.Serializable interface are
true? (Choose three.)
A. Objects from classes that use aggregation cannot be serialized.
B. Art object serialized on one JVM can be successfully deserialized on
a different JVM.
C. The values in fields with the volatile modifier will NOT survive
serialization and deserialization.
D. The values in fields with the transient modifier will NOT survive
serialization and deserialization.
E. It is legal to serialize an object of a type that has a supertype that
does NOT implement java.io.Serializable.
Answer: BDE
三、相关说明与案例(二):
在JAVA中,一个大的应用程序需要保存很多对象的时候,由于虚拟机内存有限,(资源宝贵啊 )有时不可能所有有用的对象都放到内存中,因此,需要将不常用的对象暂时持久化的文件中,当需要这个对象时,再从文件把对象恢复到内存中,这就是所谓对象的序列化和反序列化。本文讲实现如何将对象序列化到文件,然后再从文件反序列化到对象,你会发现其实特别简单
先看一下对象的序列化和反序列化的关键是什么
1,首先被序列化的对象必须实现 java.io.Serializable 接口,其实这个接口没定义任何方法
2,序列化时,需要用到对象输出流ObjectOutputStream ,然后通过文件输出流构造 ObjectOutputStream 对象调用writeObject写入到文件(或者是writeIntwriteShort等写入基本数据类型)
3,反之,反序列化时用到对象输入流ObjectIntputStream, 然后通过文件输出流构造 ObjectIntputStream对象调用readObject加载到内存,注意,是返回万能Object类型(或者是readIntreadShort等读入基本数据类型)
4,注意:序列化时transient变量(这个关键字的作用就是告知JAVA我不可以被序列化)和静态变量不会被序列化(关于静态变量不会序列化保留意见,本实例先不用静态变量)
5,顺序问题,如果你先序列化对象A后序列化B,那么在反序列化的时候一定记着JAVA规定先读到的对象是先被反序列化的对象,不要先接收对象B,那样会报错,所以反序列化时要还是先反序列化A再B
好了,现在我们做一个实例,我们现在要序列化一个会员对象到文件里,然后再把它读出来
先创建会员类
Java代码
1. import java.io.*;
2. import java.util.*;
3.
4. //一定要实现Serializable接口才能被序列化
5. public class UserInfo implements Serializable {
6. public String userName;
7. public String userPass;
8. //注意,userAge变量前面的transient
9. public transient int userAge;
10.
11. public UserInfo(){
12. }
13.
14. public UserInfo(String username,String userpass,int userage){
15. this.userName=username;
16. this.userPass=userpass;
17. this.userAge=userage;
18. }
19.
20. public String toString(){
21. return "用户名: "+this.userName+";密码:"+this.userPass+
22. ";年龄:"+this.userAge;
23. }
24. }
import java.io.*;
import java.util.*;
//一定要实现Serializable接口才能被序列化
public class UserInfo implements Serializable {
public String userName;
public String userPass;
//注意,userAge变量前面的transient
public transient int userAge;
public UserInfo(){
}
public UserInfo(String username,String userpass,int userage){
this.userName=username;
this.userPass=userpass;
this.userAge=userage;
}
public String toString(){
return "用户名: "+this.userName+";密码:"+this.userPass+
";年龄:"+this.userAge;
}
}
接着我们开始写如何序列化和反序列化,初学认真看哦
Java代码
1. import java.io.*;
2. import java.util.*;
3. public class Test {
4.
5. //序列化对象到文件
6. public static void serialize(String fileName){
7. try
8. {
9. //创建一个对象输出流,讲对象输出到文件
10. ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream(fileName));
11.
12. out.writeObject("序列化日期是:"); //序列化一个字符串到文件
13.
14. out.writeObject(new Date()); //序列化一个当前日期对象到文件
15.
16. UserInfo user=new UserInfo("renyanwei","888888",20);
17. out.writeObject(user); //序列化一个会员对象
18.
19. out.close();
20. }
21. catch (Exception x)
22. {
23. System.out.println(x.toString());
24. }
25.
26. }
27. //从文件反序列化到对象
28. public static void deserialize(String fileName){
29. try
30. {
31. //创建一个对象输入流,从文件读取对象
32. ObjectInputStream in=new ObjectInputStream(new FileInputStream(fileName));
33.
34. //注意读对象时必须按照序列化对象顺序读,否则会出错
35. //读取字符串
36. String today=(String)(in.readObject());
37. System.out.println(today);
38.
39. //读取日期对象
40. Date date=(Date)(in.readObject());
41. System.out.println(date.toString());
42.
43. //读取UserInfo对象并调用它的toString()方法
44. UserInfo user=(UserInfo)(in.readObject());
45. System.out.println(user.toString());
46.
47. in.close();
48. }
49. catch (Exception x)
50. {
51. System.out.println(x.toString());
52. }
53.
54. }
55.
56. public static void main(String[] args) {
57. serialize("D:test.txt");
58. System.out.println("序列化完毕");
59.
60. deserialize("D:test.txt");
61. System.out.println("反序列化完毕");
62. }
63.
64. }
import java.io.*;
import java.util.*;
public class Test {
//序列化对象到文件
public static void serialize(String fileName){
try
{
//创建一个对象输出流,讲对象输出到文件
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream(fileName));
out.writeObject("序列化日期是:"); //序列化一个字符串到文件
out.writeObject(new Date()); //序列化一个当前日期对象到文件
UserInfo user=new UserInfo("renyanwei","888888",20);
out.writeObject(user); //序列化一个会员对象
out.close();
}
catch (Exception x)
{
System.out.println(x.toString());
}
}
//从文件反序列化到对象
public static void deserialize(String fileName){
try
{
//创建一个对象输入流,从文件读取对象
ObjectInputStream in=new ObjectInputStream(new FileInputStream(fileName));
//注意读对象时必须按照序列化对象顺序读,否则会出错
//读取字符串
String today=(String)(in.readObject());
System.out.println(today);
//读取日期对象
Date date=(Date)(in.readObject());
System.out.println(date.toString());
//读取UserInfo对象并调用它的toString()方法
UserInfo user=(UserInfo)(in.readObject());
System.out.println(user.toString());
in.close();
}
catch (Exception x)
{
System.out.println(x.toString());
}
}
public static void main(String[] args) {
serialize("D:test.txt");
System.out.println("序列化完毕");
deserialize("D:test.txt");
System.out.println("反序列化完毕");
}
}
运行结果:
序列化完毕 今天是: Thu Oct 23 18:31:11 CST 2008 用户名: renyanwei;密码:888888;年龄:0 反序列化完毕
我们看代码总结一下,serialize方法实现了对象的序列化功能,讲对象序列化到文件,使用了文件的输出流ObjectOutputStream 的writerObject方法一次写入一个对象,对象在文件中按写入顺序存储。
deserialize方法实现反序列化,从文件将对象反序列化到内存,使用了ObjectIntputStream 的readObject方法,因为序列化对象是按照写入顺序存储的,所以在反序列化时必须按照写入顺序读取
大家看了结果就应该知道了,标明transient的实例变量是没被序列化进去的
其实序列化的意义不止如此,不但可以将对象放到文件流,还可以放到网络流里,比如我们做的Socket程序,你不但可以发送给对方一行消息,还可以发送过去一个对象,我估计QQ就不止是把我们的消息发送到对方,应该是一个对象,里面包含了发送者的信息什么的。
1. import java.io.*;
2. public class Foo implements Serializable {
3. public int x, y;
4. public Foo( int x, int y) { this.x = x; this.y = y; }
5.
6. private void writeObject( ObjectOutputStream s)
7. throws IOException {
8. s.writeInt(x); s.writeInt(y)
9. }
10.
11. private void readObject( ObjectInputStream s)
12. throws IOException, ClassNotFoundException {
13.
14. // insert code here
15.
16. }
17. }
Which code, inserted at line 14, will allow this class to correctly
serialize and deserialize?
A. s.defaultReadObject();
B. this = s.defaultReadObject();
C. y = s.readInt(); x = s.readInt();
D. x = s.readInt(); y = s.readInt();
Answer: D
Given:
12. import java.io.*;
13. public class Forest implements Serializable {
14. private Tree tree = new Tree();
15. public static void main(String [] args) {
16. Forest f= new Forest();
17. try {
18. FileOutputStream fs = new FileOutputStream(”Forest.ser”);
19. ObjectOutputStream os = new ObjectOutputStream(fs);
20. os.writeObject(f); os.close();
21. } catch (Exception ex) { ex.printStackTrace(); }
22. } }
23.
24. class Tree { }
What is the result?
A. Compilation fails.
B. An exception is thrown at runtime.
C. An instance of Forest is serialized.
D. A instance of Forest and an instance of Tree are both serialized.
Answer: B
java.io.NotSerializableException: Tree
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1156)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1509)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1474)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1392)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1150)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)
at Forest.main(Forest.java:20
如果把Tree也实现序列化接口implements Serializable就OK了
把14. private Tree tree = new Tree();改成private Tree tree;也行。
如果一个可序列化类有多个父类(包括直接或间接父类),则该类的所有父类要么是可序列化的,要么有无参数的构造器——否则会反序列化时抛出InvalidClassException异常。
当程序创建子类实例时,系统会隐式地为它的所有父类都创建实例(并建立和此子类实例的关联)!当我们反序列化某个子类的实例时,反序列化机制需要恢复其关联的父类实例,恢复这些父类实例有两种方式:
A.使用反序列化机制。
B.使用父类无参数的构造器。
在上面两种方式中,反序列化机制优先采用第一种机制。如果某个父类既不可序列化(没有implements Serializable),即不能使用第一种机制;也没有提供无参数的构造器,则不可采用第二种机制,则反序列化该子类实例将抛出异常。
Assuming that the serializeBanana2() and the deserializeBanana2()
methods will correctly use Java serialization and given:
13. import java.io.*;
14. class Food {Food() { System.out.print(”1”); } }
15. class Fruit extends Food implements Serializable {
16. Fruit() { System.out.print(”2”); } }
17. public class Banana2 extends Fruit { int size = 42;
18. public static void main(String [] args) {
19. Banana2 b = new Banana2();
20. b.serializeBanana2(b); // assume correct serialization
21. b = b.deserializeBanana2(b); // assume correct
22. System.out.println(” restored “+ b.size + “ “); }
23. // more Banana2 methods
24. }
What is the result?
A. Compilation fails.
B. 1 restored 42
C. 12 restored 42
D. 121 restored 42
E. 1212 restored 42
F. An exception is thrown at runtime.
D
posted @
2014-04-22 19:36
Fresher_Z
阅读(
421 )
评论()
编辑
收藏
举报