序列化

小马SCJP认证讲坛---序列化与反序列化
发布: 2010-4-08 16:51 | 作者: mawl | 来源: 迈胜 IT人才培训机构

原题在现:


11.jpg


考察点:对象序列化与反序列化
解析:首先需指出的是题目的Realizable应换为Readable。
          A选项的大意是:聚合类实例化的对象不能被序列化。(什么叫聚合,即has-a关系,如果说,类A聚合类B,意味着类A包含类B的对象。)
          B选项的大意是:在一架虚拟机上序列化的对象能够被成功的在另外一台虚拟机上反序列化
          C选项的大意是:用volatile修饰的对象无法成功的序列化和反序列化。
          D选项的大意是:用transient修饰的对象无法成功的序列化和反序列化。明显这句话是正确的。
          E选项的大意是:一个父类没有实现Serialization接口的子类对象进行序列化是合法的。
帮大家把大意翻译出来,相信答案就很明显了。
扩展:序列化与反序列化的的几个关键知识点:
1、什么是序列化、反序列化
Serialization(序列化)是一种将对象以一连串的字节描述的过程;
反序列化deserialization是一种将这些字节重建成一个对象的过程。
2、为什么要序列化
Java中,一切都是对象,在分布式环境中经常需要将Object从这一端网络或设备传递到另一端。这就需要有一种可以在两端传输数据的协议。Java序列化机制就是为了解决这个问题而产生的。
3、什么是serialVersionUID
      Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。
    当实现java.io.Serializable接口的实体(类)没有显式地定义一个名为serialVersionUID,类型为long的变量时,Java序列化机制会根据编译的class自动生成一个serialVersionUID作序列化版本比较用,这种情况下,只有同一次编译生成的class才会生成相同的serialVersionUID 。
4、怎么样进行一个简单的序列化反序列化过程
代码演示如下:
用来序列化的一个用户类:UserInfo.java
CODE:

package com.mison.serialize;

import java.io.Serializable;

public class UserInfo implements Serializable{
        private String username;
        private String password;
        private transient int age;
       
        public UserInfo(String username,String password,int age){
                this.age = age;
                this.password = password;
                this.username = username;
        }

        public String toString(){
                return "用户名:"+ this.username + ";密码:" + this.password + ";年龄:" + this.age ;
        }
}
实现序列化与反序列化的主函数类:TestSerializeable.java
CODE:

package com.mison.serialize;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;

public class TestSerializeable {
        //序列化对象到文件
        public static void serialze(String filename){
               
                try {
                        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(filename));
                        out.writeObject("序列化日期是:");//序列化一个字符串到文件
                        out.writeObject(new Date());//序列化一个日期到文件
                        UserInfo user = new UserInfo("mawl","马文亮",25);
                        out.writeObject(user);//序列化一个对象
                        out.close();
                } catch (FileNotFoundException e) {
                        e.printStackTrace();
                        System.out.println("系统文件找不到。");
                } catch (IOException e) {
                        e.printStackTrace();
                }
        }
       
        public static void deserialize(String filename) throws ClassNotFoundException{
                try {
                        ObjectInputStream in = new ObjectInputStream(new FileInputStream(filename));
                        String time = (String)in.readObject();
                        System.out.println(time.toString());
                        Date d = (Date)in.readObject();
                        System.out.println(d.toString());
                        UserInfo user = (UserInfo)in.readObject();
                        System.out.println(user.toString());
                } catch (FileNotFoundException e) {
                        e.printStackTrace();
                        System.out.println("系统找不到文件");
                } catch (IOException e) {
                        e.printStackTrace();
                }
        }
        public static void main(String[] args) {
                serialze("e:\\text.txt");
                System.out.println("序列化完毕");
                try {
                        deserialize("e:\\text.txt");
                        System.out.println("反序列化完毕");
                } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                }

        }

}
有关例子的注意点:
1、序列化时用transient修饰的变量不会被序列化
2、如果你先序列化对象A后序列化B,那么在反序列化的时候一定记着JAVA规定先读到的对象是先被序列化的对象,不要先接收对象B,那样会报错
3、针对对象的序列化,都保存了什么内容:
1)对象的类型
2)对象属性的类型
3)对象属性的值
(关于这点你可以用以上的例子,对text.txt这个文件的大小来判断)
hjqhezgh2010-4-09 09:30:32
恩。。学习了。。我有个疑问问下。。

如果我有这么一个类的关系
CODE:

public class A{
   private B b;
}

public class B{

}
我要序列化A,但是B是没有实现序列化接口的,那么结果会是什么呢。
mawl2010-4-09 10:31:57
回复 2# hjqhezgh
这个问题问得好,强总是能问到点儿上啊。针对这个问题我要做以下几点说明:
1、首先你这是一个关联的情况。聚合在某些情况下也是可以被序列化的。所以题目的A答案是不正确的。(但是,这只是针对当前这个类,也就是你问的A这个类)
java对象序列化不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据。可以将整个对象层次写入字节流中,可以保存在文件中或在网络连接上传递。利用对象序列化可以进行对象的“深复制”,即复制对象本身及引用的对象本身。序列化一个对象可能得到整个对象序列。
2、如果上面那个观点比较抽象的话, 我们可以做一个实际的例子。就用我上面的例子,不同的是我们增加一个B类。在UserInfo里加一个成员变量:private B b;然后我们以两种情况跑跑看。第一种我们不注释private B b。然后我们会发现text.txt文件的大小是213字节。在打开这个.txt文档看看。我们在乱码文字里可以看到:com/mison/serialize/B;这样一串字符串。这则说明没有明确申明Serializable的类B创建的这个引用b也被序列化了。第二种情况则是,我们把private B b;这段代码给注释以后,在运行我们的主函数类。会发现text.txt类变为182字节。缺少的那部分正是B这个类创建的引用值b的序列化的内容。
3、同样。这才是问题的关键。为了应证B这个类有没有被序列化亦或是能不能被序列化。我们还可以多做一点测试。我们在B这个类里面加一个属性值
private int age;然后我们在运行一个主函数这个类。这时我们会发现text.txt的这个文件的大小还是213字节。这就说明B这个类没有被序列化。反过来说,这就说明被序列化的也就只是UserInfo这个类的成员变量b 这个引用值。说白点就只是序列化了一个引用对象的地址。既然进行到这步,我们肯定会在做一个测试。那就是我们在创建这个b的引用的时候直接给它new一个内存空间会怎么样?(这个你不妨可以试试看)它会抛一个java.io.NotSerializableException: com.mison.serialize.B    的错误。这下答案很明显了。进一步说明了,B类不能被实例化。

你可能感兴趣的:(java,jvm,虚拟机,网络协议)