java.io.Serializable序列化与反序列化

在这里插入图片描述

  • 类通过实现 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) 创建写入指定 OutputStreamObjectOutputStream。
        参数:
            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()方法,底层源码有如下操作:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值