[Java/序列化] Java对象的序列化
概述:Java对象的序列化
Java基础:对象的序列化
什么是对象的序列化(Serialization)
- “序列化”是一种把内存中Java对象的状态转化成字节流的机制,“反序列”是其相反的过程,把序列化成的字节流用来在内存中重新创建一个实际的Java对象。
这个机制被用来“持久化”对象。
通过对象序列化,可以方便的实现对象的持久化储存或在网络上的传输。
大致的过程如下图所示:

- 
对象被转换成“字节流”后可以存入文件、内存,亦或者是数据库内进行持久化保存。然后,通过“反序列化”可以把“字节流”转换成实际的Java对象。 
- 
对象的序列化是与平台无关的。 
因此,在一个平台上被“序列化”的对象可以很容易的在另一个不相同的平台上给“反序列化”出来。
序列化的通俗解释
- 假设我有一条叫rex的小狗,我想把它寄养给远方亲戚。
那么问题来了,小狗是活的,是会呼吸的一种动物,我怎样通过电话线来传达一个对象呢?
我总不能直接把小狗塞进电话话筒里吧,所以,我不得不把这个小狗转换成另一种形式来代表它,从而可以通过电话传输。
换句话说,我们就可以对小狗进行序列化操作,并且通过电话线把序列化的信息给发送过去:

这就是一个完美的小狗代表,一个被序列化的小狗。
序列化意味着把我的小狗对象转化成了另一种代表形式——一个JSON对象。
这个对象可以通过电话线以一串0,1,0,1......的二进制串来传播。电话线另一端的远方亲戚可以通过把这一串0,1,0,1的二进制串翻译成一个JSON对象——也就是我的小狗。
因此,我的小狗的信息就完美的通过电话线使用二进制符号传播出去了。

序列化的优点
- 可以“持久化保存”一个对象。
- 可以通过网络方便地传送一个对象。

如何实现序列化?
- 总结
如果父类已经实现了Serializable序列化接口的话,其子类就不用再实现这个接口了,但是反过来就不成立了。
只有非静态的数据成员才会通过序列化操作被序列化。
静态(Static)数据成员和被标记了transient的数据成员在序列化的过程中将不会被序列化,因此,如果你不想要保存一个非静态的数据成员你就可以给标记上transient。
当一个对象被反序列化时,这个对象的构造函数将不会在被调用的。
需要实现序列化的对象如果内部引用了一个没有实现序列化接口的对象,在其序列化过程中将会发生错误
- step1 实现Serializable接口
为了使一个Java对象能够被“序列化”,我们必须让这个对象实现
java.io.Serializable接口。
我们打开java.io.Serializable.java这个文件,看一看这个接口中定义了哪些内容:
public interface Serializable {
}
可以发现,在此接口中没有定义任何的方法。所以,此接口是一个标识接口。表示一个类具备了“可序列化”的能力。
import java.io.Serializable;
public class Person implements Serializable {
    private String name;
 
    public Person(String name) {
        this.name = name;
    }
}
上面就是一个实现了
Serializable接口的Person类对象,这个对象现在就已经具备了“可序列化”的能力。
所以,此类的对象是可以经过“二进制数据流”进行传输的。
而如果想要完成对象的输入或者输出,还必须依靠对象输出流(ObjectOutputStream)和对象输入流(ObjectInputStream)。
- step*...
最佳实践
ObjectSerializationUtils
ObjectSerializationUtils
import lombok.extern.slf4j.Slf4j;
import java.io.*;
/**
 * Java对象序列化工具
 * @updateTime 2025.6.29 09:59
 */
@Slf4j
public class ObjectSerializationUtils {
    /**
     * 将java对象序列化到 outputStream
     * @param dataObject
     *   注意, dataObject 所属类及其非 transient 修饰的成员,必须实现 {@link Serializable } 接口,否则会报 {@link java.io.NotSerializableException } 错
     * @param outputStream 输出流(由调用方自己负责回收/关闭资源,本方法不负责)
     *   eg: FileOutputStream fileOut = new FileOutputStream("employees.java-data-object.ser");
     * @param <T>
     */
    public static <T> void serialize(T dataObject, OutputStream outputStream){
        String errorMessage = null;
        ObjectOutputStream out = null;
        try {
            out = new ObjectOutputStream(outputStream);
            out.writeObject(dataObject);
            out.close();
        } catch (IOException exception) {
            errorMessage = String.format("Serialize the java data object(class:%s) to output stream fail!", dataObject.getClass().getCanonicalName());
            log.error(errorMessage + " exception:", exception);
            throw new RuntimeException(errorMessage, exception);
        }
    }
    /**
     * 从 inputStream 反序列化为 java对象
     * @param clazz
     *   注意, clazz 类及其非 transient 修饰的成员,必须实现 {@link Serializable } 接口
     * @param inputStream 输入流(由调用方自己负责回收/关闭资源,本方法不负责)
     *   eg: FileInputStream fileIn = new FileInputStream("student..java-data-object.ser");
     * @return
     * @param <T>
     */
    public static <T> T deserialize(Class<T> clazz, InputStream inputStream) {
        String errorMessage = null;
        ObjectInputStream in = null;
        T dataObject = null;
        try {
            in = new ObjectInputStream(inputStream);
            dataObject = (T) in.readObject();
            in.close();
        } catch (IOException | ClassNotFoundException exception) {
            errorMessage = String.format("Serialize the java data object(class:%s) to output stream fail!", dataObject.getClass().getCanonicalName());
            log.error(errorMessage + " exception:", exception);
            throw new RuntimeException(errorMessage, exception);
        }
        return dataObject;
    }
}
ObjectSerializationUtilsTest
import com.xxx.sdk.pojo.someip.entity.config.SerializationParameterConfiguration;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/**
 * @updateTime 2025.6.29 10:00
 */
@Slf4j
public class ObjectSerializationUtilsTest {
    @SneakyThrows
    @Test
    public void testSerialize() {
        SerializationParameterConfiguration serializationParameterConfiguration = SerializationParameterConfiguration.getDefault();
        log.info( "{}", serializationParameterConfiguration );
        File file = new File( "/tmp/xxx-sdk/serializationParameterConfiguration.ser" );
        FileOutputStream fos = new FileOutputStream( file );
        ObjectSerializationUtils.serialize( serializationParameterConfiguration, fos );
        fos.close();
        log.info("write the java data object to the output stream!filePath:{}", file.getAbsolutePath());
    }
    @SneakyThrows
    @Test
    public void testDeserialize() {
        File file = new File( "/tmp/xxx-sdk/serializationParameterConfiguration.ser" );
        FileInputStream fis = new FileInputStream( file );
        SerializationParameterConfiguration serializationParameterConfiguration = ObjectSerializationUtils.deserialize( SerializationParameterConfiguration.class , fis );
        fis.close();
        log.info( "{}", serializationParameterConfiguration );
        log.info("Read the java data object from the input stream!filePath:{}", file.getAbsolutePath());
    }
}
FAQ for Java对象的序列化
Q:如何解决 java.io.NotSerializableException: 对象不支持序列化问题?
问题描述
- 当程序尝试将一个对象写入磁盘或通过网络传输时,遇到以下异常:
java.io.NotSerializableException: <ClassName>
这个异常通常出现在尝试将不支持序列化的对象写入
ObjectOutputStream序列化或者通过ObjectInputStream反序列化时。
问题分析
报错原因
NotSerializableException 异常的根本原因是某个对象的类没有实现 Serializable 接口,或者它的成员变量中包含没有实现 Serializable 接口的对象。
- 类未实现 Serializable 接口
只有实现了 Serializable 接口的类才能进行序列化。如果一个类没有实现该接口,尝试序列化该类的对象时会抛出 NotSerializableException。
- 类成员未实现 Serializable 接口
如果一个类的成员对象没有实现 Serializable 接口,那么即使外部类实现了 Serializable,也无法序列化该对象。
- transient关键字的使用
如果在类中使用 transient 修饰某个成员变量,那么该成员变量将不会被序列化。它的值将会被忽略,但如果该变量的类型没有实现 Serializable,也会导致该异常。
解决思路
要解决 NotSerializableException 异常,通常有以下几种方法:
- 确保类实现 Serializable 接口
让需要序列化的类实现 java.io.Serializable 接口。
- 确保类的成员变量也实现 Serializable 接口
如果类中包含其他对象,确保这些对象的类也实现了 Serializable 接口。
- 使用 transient关键字排除不需要序列化的成员变量
如果类中有某些成员变量不需要序列化,可以使用 transient 关键字排除它们。
- 避免使用无法序列化的外部库类
如果类的成员变量使用了外部库中的类,而该类没有实现 Serializable,则需要考虑替换或手动处理序列化过程。
X 参考文献
 
    本文链接: https://www.cnblogs.com/johnnyzen
关于博文:评论和私信会在第一时间回复,或直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
日常交流:大数据与软件开发-QQ交流群: 774386015 【入群二维码】参见左下角。您的支持、鼓励是博主技术写作的重要动力!

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号