java序列化

目录:
一、什么是序列化?

看过很多博客或者书籍,里面都是对序列化的定义。列举几个常见的:
  1. 将对象的状态信息转换为可以存储或传输的二进制形式的过程
  2. 将临时存储在内存块中的数据转换成可传输数据
个人理解:“就是将对象这种类型的数据变得可以存储,类似数据库可以存储基本数据类型一样”
二、为什么要用序列化?有什么作用?

  1. 存储对象在存储介质中,以便在下次使用的时候,可以很快捷的重建一个副本。
  2. 便于数据传输,尤其是在远程调用的时候!
  3. 当我们需要把对象的状态信息通过网络进行传输,或者需要将对象的状态信息持久化,以便将来使用时都需要把对象进行序列化
问题:有时我没有序列化的时候,一样可以存储在数据库中啊,该拿出来的时候,照样拿出来啊。就像String对象。
上面的问题就是说在我存储的时候,不通过序列化也一样完美存储,为什么要多此一举?在存储时需要序列化,这是肯定的。大家知道的是序列化是将对象进行流化存储,我们有时候感觉自己在项目中并没有进行序列化操作,也一样是存进去了,那么对象需要经过序列化才能存储的说法,似乎从这儿就给阉割了。事实究竟是怎样的呢?首先看我们常用的数据类型类声明:
上面是String类系统定义。
而像其他int、long、boolean类型等,都是基本数据类型,数据库里面有与之对应的数据结构。从上面的类声明来看,我们以为的没有进行序列化,其实是在声明的各个不同变量的时候,由具体的数据类型帮助我们实现了序列化操作。
那么问题又来了,既然bean里面定义的基础类型都序列化了,那bean还序列化干嘛?
请注意我以上的说法:首先,序列化的目的有两个,第一个是便于存储,第二个是便于传输。我们一般的实体类不需要程序员再次实现序列化的时候,请想两个问题:第一:存储媒体里面,是否是有其相对应的数据结构?第二:这个实体类,是否需要远程传输(或者两个不同系统甚至是分布式模块之间的调用)?翻译过来就是:现在数据库里面要存储一个类型为Person的实体类,你说数据库咋整?是不是数据库都哭了。你存String那数据库可以转化为varchar型,Person咋转?
既然说到序列化,先看看java给我们的办法,没错实现Serializable接口,进去瞧瞧吧:
好吧,里面啥都没有。那序列化怎么实现的呢?这么牛逼吗?
其实,看一下接口的注释说明就知道,当我们让实体类实现Serializable接口时,其实是在告诉JVM此类可被序列化,可被默认的序列化机制序列化。
然后,需要说明的是,当我们在实体类声明实现Serializable接口时,再次进行观察,会发现这些类是需要被远程调用的。也就是说需要或者可能需要被远程调用,这就是序列化便于传输的用途。
三、bean是否一定要序列化

Java的JavaBean,Bean的状态信息通常是在设计时配置的,Bean的状态信息必须被保存下来,以便当程序运行时能恢复这些状态信息,这也需要序Serializable机制。因此并不是所有的Bean都需要序列化。只是有时需要远程传输等才需要序列化。
四、如何实现序列化
java中提供给我们的序列化方法就是实现Serializable接口就完事了。当然了好有许多第三方插件提供序列化,此处不再陈述。
将 Java 对象序列化为二进制文件的 Java 序列化技术是 Java 系列技术中一个较为重要的技术点,在大部分情况下,开发人员只需要了解被序列化的类需要实现 Serializable 接口,使用 ObjectInputStream 和 ObjectOutputStream 进行对象的读写。然而在有些情况下,光知道这些还远远不够,文章列举了一些真实情境,它们与 Java 序列化相关,通过分析情境出现的原因,使读者轻松牢记 Java 序列化中的一些高级认识。
不说了,先上一段代码解释下:
 
/**
*测试开始
*/
public class SerializableDemo {
	public static void main(String[] args) throws Exception {
		Person p = new Person();
		p.setName("sjyang");
		p.setAge(25);
		//序列化
		SerializableUtil su = new SerializableUtil();
		su.writeFile(p);
		//反序列化
		Person p2 = (Person)su.readFile();
		System.out.println(p2.getAge()+"姓名"+p2.getName());
	}
}
/**
 * 第一步:定义一个实体类,作为序列化对象模板
 */
class Person implements Serializable{
	private String name;
	private int age;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}
/**
 * 第二步:定义一个序列化工具类(就是将对象转化为对象流,这里将对象存到一个文件里)
 */
class SerializableUtil{
	private static final String fileRoad = "E:\\obj.bin";
	//序列化
	public void writeFile(Serializable Obj) throws FileNotFoundException, IOException{
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileRoad));
		oos.writeObject(Obj);
		System.out.println("序列化成功,写入文件地址"+fileRoad);
		oos.close();
	}
	//反序列化
	public Object readFile() throws Exception{
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileRoad));
		Object obj = ois.readObject();
		ois.close();
		return obj;
	}
}

  

 
 
 
上面的序列化过程非常简单,就是将创建的实体类对象存入到名为obj.bin的文件中了,我们可以通过反序列化获得该对象,只要反序列化的地方有该对象的模板类。
下面我们来看看序列化中注意事项:

•序列化 ID 的问题
•静态变量序列化
•父类的序列化与 Transient 关键字

1、序列化ID
假设A和B通过网络传输对象数据C,A将C序列化传输给B,然后B反序列化得到C
问题:C 对象的全类路径假设为 com.inout.Test,在 A 和 B 端都有这么一个类文件,功能代码完全一致。也都实现了 Serializable 接口,但是反序列化时总是提示不成功。报错为:
解决:虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID = 1L)。清单 1 中,虽然两个类的功能代码完全一致,但是序列化 ID 不同,他们无法相互序列化和反序列化。注意:反序列化时,不光要保证ID一致,同时还要保证反序列化的这个类的包名和你序列化那个对象的类包名一致。
序列化 ID 在 Eclipse 下提供了两种生成策略,一个是固定的 1L,一个是随机生成一个不重复的 long 类型数据(实际上是使用 JDK 工具生成),在这里有一个建议,如果没有特殊需求,就是用默认的 1L 就可以,这样可以确保代码一致时反序列化成功。那么随机生成的序列化 ID 有什么作用呢,有些时候,通过改变序列化 ID 可以用来限制某些用户的使用。
特性使用案例
读者应该听过 Façade 模式,它是为应用程序提供统一的访问接口,案例程序中的 Client 客户端使用了该模式,案例程序结构图。
Client 端通过 Façade Object 才可以与业务逻辑对象进行交互。而客户端的 Façade Object 不能直接由 Client 生成,而是需要 Server 端生成,然后序列化后通过网络将二进制对象数据传给 Client,Client 负责反序列化得到 Façade 对象。该模式可以使得 Client 端程序的使用需要服务器端的许可,同时 Client 端和服务器端的 Façade Object 类需要保持一致。当服务器端想要进行版本更新时,只要将服务器端的 Façade Object 类的序列化 ID 再次生成,当 Client 端反序列化 Façade Object 就会失败,也就是强制 Client 端从服务器端获取最新程序。
2、静态变量序列化
静态变量是属于类的,因此不能序列化
3、父类的序列化与 Transient 关键字
当一个需要被序列化的类继承一个父类时,如何调用父类中变量?
答案:父类中必须有一个无参构造方法,且对变量初始化。否则获取的变量值都为初始值,例如String为null。
transient关键字:用来忽视需要序列化的属性。
 
总结:
java的序列化在许多细节方面还需要研究,在此希望大家批评指正。
posted @ 2018-01-26 16:21  巫师大人  阅读(232)  评论(0编辑  收藏  举报