java的序列化机制_java对象的序列化机制详解

Java对象的序列化机制

Java对象的序列化,是将内存中的java对象转化为二进制的字节流,然后保存到磁盘中或者在网络上。这就是序列化对象,反序列化顾名思义就是将对象的二进制字节流恢复成原来的对象。注意只有对象的类名和属性能被序列化(包括基本类型,数组,对其他对象的引用)不包括方法,static属性(静态属性),transient属性(瞬态属性)都不会被序列化。

那什么叫做序列化的对象呢,在Java中不是所有类都是序列化类,如果一个类要实现序列化就必须实现下面两个接口之一:

(1)Serializable接口(2)Externalizable接口

对于这两个接口的区别后面就会知道的,我们先把类都实现Serializable接口,这个接口java没有提供任何的方法,java设计他只是作为一个类的序列化的标志,表明此类可以进行序列化。

如果要讲java对象转化为二进制的字节流并写出,就一定需要对象的流来进行输出,所以这里用到ObjectOutputStream类,这个输出流是一个处理流,所以需要创建一个字节输出流然后用这个处理流进行包装,所以处理流也叫作包装流。反之ObjectInputSteam是将对象写入的类。写出的对象方法是ObjectOutputStem对象.writeObject(序列化对象的实例)。写入的方法是ObjectInputStream的对象.readObject(序列化的对象);说了这么多,我就做一个例子吧。。

代码:创建一个实现序列化的Person类

public class Person implementsExternalizable {privateString name;private intage;public Person1(String name,intage) {this.name=name;this.age=age;//这个地方用于测试序列化和反序列化对象时候实例化类的情况

System.out.println("带参数的构造器的使用");

}publicString getName() {returnname;

}public voidsetName(String name) {this.name =name;

}public intgetAge() {returnage;

}public void setAge(intage) {this.age =age;

}

}

代码:创建测试类,进行测试

public static void main(String[] args) throwsIOException {//如果处理流,当关闭流的时候就不需要再将节点流进行关闭了//这是对对象进行写出的操作(序列化对象)

FileOutputStream fos=null;

ObjectOutputStream oos=null;try{

fos=new FileOutputStream(new File("E://javatest.txt"));

oos=newObjectOutputStream(fos);

Person per=new Person("孙悟空",66);

oos.writeObject(per);

}catch(FileNotFoundException e) {//TODO Auto-generated catch block

e.printStackTrace();

}finally{if(oos!=null) {

oos.close();

}

}//这是对对象进行写入的操作(反序列化对象)

FileInputStream fis=null;

ObjectInputStream ois=null;try{

fis=new FileInputStream("E://javatest.txt");

ois=new ObjectInputStream(fis);//处理流将文件的字节流进行包装//将对象反序列化写入的时候得到的都是Object类型的数据,必须进行强制类型的转化

Person per=(Person)ois.readObject();

System.out.println("姓名"+per.getName()+"年龄"+per.getAge());

}catch(Exception e) {

e.printStackTrace();

}finally{if(ois!=null) {

ois.close();

}

}

}

readObject方法会抛出ClassNotFoundException异常,也就是说当反序列化时候找不到对应的java类会将引发这个异常,因为反序列化读取的仅仅是java对象的数据,而不是java类,因此采用反序列化恢复java对象时,必须提供该java对象所属类的class文件,否则就会引发该异常。

输出结果可以看出当反序列化的时候构造器没有执行,也就是反序列化类无需通过构造器来进行初始化java对象

注:如果我们向文件中使用序列化机制写入多个java的对象,使用反序列化机制恢复对象时必须按实际的写入顺序读取。

Y(^o^)Y属性是引用类型的对象的序列化

我们上面所说的属性都是String类型和基本类型,如果我们需要一个引用类型呢,那么这个引用类型也必须是可序列化的类,否则拥有该类型的属性类不可序列化。下面我将要定义一个引用类型的属性,重新创建一个Teacher类,引用属性必须实现序列化,否则Treacher不论实现不实现(1)Serializable接口  (2)Externalizable接口这两个接口,他都不是序列化的类,因为当对象序列化的时候,会顺带着把引用类型的属性进行序列化,所以要想Teacher是序列化的类,则必须将Person的类进行序列化。

代码Teacher类

public class Teacher implementsSerializable {privateString name;private Person student;//引用类型的属性(这个Person类就是上面实现序列化的类)

publicTeacher(String name,Person student) {this.name=name;this.student=student;

System.out.println("带参数的构造器的使用");

}publicString getName() {returnname;

}public voidsetName(String name) {this.name =name;

}publicPerson getStudent() {returnstudent;

}public voidsetStudent(Person student) {this.student =student;

}

}

代码 测试代码

public static void main(String[] args) throwsIOException {//TODO Auto-generated method stub

FileOutputStream fos=null;

ObjectOutputStream oos=null;

Person p=new Person("孙悟空",66);

Teacher t1=new Teacher("玄奘法师",p);

Teacher t2=new Teacher("菩提祖师",p);try{

fos=new FileOutputStream(new File("E://javatext.txt"));

oos=newObjectOutputStream(fos);

oos.writeObject(t1);

oos.writeObject(t2);

oos.writeObject(p);

oos.writeObject(t1);

}catch(FileNotFoundException e) {//TODO Auto-generated catch block

e.printStackTrace();

}finally{if(oos!=null) {

oos.close();

}

}

}

}

分析这段代码可以看出我创建出来了三个类。Person p=new Person("孙悟空",66);Teacher t1=new Teacher("玄奘法师",p);Teacher t2=new Teacher("菩提祖师",p);如果我将这三个类进行对象的序列化的话,t1写出并且Person类也会进行序列化,同理t2也是,然后我们又显示序列化了Person类,所以Person类在此次写出中,被序列化了三回,那么对于t1,t2来说实际上他们的Person类是同一个,但是如果Person序列化三回的话,t1,t2就没有引用同一个Person类,这显然是不符合实际情况的。Java对此采用了一种特殊的序列化算法,算法的内容是:

(1)所有保存到磁盘中的对象都有一个序列化编号。

(2)当程序师徒序列化一个对象的时候,程序将先检查对象是否已经序列化过,只有当该对象从未(在本次虚拟机中)被序列化过,系统才会将该对象转化成字节序列并输出。

(3)如果某个对象是已经序列化过的,程序将直接只是输出一个序列化编号,而不是重新序列化该对象。

Y(^o^)Y序列化的对象是可变的类

根据java的序列化机制,当我先写进去序列化的时候,如果我改变了可变类的属性值,那么当我想再次进行序列化的时候就不能把更改后的值序列化了,因为java的序列化机制当在此序列化同一的对象的时候,输出的是一个序列化编号。程序会比较两个对象是同一个对象,就不会把对象重新的序列化。就是更改后的对象并没有被写入。这再次验证了java的序列化机制。

代码 测试类

public classVolatileClassTest {/*** 序列化可变类

*@paramargs

*@throwsIOException

*@throwsClassNotFoundException*/

public static void main(String[] args) throwsIOException, ClassNotFoundException {//TODO Auto-generated method stub

FileOutputStream fos=null;

ObjectOutputStream oos=null;

FileInputStream fis=null;

ObjectInputStream ois=null;try{//进行序列化的操作

fos=new FileOutputStream(new File("E://javatest.txt"));

oos=newObjectOutputStream(fos);

Person p=new Person("孙悟空",600);

oos.writeObject(p);//将对象进行序列化

p.setName("红孩儿");//可变类将姓名属性设置为红孩儿,可变类

oos.writeObject(p);//将更改后的类进行序列化//进行反序列化操作

fis=new FileInputStream(new File("E://javatest.txt"));

ois=newObjectInputStream(fis);

Person per=(Person)ois.readObject();//输出的姓名还是孙悟空,再次验证了java的序列化机制

System.out.println("姓名是"+per.getName());

}catch(FileNotFoundException e) {//TODO Auto-generated catch block

e.printStackTrace();

}finally{if(oos!=null) {

oos.close();

}if(ois!=null) {

ois.close();

}

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值