【JavaEE】揭秘java的序列号的本质

我们都知道,在我们自定义一个类的时候,如果该类实现了Serializable接口, 那么,他会给出一个警告,要求产生一个serialVersionUID,也就是我们所说的序列号。那么,这个序列号是什么,有什么用呢?我们先来做一个测试。
首先给出一个FileSectionInfo类,目的是让该类实现Serializable接口,然后产生一个序列号。(类里面其余的代码不用管)

public class FileSectionInfo implements Serializable {
	private static final long serialVersionUID = -2935738602520618299L;
	
	private int fileHandle;
	private long offset;
	private int size;
	
	FileSectionInfo() {
	}
	
	FileSectionInfo(byte[] value) {
		if (value.length != 16) {
			System.out.println("长度不为16B!");
			return;
		}
		
		byte[] bFileHandle = BytesToString.getBytesAt(value, 0, 4);
		byte[] bOffset = BytesToString.getBytesAt(value, 4, 8);
		byte[] bSize = BytesToString.getBytesAt(value, 12, 4);
		
		this.fileHandle = BytesToString.bytesToInt(bFileHandle);
		this.offset = BytesToString.bytesToLong(bOffset);
		this.size = BytesToString.bytesToInt(bSize);
	}

	FileSectionInfo(int fileHandle, long offset, int size) {
		this.fileHandle = fileHandle;
		this.offset = offset;
		this.size = size;
	}

	int getFileHandle() {
		return fileHandle;
	}

	void setFileHandle(int fileHandle) {
		this.fileHandle = fileHandle;
	}

	long getOffset() {
		return offset;
	}

	void setOffset(long offset) {
		this.offset = offset;
	}

	int getSize() {
		return size;
	}

	void setSize(int size) {
		this.size = size;
	}
	
	byte[] toBytes() {
		byte[] bFileHandle = BytesToString.intToBytes(fileHandle);
		byte[] bOffset =BytesToString.longToBytes(offset);
		byte[] bSize = BytesToString.intToBytes(size);
		
		byte[] result = new byte[16];
		BytesToString.setBytesAt(result, 0, bFileHandle);
		BytesToString.setBytesAt(result, 4, bOffset);
		BytesToString.setBytesAt(result, 12, bSize);
		
		return result;
	}

	@Override
	public String toString() {
		StringBuffer res = new StringBuffer("fileHandle:");
		res.append(this.fileHandle).append("\n")
		.append("offset:").append(this.offset).append("\n")
		.append("size:").append(this.size);
		
		return res.toString();
	}
	
}

我们编写一下我们的测试类:

public static void main(String[] args) {
		try {
			FileOutputStream os = new FileOutputStream(
					"F:\\example\\out.data");
			ObjectOutputStream oos = new ObjectOutputStream(os);
			oos.writeObject(new FileSectionInfo(10, 20, 30));
			
			oos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
}

这段代码,其实是为了生成一个FileSectionInfo类的对像,然后把该对象写到一个out.data文件中。执行完上述代码,控制台是没有任何输出结果的,但是我们可以看到我们的out.data文件是有内容的。那么用UE软件打开它,将看到的是十六进制的字符集。
在这里插入图片描述观察右面的字符集,我们可以清楚的看到,这个类所在的包名字,这个类的三个成员,而成员后面所跟的J、I等是这个成员的类型。我们现在要找的是我们的序列号。首先来看我们在类里面生成的序列号:-2935738602520618299L;
因为这是十进制原码,我们需要将它变为二进制补码,如下:
十进制原码:
-2935738602520618299L
变为二进制原码:
1010 1000 1011 1101 1101 0110 1011 0100 0110 1100 1010 0100 1010 0101 0011 1011
变为二进制补码:
1101 0111 0100 0010 0010 1001 0100 1011 1001 0011 0101 1011 0101 1010 1100 0101
变为十六进制补码:
D7 42 29 4B 93 5B 5A C5
在这里插入图片描述可以看到这就是序列号,它是一个类的唯一标识,因为它的类型是带有符号的长整型,所以,用它来标识一个类,那这个类的的重复概率是万亿分之一。
由此,我们可以想到,java是将序列号作为键,类作为值,形成一个hashMap来存储,这样,就可以通过序列号来找到对应的类,这当然也需要反射机制来实现。
上述的传输形式,我采用的是发送Object对象来实现的。但是,这样虽然可以实现,但是效率未必是最高的。因为它也保存了许多对于我们来说没有用的信息。
假如我们去掉序列号,对于写操作是完全没有问题的,但是对于读操作,就会出现异常:
先对Test类进行修改:

	public static void main(String[] args) {
		try {
			FileOutputStream os = new FileOutputStream(
					"F:\\example\\out.data");
			ObjectOutputStream oos = new ObjectOutputStream(os);
			oos.writeObject(new FileSectionInfo(10, 20, 30));
			
			oos.close();
			
			FileInputStream is = new FileInputStream("F:\\example\\out.data");
			ObjectInputStream ois = new ObjectInputStream(is);
			FileSectionInfo file = (FileSectionInfo) ois.readObject();
			System.out.println(file);
			ois.close();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

然后去掉FileSectionInfo类的接口和其序列号,执行,结果如下:
在这里插入图片描述进一步确定,序列号是一个类的唯一标识。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值