POJO(plain ordinary javaobject):“简单普通Java对象”
①有一些属性为private
②针对每一个属性定义get
和set
方法接口
③没有任何类继承,也没有实现任何接口,更没有其他框架入侵的java对象
示例:
public class BasicInfoVo {
private String orderId;
private Integer uid;
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
}
JavaBean:是一种Java语言编写的可重用的组件
①所有属性为private
②有一个无参构造器
③这个类属性使用getter
和setter
来访问,其他方法遵从标准命名规范
④这个类的接口是可序列化的,实现serializable
接口
示例:
public class UserInfo implements java.io.Serializable{
//实现serializable接口。
private static final long serialVersionUID = 1L;
private String name;
private int age;
//无参构造器
public UserInfo() {
}
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;
}
//javabean当中可以有其它的方法
public void userInfoPrint(){
System.out.println("");
}
}
区别:
①POJO
其实是比javabean
更纯净的简单类或接口。POJO
严格地遵守简单对象的概念,而一些JavaBean
中往往会封装一些简单逻辑。
②POJO
主要用于数据的临时传递,它只能装载数据, 作为数据存储的载体,而不具有业务逻辑处理的能力。
③Javabean
虽然数据的获取与POJO
一样,但是javabean
当中可以有其它的方法。
备注:
Serializable
接口属于标志接口,这个接口没有任何的方法定义,它仅仅只是标记某个类能被序列化和反序列化。(接口不包含任何方法)
一、什么是序列化?
【将对象的状态信息转换为可以存储或传输的形式的过程,在序列化期间,对象将其当前状态写入到临时存储区或持久性存储区,之后,便可以通过从存储区中读取或反序列化对象的状态信息,来重新创建该对象】
序列化将数据分解成字节流,以便存储在文件中或在网络上传输。
反序列化就是打开字节流并重构对象。
二、什么情况下需要序列化?
【当我们需要把对象的状态信息通过网络进行传输,或者需要将对象的状态信息持久化,以便将来使用时都需要把对象进行序列化】
三、Serializable
主要用来支持两种主要的特性:
1、Java
的RMI(remote method invocation
),RMI
允许像在本机上一样操作远程机器上的对象,当发送消息给远程对象时,就需要用到序列化机制来发送参数和接受返回值。
2、Java
的JavaBean
,Bean
的状态信息通常是在设计时配置的,Bean
的状态信息必须被保存下来,以便当程序运行时能恢复这些状态信息,这也需要序Serializable
机制。
只需要了解被序列化的类需要实现 Serializable
接口,使用 ObjectInputStream
和 ObjectOutputStream
进行对象的读写。
四、常出现的情景
1、两个类A B
代码完全相同,试图通过网络传递对象数据,A
端将对象 C
序列化为二进制数据再传给 B
,B
反序列化得到 C
。但一直反序列化不成功。
解决:虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID
是否一致(就是 private static final long serialVersionUID = 1L
)。虽然两个类的功能代码完全一致,但是序列化 ID
不同,他们无法相互序列化和反序列化。
序列化 ID 在 Eclipse 下提供了两种生成策略,
(1)固定的 1L(一般这么做。可以确保代码一致时反序列化成功)
private static final long serialVersionUID = 1L;
(2)一个是随机生成一个不重复的 long
类型数据(作用是:通过改变序列化 ID
可以用来限制某些用户的使用。)
2、Facade
外观模式:
Client
端通过 Façade Object
才可以与业务逻辑对象进行交互。而客户端的 Façade Object
不能直接由 Client
生成,而是需要 Server
端生成,然后序列化后通过网络将二进制对象数据传给 Client
,Client
负责反序列化得到 Façade
对象。
即 服务端(生成Facade object
)序列化 ----->客户端(反序列化,得到Facade
对象,可用其与业务逻辑对象交互)
该模式可以使得 Client
端程序的使用需要服务器端的许可,同时 Client
端和服务器端的 Façade Object
类需要保持一致。当服务器端想要进行版本更新时,只要将服务器端的 Façade Object
类的序列化 ID
再次生成,当 Client
端反序列化 Façade Object
就会失败,也就是强制 Client
端从服务器端获取最新程序。
3、一个类实现了序列化,并且有其静态变量。
但序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。
4、父类的序列化与transient
:
父类不实现序列化,子类实现序列化,则将子类对象反序列化后输出父类定义的某变量的数值,该变量数值与序列化时的数值不同。
原因:一个java
对象的构造必须先有父对象,再有子对象,反序列化也是,父类没有实现序列化,则虚拟机不会序列化父类。
解决:
a、父类也实现序列化
b、为父类创造默认的无参的构造方法,在父类无参构造方法中对父类进行变量初始化。则反序列化时,构造父对象时只能调用父类无参构造函数为默认父对象,如果不先在无参构造函数中初始化变量,则父类变量值都会是默认声明值,如0,null
。
所以,使变量不被序列化的方法有:
a、Transient
关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient
变量的值被设为初始值,如 int
型的是 0
,对象型的是 null
。
b、可以将不需要被序列化的字段抽取出来放到父类中,子类实现序列化,父类不实现,则父类的字段数据将不会被序列化。
5、对敏感字段加密
服务端给客户端发送序列化对象数据,并在序列化时进行加密,客户端用解密密钥,在反序列化时,对密码读取,保证了序列化对象的数据安全。
在序列化过程中,虚拟机会试图调用对象类里的 writeObject
和 readObject
方法,进行用户自定义的序列化和反序列化,如果没有这样的方法,则默认调用是 ObjectOutputStream
的 defaultWriteObject
方法以及 ObjectInputStream
的 defaultReadObject
方法。用户自定义的 writeObject
和 readObject
方法可以允许用户控制序列化的过程,比如可以在序列化的过程中动态改变序列化的数值。基于这个原理,可以在实际应用中得到使用,用于敏感字段的加密工作。
如RMI
技术。