-
类通过实现 java.io.Serializable 接口以启用其序列化功能。
序列化和反序列化的时候,如果对象没有实现序列化接口 (implement Serializable),会抛出NotSerializableException没有序列化异常。
类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
Serializable接口也叫标记型接口
要进行序列化和反序列化的类必须实现Serializable接口,就会给类添加一个标记
当我们进行序列化和反序列化的时候,就会检测类上是否有这个标记
有:就可以序列化和反序列化
没有:就会抛出 NotSerializableException异常
static关键字:静态关键字
静态优先于非静态加载到内存中(静态优先于对象进入到内存中)
对象中被static修饰的成员变量不能被序列化的,序列化的都是对象
private static int age;
oos.writeObject(new Person("小美女",18));
Object o = ois.readObject();
Person{name='小美女', age=0}
transient关键字:瞬态关键字
对象中被transient修饰成员变量,不能被序列化
与static唯一的区别就是该关键字修饰的不是静态变量,不是在类内加载时被加载进内存中,而是随着对象的创建而加载。
private transient int age;
oos.writeObject(new Person("小美女",18));
Object o = ois.readObject();
Person{name='小美女', age=0}
serialVersionHID 成员变量:序列化版本号
当.java文件编译成.class文件,便会给该类加上一个序列号
序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象 的发送者和接收者是否为该对象加载了与序列化兼容的类。如果接收者加载的该对象的类的 serialVersionUID 与对应的发送者的类 的版本号不同,则反序列化将会导致 InvalidClassException。可序列化类可以通过声明名为 "serialVersionUID" 的字段(该 字段必须是静态 (static)、最终 (final) 的 long 型字段)显式声明其自己的 serialVersionUID:
ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
如果可序列化类未显式声明 serialVersionUID,则序列化运行时将基于该类的各个方面计算该类的默认 serialVersionUID 值, 如“Java(TM) 对象序列化规范”中所述。不过,强烈建议所有可序列化类都显式声明 serialVersionUID 值,原因是计算默认的 serialVersionUID 对类的详细信息具有较高的敏感性,根据编译器实现的不同可能千差万别,这样在反序列化过程中可能会导致意外 的 InvalidClassException。因此,为保证 serialVersionUID 值跨不同 java 编译器实现的一致性,序列化类必须声明一个明确 的 serialVersionUID 值。还强烈建议使用 private 修饰符显示声明 serialVersionUID(如果可能),原因是这种声明仅应用于直 接声明类 -- serialVersionUID 字段作为继承成员没有用处。数组类不能声明一个明确的 serialVersionUID,因此它们总是具有默认 的计算值,但是数组类没有匹配 serialVersionUID 值的要求。
public class Person implements Serializable {
private static final long serialVersionHID = 1L;
private String name;
//private static int age;
private transient int age;
//private int age;
}
- **Serializable接口之所以称为标记型接口,**因为该接口内部没有任何方法
public interface Serializable {
}
- 对象序列化流: ObjectOutputStream
java.io.ObjectOutputStream extends OutputStream
ObjectOutputStream:对象的序列化流
作用:把对象以流(二进制字节)的方式写入到文件中保存
构造方法:
ObjectOutputStream(OutputStream out) 创建写入指定 OutputStream 的 ObjectOutputStream。
参数:
OutputStream out:字节输出流
特有的成员方法:
void writeObject(Object obj) 将指定的对象写入 ObjectOutputStream。
使用步骤:
1.创建ObjectOutputStream对象,构造方法中传递字节输出流
2.使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中
3.释放资源 close
- 对象反序列化流: ObjectInputStream
java.io.ObjectInputStream extends InputStream
ObjectInputStream:对象的反序列化流
作用:把文件中保存的对象,以流的方式读取出来使用
构造方法:
ObjectInputStream(InputStream in) 创建从指定 InputStream 读取的 ObjectInputStream。
参数:
InputStream in:字节输入流
特有的成员方法:
Object readObject() 从 ObjectInputStream 读取对象。
使用步骤:
1.创建ObjectInputStream对象,构造方法中传递字节输入流
2.使用ObjectInputStream对象中的方法readObject读取保存对象的文件
3.释放资源 (close)
4.使用读取出来的对象(打印)
readObject方法声明抛出了ClassNotFoundException(class文件找不到异常)
当不存在对象的class文件时抛出此异常
反序列化的前提:
1.类必须实现Serializable
2.必须存在类对应的class文件
✔✔✔测试序列化与反序列化是否能够破坏单例模式
public static void main(String[] args) throws IOException, ClassNotFoundException {
//序列化写对象
writeObject2File();
//反序列化读对象
Singleton singleton1 = readObject2File();
Singleton singleton2 = readObject2File();
//判断两个对象是否一样,是否破坏了单例模式
System.out.println(singleton1 == singleton2);
}
//序列化- 将对象转换为字节流文件
private static void writeObject2File() throws IOException {
Singleton instance = Singleton.getInstance();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\a.txt"));
oos.writeObject(instance);
oos.close();
}
//反序列化 - 将字节流文件转为对象
private static Singleton readObject2File() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\a.txt"));
Singleton singleton = (Singleton)ois.readObject();
return singleton;
}
测试结果为:false
☯☯☯解决办法
- 在序列化对象中增加一个readResolve()方法
public class Singleton implements Serializable {
//私有构造方法
private Singleton() {}
//定义一个静态内部类
private static class SingletonHolder {
//在内部类中声明并初始化外部类的对象
private static final Singleton INSTANCE = new Singleton();
}
//提供公共的访问方式
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
//当进行反序列化时,会自动调用该方法,将该方法的返回值直接返回
public Object readResolve() {
return SingletonHolder.INSTANCE;
}
}
- 对象输入流ObjectInputStream调用readObject()方法,底层源码有如下操作: