Java安全基础——序列化/反序列化

前言:在Java中实现对象反序列化非常简单,实现java.io.Serializable(内部序列化)或java.io.Externalizable(外部序列化)接口即可被序列化,其中java.io.Externalizable接口只是实现了java.io.Serializable接口

1:序列化对象和反序列化对象(两种方式)

(1)使用java.io.Serializable进行序列化

1:将类实现Serializable这个接口

2:设置serialVersionUID值

3:使用ObjectOutputStream进行序列化

4:使用ObjectInputStream进行反序列化

Text类:

复制代码
public class Text1 implements Serializable {
    @Serial
    private static final long serialVersionUID = 3842394723984792L;
    public String text="111";
    public int number;

    public Text1(){
    }

    public void text1(){
        System.out.println("我是text1中的方法");
    }

    @Override
    public String toString() {
        return "Text1{" +
                "text='" + text + '\'' +
                ", number=" + number +
                '}';
    }
}

Main类:

复制代码
 //先字节流建立连接
 FileOutputStream out = new FileOutputStream("1.txt");
 //转换成序列化流
 ObjectOutputStream oos = new ObjectOutputStream(out);
 //写入对象
 oos.writeObject(new Text1());
 //关流
 oos.close();

 //先字节流建立连接
 FileInputStream fis = new FileInputStream("1.txt");
 //转换成序列化流
 ObjectInputStream ois = new ObjectInputStream(fis);
 //读取对象
 Text1 o = (Text1)ois.readObject();
 //关流
 ois.close();

 //打印对象
System.out.println(o);

(2)使用java.io.Externalizable进行序列化

1:将类实现Serializable这个接口

2:重写里面的两个方法

3:设置serialVersionUID值

4:使用ObjectOutputStream进行序列化

5:使用ObjectInputStream进行反序列化

Text类:

复制代码
public class Text1 implements Externalizable {
    @Serial
    private static final long serialVersionUID = 3842394723984792L;
    public String name="张三";
    public int number=888888;

    public Text1(){
    }

    public void text1(){
        System.out.println("我是text1中的方法");
    }

    @Override
    public String toString() {
        return "Text1{" +
                "name='" + name + '\'' +
                ", number=" + number +
                '}';
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
            out.writeObject(name);
            out.writeInt(number);

    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
          name = (String) in.readObject();
          number = in.readInt();
    }
}

Main类:

复制代码
 //先字节流建立连接
 FileOutputStream out = new FileOutputStream("1.txt");
 //转换成序列化流
 ObjectOutputStream oos = new ObjectOutputStream(out);
 //写入对象
 oos.writeObject(new Text1());
 //关流
 oos.close();

 //先字节流建立连接
 FileInputStream fis = new FileInputStream("1.txt");
 //转换成序列化流
 ObjectInputStream ois = new ObjectInputStream(fis);
 //读取对象
 Text1 o = (Text1)ois.readObject();
 //关流
 ois.close();

 //打印对象
System.out.println(o);

2:自定义序列化(writeObject)和反序列化(readObject)

当我们对DeserializationTest类进行序列化操作时,会自动调用(反射调用)该类的writeObject(ObjectOutputStream oos)方法,对其进行反序列化操作时也会自动调用该类的readObject(ObjectInputStream)方法,也就是说我们可以通过在待序列化或反序列化的类中定义readObject和writeObject方法,来实现自定义的序列化和反序列化操作,当然前提是,被序列化的类必须有此方法,并且方法的修饰符必须是private,如果该类没有重写,默认调用默认的序列化方法

自定义序列化的使用场景:

1:需要手动处理使用transient关键字不能自动序列化字段

2:字段需要加密后序列化

3:还用更复杂的一些场景

Text类:

复制代码
public class Text1 implements Serializable {
    @Serial
    private static final long serialVersionUID = 3842394723984792L;
    transient public String name="张三";
    public int number=888888;

    public Text1(){
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
            out.defaultWriteObject();
            out.writeObject(name);
    }
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
           in.defaultReadObject();
           name = (String) in.readObject();
    }

    @Override
    public String toString() {
        return "Text1{" +
                "name='" + name + '\'' +
                ", number=" + number +
                '}';
    }

}

Main类:

复制代码
 //先字节流建立连接
 FileOutputStream out = new FileOutputStream("1.txt");
 //转换成序列化流
 ObjectOutputStream oos = new ObjectOutputStream(out);
 //写入对象
 oos.writeObject(new Text1());
 //关流
 oos.close();

 //先字节流建立连接
 FileInputStream fis = new FileInputStream("1.txt");
 //转换成序列化流
 ObjectInputStream ois = new ObjectInputStream(fis);
 //读取对象
 Text1 o = (Text1)ois.readObject();
 //关流
 ois.close();

 //打印对象
System.out.println(o);

3:使用反序列化绕过构造方法创建对象

除此之外,反序列化类对象是不会调用该类构造方法的,因为在反序列化创建类实例时使用了sun.reflect.ReflectionFactory.newConstructorForSerialization创建了一个反序列化专用的Constructor(反射构造方法对象),使用这个特殊的Constructor可以绕过构造方法创建类实例(前面章节讲sun.misc.Unsafe 的时候我们提到了使用allocateInstance方法也可以实现绕过构造方法创建类实例)

步骤:

1:获取sun.reflect.ReflectionFactory对象

2:使用反序列化方式获取某类的构造方法

3:实例化某类的对象

复制代码
// 获取sun.reflect.ReflectionFactory对象
ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
// 使用反序列化方式获取Text1类的构造方法
Constructor<?> constructor = reflectionFactory.newConstructorForSerialization(Text1.class, Object.class.getConstructor());
//实例化Text1的对象
Text1 o = (Text1)constructor.newInstance();
System.out.println(o);
相关推荐
CoderYanger2 小时前
Java EE 进阶:8.MyBatis 操作数据库(入门)(第二弹)
java·数据库·spring boot·spring·面试·java-ee·mybatis
CQU_JIAKE5 小时前
11.7[高体】
java·后端·spring
apcipot_rain6 小时前
C++转Java日记(04)
java·开发语言·c++
特立独行的猫A6 小时前
我给女儿写了个打字练习:一边敲键盘,一边把小学英语单词记牢
java·后端·python
云和数据.ChenGuang7 小时前
LangGraph 异步 Node 的设计与写法实践
开发语言·人工智能·python·机器学习·php
憧憬成为java架构高手的小白8 小时前
LangGraph4j 实战(下)
java
天诰8 小时前
SA-K-11 检索会消失吗与四个未解问题
java·服务器·人工智能·python·机器学习·ai编程
蜗牛一步步往上爬10 小时前
第十六章 C++语言在深度学习中的应用
开发语言·c++·深度学习
这个昵称不容易12 小时前
SpringBoot「衣依」服装销售平台:144个Java文件180个接口,从挑衣服到收货评价整条链路都在
java·springboot·毕设
计算机毕设定制辅导-无忧学长12 小时前
《基于微信小程序的居家医疗陪护预约系统设计与实现》
java·spring boot·微信小程序·uniapp·居家医疗陪护预约系统