文章目录
- 对象流
- 1、定义:
- 2、注意事项:
- 序列化和反序列化
- 1、定义:
- 2、序列化特点:
- 3、serialVersionUID
- 4、Transient关键字
- 5、ArrayList源码再研究
- 6、Serializable 接口
- 7、序列化实例
对象流
1、定义:
对象流:ObjectXXX 例:ObjectInputStream 字节输入流/ObjectOutputStream 字节输出流
例如:ObjectInputStream 、ObjectOutputStream
2、注意事项:
对象流使用时,自定义对象必须实现Serializable接口,否则会抛出java.io.NotSerializableException
序列化和反序列化
1、定义:
序列化是一种对象持久化的手段:指将对象转换为字节流的过程
反序列化是根据字节流回复对象的过程
2、序列化特点:
1、在java中,只要一个类实现了java.io.Serializable接口,那么他就可以被序列化,这是因为,在序列化操作过程中会对类型进行检查,要求被序列化的类必须属于Enum、Array和Serializable类型其中的任何一种
2、通过ObjectOutputStream和ObjectInputStream对对象进行序列化及反序列化
3、虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致
一个非常重要的一点是两个类的序列化ID是否一致(就是 private static final long serialVersionUID)
4、序列化并不保存静态变量 (假如类中的变量是静态的,则不会做出相应的序列化)
5、要想将父类对象也序列化,就需要让父类也实现Serial接口
6、Transient关键字的作用是控制变量的序列化,在变量声明的前面加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被替代 如:int 0,对象型是null
7、transient的作用:服务器端给客户端发送序列化对象数据,对象中有一些数据是敏感的,比如密码字符串等,希望对该密码字段在序列化时,进行加密,而客户端如果拥有解密的秘钥,只有在客户端进行反序列化时,才可以对密码进行读取,这样可以一定程度保证序列化安全
8、在类中增加writeObject和readObject方法可以实现自定义序列化策略
3、serialVersionUID
作用:(用来表明 串行化版本统一标识符)
有两种生成方式:
1、一个是默认的1L
2、另一种是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段。UID的默认值依赖JAVA编译器,对同一个类,不同编译器编译可能导致UID不同
显示定义UID的作用:
1、确保不同版本有不同的UID
2、序列化一个类实例,更改一个字段或者添加一个字段,不设置UID,任何更改导致无法反序列化实例,并抛出异常,如果添加UID,反序列化旧实例,新增加或者 更改字段被设为初始值,对象为null
4、Transient关键字
**Transient关键字的作用:**是控制变量的序列化,在变量声明的前面加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被替代 如:int 0,对象型是null
**transient的使用场景:**服务器端给客户端发送序列化对象数据,对象中有一些数据是敏感的,比如密码字符串等,希望对该密码字段在序列化时,进行加密,而客户端如果拥有解密的秘钥,只有在客户端进行反序列化时,才可以对密码进行读取,这样可以一定程度保证序列化安全
5、ArrayList源码再研究
在ArrayList中,element被transient修饰,为什么还能反序列化呢?
(自定义的writeObject 和 readObject 方法)
在序列化中,如果序列化的类中定义了writeObject 和 readObject 方法
虚拟机会试图调用对象类里的 writeObject 和 readObject 方法,进行用户自定义的序列化和反序列化 如果没有这样的方法,则默认调用ObjectOutputStream 的 defaultWriteObject 方法以及用户自定义的writeObject 和 readObject 方法可以允许用户控制序列化的过程,比如可以在序列化的过程中动态的改变序列化的数值
增加writeObject方法的跟踪:
writeObject — writeObject0— writeOrdinaryObject—writeSerialData—invokeWriteObject
通过反射来调用ArrayList中的writeObject和readObject方法
transient修饰element的作用:
假如 ArrayList是100个空间,add了一个数据,还剩99个空间为null,直接对element进行序列化,真正有用的只有一个,效率太低,源码按需序列化(size)
6、Serializable 接口
Serializable 接口是一个空接口,是如何保证序列化和反序列化
在writeObject()-》writeObject0()
if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum<?>) obj, desc, unshared);
} else if **(obj instanceof Serializable)** {
writeOrdinaryObject(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
7、序列化实例
创建一个person类
import java.io.Serializable;
/**
*
*/
public class Person implements Serializable {
private static final long serialVersionUID = 2664435368610967824L;
//private static final long serialVersionUID=1800778823450372341L;
private int id;
private String name;
public Person(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
读写person类
import java.io.*;
import java.util.ArrayList;
public class ObjectDome {
public static void main(String[] args) {
String path="C:\\Users\\Administrator\\Desktop\\Test.txt";
writeObject(path );
readObject(path );
}
//读对象操作
public static void readObject(String path){
try {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(path));
ArrayList <Integer > person =(ArrayList )objectInputStream .readObject() ;
//System.out.println(person.getId() +person .getName() );
System.out.println(person.toString() );
objectInputStream .close() ;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//写对象操作
public static void writeObject(String path){
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(path) );
//Person person = new Person(1, "zhangsan");
ArrayList <Integer >integers =new ArrayList<>() ;
integers .add(1);
objectOutputStream .writeObject(integers );
objectOutputStream .close() ;
} catch (IOException e) {
e.printStackTrace();
}
}
//
}
控制台输出写入的【1】