(二)远程服务:Java 对象序列化和反序列化

文章目录

在远程方法调用 RMI 学习的过程中,涉及到一个概念,序列化,本文进行详述。

  • Java 对象的序列化和反序列化 的两种应用场景

有时候需要将 Java 对象保存永久保存,比如保存到文件中,过程:Java 对象 -> IO 对象流 -> 写入文件 -> 字符串。当我们需要将文档中的字符串恢复为 Java 对象的时候,需要相反的过程:字符串 -> 读文件 -> IO 流 -> (强制)转化为 Java 对象
还有一种场景,在网络通信中,对象要先变为IO流经过网络传输之后,再被还原为Java对象。过程:对象 -> 写IO -> 读IO -> 对象

  • 序列化是一个处理对象信息的过程

序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程。
反序列化是将传输信息恢复为对象的过程。

  • Java 对象被序列化的两种方式

方式一:Java 对象所属的类需要直接或者间接实现 Serializable接口。
方式二:Java 对象所属的类需要直接或者间接实现 Externalizable接口。这里需要注意,如果通过继承实现Externalizable接口,必须覆盖重写 writeExternal 和 readExternal 方法,否则子类新增属性无法参与序列化。

  • Serializable 和 Externalizable 的区别

Serializable 可以是间接或者直接实现,所在类的属性都会被序列化。
Serializable 的思路是,如果没有特殊,本类以及子类的属性将参与序列化。
Externalizable 也可以是间接实现或者直接实现,但是其参与序列化和反序列化的属相都需要通过重写方法去指定。

  • transient
    实现 Serializable 的类 ,其属性默认都会参与序列化和反序列化,如果不想某个属性参与序列化,增加 transient 修饰即可。但是 实现 Externalizable 的类不会受transient 的影响。

    private transient String other;//transient 表示该字段不参与序列化
    
  • Externalizable 需要覆盖重写的 writeExternal 和 readExternal 方法举例

	
	//声明属性
	private String userName;
	private String password;
	private int age;
	private int age2;	
	
	//get set方法略
	
	//这两个方法无需显式调用,但是哪些属性需要序列化,则需要单独指明	
    @Override
	public void writeExternal(ObjectOutput out) throws IOException {
		out.writeUTF(password);
		out.writeUTF(userName);
		out.writeInt(age);
		out.writeInt(age2);
	}
	@Override
	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
		userName=in.readUTF();
		password=in.readUTF();
		age=in.readInt();
		age2=in.readInt();
	}
  • 序列号的生成

Java 的序列化和反序列化工作是由 JVM 完成的,在序列化和反序列化的过程中,JVM 会根据类的路径、名字、内容生成一个 long 类型的数字串(当然你也可以自己指定),这个数字串被称为序列号。

```java
private static final long serialVersionUID = -5635658486326609381L;
```
  • 序列号的生成和 JVM 的版本有关系吗?

根据我的实验,对于同一个路径下,同一个类,在内容不变的情况下,JVM 生成的序列号是一致的。测试 jdk 版本有 jdk1.6、jdk1.7、jdk1.8。

  • 序列号是允许不显式表述的

序列号是可以不写出来的,但是要求序列化参照的类和反序列化参照的类都不写才可以。

  • 序列号的意义

由于序列号的生成是和 类的路径、名字、内容 有关,这意味着如果你的类的内容有修改,JVM 自动生成的序列号就会有变动,如果你在类中指定了序列号,JVM 就会以你指定的为准。这样做是有好处的,可以提醒你远程交互中两端的类的版本不一致。但是这也有弊端,对于远程调用来说,如果一方
实体类有变动,那么双方生成的序列号可能就不一样。所以序列号是为了保证序列化和反序列化双方的一致性。

  • 一定要显式的指定序列号

由于我们无法确保类的内容不会更改,所以一定要写序列号,这个是可以自定义的,比如写成1L

  • Java 对象序列化会影响性能且需要平台一致性

显然序列化会影响性能,而且需要同为 Java 平台。

  • 序列化应用的简单测试——以对象保存到 txt 中为例
  • 实体类
	package common;
	import java.io.Serializable;
	public class Model implements Serializable{
	private static final long serialVersionUID = -3265803653258922833L;

	private String userName;
	private String password;
	private transient int age;
	private int age2;
	private transient String other;//transient 表示该字段不参与序列化
	//get\set 方法 略

	//如果 implements Externalizable ,需在下面的方法中指明需序列化个反序列化字段
	//序列化字段
	/*@Override
	public void writeExternal(ObjectOutput out) throws IOException {
		
		out.writeUTF(password);
		out.writeUTF(userName);
		out.writeInt(age);
		out.writeInt(age2);
	}
	//反序列化字段
	@Override
	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
		userName=in.readUTF();
		password=in.readUTF();
		age=in.readInt();
		age2=in.readInt();
	}*/
	
}

  • 测试方法
	package common;

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

public class Test {
	
	//文件路径和名字
	private static String filePath="D://a.txt";
	
	
	public static void main(String[] args) {
		Model m=new Model();
		m.setUserName("jecket");
		m.setPassword("123456");
		m.setOther("other");
		System.out.println(ToStringBuilder.reflectionToString(m));
		//写到文件
		jdkSerializableWrite(filePath,m);
		
		//从文件读取
		Model m2=jdkSerializableRead(filePath);
		System.out.println(ToStringBuilder.reflectionToString(m2));
	}
	
	/**
	 * 将对象序列化写入 txt 文件
	 * @param <T>
	 * @param t
	 */
	public static <T> void jdkSerializableWrite(String filePath,T t){
        try {
           ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(filePath));
           oos.writeObject(t);
           oos.close();

       } catch (FileNotFoundException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       } catch (IOException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       }  
   }
	/**
	 * 读取 序列化对象 从txt文件
	 * @param <T>
	 * @param filePath
	 * @return
	 */
	public static <T> T jdkSerializableRead(String filePath) {
		 try {
	            @SuppressWarnings("resource")
	            ObjectInputStream ois=new ObjectInputStream(new FileInputStream(filePath));
	            return (T) ois.readObject();
	            
	        } catch (FileNotFoundException e) {
	            // TODO Auto-generated catch block
	            e.printStackTrace();
	        } catch (IOException e) {
	            // TODO Auto-generated catch block
	            e.printStackTrace();
	        } catch (ClassNotFoundException e) {
	            // TODO Auto-generated catch block
	            e.printStackTrace();
	        }
		return null; 
	}
	
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值