java 序列化内容分析

网络通信,数据持久化的时候经常会涉及到对象序列化问题,如果对序列化的内容大小或者序列化的效率比较敏感,可以选择protobuf,hession等,如果不是特别在意这块,选择java默认的实现就可以了。java中通过调用ObjectOutputStream对象的writeObject()方法实现对象的序列化。来看段源码:

            // remaining cases
            if (obj instanceof String) {
                writeString((String) obj, unshared);
            } else if (cl.isArray()) {
                writeArray(obj, desc, unshared);
            } else if (obj instanceof Enum) {
                writeEnum((Enum) obj, desc, unshared);
            } else if (obj instanceof Serializable) {
                writeOrdinaryObject(obj, desc, unshared);
            } else {
                if (extendedDebugInfo) {
                    throw new NotSerializableException(
                        cl.getName() + "\n" + debugInfoStack.toString());
                } else {
                    throw new NotSerializableException(cl.getName());
                }
            }

ObjectOutputStream在写入对象的时候对四种不同的类型做了不同的处理,除了字符串,数组类型,枚举类型之外,其他的对象想要序列化必须实现Serializable这个标识接口。看到这里感觉好像基本数据类型(int,long之类)就不能写入,其实不是的。ObjectOutputStream在写入基本数据类型的时候会把它们转化成对象类写入,int的对象类就是Integer,而

Integer类的父类Number就是实现Serializable这个接口的。

经常看到网上说java中实现一个对象的序列化可以实现Serializable接口或者Externalizable接口,本质上Externalizable就是Serializable接口的一个子接口,但是实现Externalizable接口的类可以自定义写入需要通信或者持久化的字段信息。来看个例子:

public class ObjectTest {

	public static void main(String[] args) throws Exception {
		new ObjectTest().serialization();
	}
	
	public void serialization() throws Exception{
		URL url=ObjectTest.class.getResource("/config.properties");
		OutputStream output=new FileOutputStream(new File(url.getPath()));
		ObjectOutput objectOut=new ObjectOutputStream(output);
		objectOut.writeObject(new Object1());
		objectOut.close();
		output.close();
		
		URL url2=ObjectTest.class.getResource("/config2.properties");
		OutputStream output2=new FileOutputStream(new File(url2.getPath()));
		ObjectOutput objectOut2=new ObjectOutputStream(output2);
		objectOut2.writeObject(new Object2());
		objectOut2.close();
		objectOut2.close();
	}
}

package net.flyingfat.serialization;

import java.io.Serializable;

public class Object1 implements Serializable {
	private static final long serialVersionUID = 1L;
	private String bac="abc";
	private int a=123;
	
	public String getBac() {
		return bac;
	}

	public void setBac(String bac) {
		this.bac = bac;
	}

	public int getA() {
		return a;
	}

	public void setA(int a) {
		this.a = a;
	}
	
}

package net.flyingfat.serialization;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

public class Object2  implements Externalizable {
	private static final long serialVersionUID = 1L;
	private String bac="123";
	private int a;

	public String getBac() {
		return bac;
	}

	public void setBac(String bac) {
		this.bac = bac;
	}

	@Override
	public void writeExternal(ObjectOutput out) throws IOException {
		out.write(bac.getBytes("utf-8"));
	}

	@Override
	public void readExternal(ObjectInput in) throws IOException,
			ClassNotFoundException {
		in.readObject();
	}

	public int getA() {
		return a;
	}

	public void setA(int a) {
		this.a = a;
	}
	
}

通过UE打开两个文件进行对比:

config.properties内容如下:


config2.properties内容如下:

2

截图内容太密,没法仔细标出每个字节是干嘛的,有兴趣的直接看源码吧,简单说明下:

1.两者开头都一样,写入类的定义(net.flyingfat.serialization.Object1/Object2),紧接着的8个字节就是 serialVersionUID(序列化id,它的作用应该都清楚)

2.后面具体对象的信息就一样了,实现Serializable接口的对象写入的是每个字段的类型,名字,和值,而实现Externalizable接口的对象写入的就是我们自定义的内容。


在来看另一个现象

public class Object1 extends Super  {
	private static final long serialVersionUID = 1L;
	private String bac="abc";
	private int a=123;
	
	public String getBac() {
		return bac;
	}

	public void setBac(String bac) {
		this.bac = bac;
	}

	public int getA() {
		return a;
	}

	public void setA(int a) {
		this.a = a;
	}
	
}

class Super implements Serializable  {
	
	private String s;
	
	public Super(String s) {
		super();
		this.s = s;
	}
	public Super() {}

	public String getS() {
		return s;
	}

	public void setS(String s) {
		this.s = s;
	}
	
}

public class Object2 extends Super implements Serializable {
	private static final long serialVersionUID = 1L;
	private String bac="abc";
	private int a=123;
	
	public String getBac() {
		return bac;
	}

	public void setBac(String bac) {
		this.bac = bac;
	}

	public int getA() {
		return a;
	}

	public void setA(int a) {
		this.a = a;
	}
	
}

class Super  {
	
	private String s;
	
	public Super(String s) {
		super();
		this.s = s;
	}
	public Super() {}

	public String getS() {
		return s;
	}

	public void setS(String s) {
		this.s = s;
	}
	
}
同时序列化这两个对象,可以看到内容不一样。

结论就是: 对象在序列化的时候,看该对象是直接还是间接实现Serializable接口,直接实现就只序列化当前对象本身的字段信息,如果是间接就会挨个往上找到父类,一并把父类的字段信息也序列化了。



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值