认识序列化
序列化有很多机制,这里只介绍java序列化
- 序列化:java序列化把java对象转换为字节序列的过程
- 反序列化:把字节序列恢复为java对象的过程
序列化的使用场景
- 永久性保存对象,保存队形的字节序列到本地文件或者数据库
-
通过序列化以字节流的形式使对象在网络中进行传递和接收
- 通过序列化在进程间传递对象
序列化的好处
- 实现了数据的持久化
- 利用序列化实现远程通信
java序列化机制
- 使用Seriallizable接口
先定义一个User类
public class User implements Serializable{
//序列化Id
private static final long serialVersionUID=1L;
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static long getSerialversionuid() {
return serialVersionUID;
}
}
序列化和反序列化
//序列化
private static void SerializeUser() throws Exception{
//序列化
User user=new User();
user.setName("miracle");
user.setAge(18);
//序列化到文件中
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("D:/test/template"));
oos.writeObject(user);
oos.close();
}
//反序列化
private static void DeSerializeUser() throws Exception{
File file=new File("D:/test/template");
ObjectInputStream ois=new ObjectInputStream(new FileInputStream(file));
User newUser=(User)ois.readObject();
}
}
- 使用Externalizable接口
public class User implements Externalizable{
private int age;
private String name;
public User(){}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(name);
out.writeInt(age);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name=(String)in.readObject();
age=in.readInt();
}
}
Test和Serializable接口的一样
总结:
- Externalizable继承自Serializable接口
- 需要重写writeExternal与readExternal方法
- 必须要提供一个无参构造
深入分析java序列化机制
- serialVersionUID的作用
- 序列化对象版本控制
- 如果增加了属性,可以向下兼容,如果删除了一些属性,就不兼容旧数据,InvalidClassException异常
serialVersionUID的生成方式
- 默认1L
- 根据类名,接口名,成员方法及属性等生成64位哈希字段
- 静态变量的序列化
静态变量不会序列化,因为静态变量在全局区,流里面没有写入静态变量
- Transient关键字的作用
Transient关键字的作用是控制变量的序列化,在变量前加上该关键字,可以阻止变量被序列化到文件中,transient变量的值被设为初始值
- 深度克隆
- 浅拷贝:使用一个已知实例对新创建实例的成员变量逐个赋值
- 深拷贝:当一个类的拷贝构造方法,不仅要复制对象的所有非引用成员变量值,还要为引用类型的成员变量创建创建新的实例,并且初始化为形式参数实例值.
先定义一个工具类
public class CloneUtils {
public static <T extends Serializable> T clone(T obj){
T cloneObj=null;
try {
//写入字节流
ByteArrayOutputStream out=new ByteArrayOutputStream();
ObjectOutputStream obs=new ObjectOutputStream(out);
obs.writeObject(obj);
obs.close();
//分配内存,写入原始对象,生成新对象
ByteArrayInputStream ios=new ByteArrayInputStream(out.toByteArray());
ObjectInputStream ois=new ObjectInputStream(ios);
//返回生成的新对象
cloneObj =(T)ois.readObject();
ois.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return cloneObj;
}
}
定义一个Person类
public class Person implements Serializable{
private static final long serialVersionUID=123456L;
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static long getSerialversionuid() {
return serialVersionUID;
}
@Override
public String toString() {
return "Person [name=" + name + "]";
}
}
Test类
public class Test {
public static void main(String[] args) {
Person person1=new Person("张三");
Person person2=CloneUtils.clone(person1);
person2.setName("李四");
Person person3=CloneUtils.clone(person1);
person3.setName("王五");
System.out.println(person1);
System.out.println(person2);
System.out.println(person3);
}
}
结果是
Person [name=张三]
Person [name=李四]
Person [name=王五]
扩展
AVRO,apche提供的一套序列化工具,MapReduce就使用了AVRO