楼主: 76r2Icm0zv4B
39 0

[互联网] Java安全基础——序列化 反序列化 [推广有奖]

  • 0关注
  • 0粉丝

等待验证会员

学前班

80%

还不是VIP/贵宾

-

威望
0
论坛币
0 个
通用积分
0
学术水平
0 点
热心指数
0 点
信用等级
0 点
经验
30 点
帖子
2
精华
0
在线时间
0 小时
注册时间
2018-6-26
最后登录
2018-6-26

楼主
76r2Icm0zv4B 发表于 2025-12-1 12:27:34 |AI写论文

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币

前言:在 Java 中,对象的反序列化操作十分便捷,只需让类实现特定接口即可完成序列化与反序列化过程。常见的做法是实现

java.io.Serializable(内部序列化)

java.io.Externalizable(外部序列化)

接口。值得注意的是,

java.io.Externalizable

接口本质上是对

java.io.Serializable

接口的一种扩展形式。

1. 对象的序列化与反序列化(两种实现方式)

(1)基于 java.io.Serializable 的序列化机制

该方式为最常用的序列化手段,具体步骤如下:

  1. 使目标类实现 Serializable 接口;
  2. 声明并设置
  3. serialVersionUID
  4. 利用 ObjectOutputStream 执行对象序列化;
  5. 通过 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. 让类实现 Externalizable 接口;
  2. 重写接口中规定的两个核心方法;
  3. 设定
  4. serialVersionUID
  5. 使用 ObjectOutputStream 进行序列化操作;
  6. 借助 ObjectInputStream 实现反序列化处理。

Text 类实现 Externalizable 示例:

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);
// 读取对象
serialVersionUID
// 先建立字节流连接
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);

自定义序列化与反序列化(writeObject 与 readObject)

当对某个类执行序列化操作时,Java 会通过反射机制自动调用该类中定义的特定方法。在序列化过程中,系统将查找并调用 writeObject 方法;而在反序列化时,则会自动触发 readObject 方法的执行。

因此,我们可以在需要进行序列化的类中手动添加这两个私有方法,以实现对序列化过程的精细化控制。需要注意的是,这些方法必须声明为 private,且其签名需严格符合规范,否则不会被 JVM 自动识别和调用。

DeserializationTest
writeObject(ObjectOutputStream oos)
readObject(ObjectInputStream)
readObject
writeObject
private,如果该类没有重写,默认调用默认的序列化方法

应用场景包括:

  • 处理被 transient 修饰但仍需保存的字段
  • 在序列化前对敏感数据进行加密,反序列化时再解密
  • 应对更复杂的序列化逻辑需求,如版本兼容、数据校验等

示例代码:Text1 类

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();  // 处理非 transient 字段
        out.writeObject(name);     // 手动写入 transient 字段
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();    // 恢复非 transient 字段
        name = (String) in.readObject();  // 手动恢复 transient 字段
    }

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

利用反序列化绕过构造函数创建对象

一个值得注意的特性是:**反序列化过程并不会调用类的构造方法**。这是因为 JVM 在反序列化时使用了底层机制,通过反射包中的特殊工厂生成实例。具体来说,它利用了 sun.reflect.ReflectionFactory 创建了一个专用于反序列化的“无构造调用”实例生成器。

这种方式与之前章节中提到的通过 Unsafe.allocateInstance() 直接分配内存创建对象类似,都可以实现跳过构造函数的目的。

sun.reflect.ReflectionFactory.newConstructorForSerialization
Constructor(反射构造方法对象)
Constructor
sun.misc.Unsafe
allocateInstance

实现步骤如下:

  1. 获取 ReflectionFactory 实例
  2. 通过工厂方法生成目标类的序列化专用构造器
  3. 使用该构造器创建对象实例
// 获取 ReflectionFactory 实例
ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();

// 获取 Text1 类用于反序列化的构造器(继承自 Object 的构造器)
Constructor<?> constructor = reflectionFactory.newConstructorForSerialization(
    Text1.class,
    Object.class.getConstructor()
);

// 通过 newInstance 实例化对象(不调用 Text1 的构造函数)
Text1 o = (Text1) constructor.newInstance();

// 打印结果
System.out.println(o);
二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:Java jav reflection exception implement

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
加好友,备注cda
拉您进交流群
GMT+8, 2026-2-19 10:31