序列化和反序列化

tim-qtp...大约 5 分钟Java基础序列化反序列化

主要是解决网络通信中对象传输的问题,网络传输的数据必须是二进制的,但是在java中都是对象,是没办法传输对象的!

序列化:将Java对象转化成可传输的字节序列格式(字节流、JSON、xml),以便于传输和存储

反序列化:将字节序列化数据,里面的描述信息和状态,转化成Java对象的过程

serialVersionUlD又有什么用?

private static final long serialVersionUID 1L;

经常会看到这样的代码,这个ID其实就是用来验证序列化的对象和反序列化对应的对象的心是否是一致的。

所以这个ID的数字其实不重要,无论是1L还是idea自动生成的,只要序列化的时候对象的serialVersionUID和反序列化的时候对象的serialVersionUlD一致的话就行。

如果没有显式指定serialVersionUlD,则编译器会根据类的相关信息自动生成一个。

所以如果你没有定义一个serialVersionUlD然后序列化一个对象之后,在反序列化,之前把对象的类的结构改了,比如增加了一个成员变量,则此时的反序列化会失败。

因为类的结构变了,所以serialVersionUlD就不一致了。所以serialVersionUlD就是起验证作用。

Java序列化不包含静态变量

简单地说就是序列化之后存储的内容不包含静态变量的值,看一下下面的代码就很清晰了。

public class Test implements Serializable {
    private static final long serialVersionUID = 1L; // 序列化版本号
    public static int yes = 1; // 静态变量

    public static void main(String[] args) {
        try {
            // 序列化
            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("tim的無級攻略"));
            out.writeObject(new Test()); // 将 Test 对象写入文件
            out.close();

            // 修改静态变量的值
            Test.yes = 2;

            // 反序列化
            ObjectInputStream oin = new ObjectInputStream(new FileInputStream("tim的無級攻略"));
            Test t = (Test) oin.readObject(); // 从文件中读取对象
            oin.close();

            // 输出静态变量的值
            System.out.println(t.yes); // 输出 2,而不是 1
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

解释一下序列化的过程和作用?

第一步,实现 Serializable 接口。

public class Person implements Serializable {
    private String name;
    private int age;

    // 省略构造方法、getters和setters
}

第二步,使用 ObjectOutputStream 来将对象写入到输出流中。

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.ser"));

第三步,调用 ObjectOutputStream 的 writeObject 方法,将对象序列化并写入到输出流中。

Person person = new Person("沉默王二", 18);
out.writeObject(person);

还有一种方法是使用Jackson序列化

Jackson是一个流行的Java库,用于处理JSON数据格式。它提供了简单而强大的API来实现Java对象与JSON之间的相互转换。

基本依赖配置

首先需要添加Jackson依赖(Maven项目):

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.13.0</version> <!-- 使用最新版本 -->
</dependency>

基本序列化操作

1. 简单对象序列化
import com.fasterxml.jackson.databind.ObjectMapper;

public class JacksonSerialization {
    public static void main(String[] args) {
        ObjectMapper mapper = new JacksonSerialization();
        Person person = new Person("李四", 30, "lisi@example.com");
        
        try {
            // 将对象序列化为JSON字符串
            String jsonString = mapper.writeValueAsString(person);
            System.out.println(jsonString);
            
            // 将对象序列化到文件
            mapper.writeValue(new File("person.json"), person);
            
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

class Person {
    private String name;
    private int age;
    private String email;
    
    // 必须有无参构造器
    public Person() {}
    
    public Person(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }
    // 必须提供getter方法
    // setter方法可选
}
2. 反序列化操作
public class JacksonDeserialization {
    public static void main(String[] args) {
        ObjectMapper mapper = new ObjectMapper();
        String jsonString = "{\"name\":\"王五\",\"age\":25,\"email\":\"wangwu@example.com\"}";
        
        try {
            // 从JSON字符串反序列化
            Person person = mapper.readValue(jsonString, Person.class);
            System.out.println(person.getName());
            
            // 从文件反序列化
            Person fromFile = mapper.readValue(new File("person.json"), Person.class);
            System.out.println(fromFile.getEmail());
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

高级特性

1. 注解使用
import com.fasterxml.jackson.annotation.*;

@JsonIgnoreProperties(ignoreUnknown = true) // 忽略未知属性
class Person {
    @JsonProperty("user_name") // 自定义JSON属性名
    private String name;
    
    private int age;
    
    @JsonIgnore // 忽略此字段
    private String password;
    
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
    private Date birthDate;
    
    // 构造方法、getter和setter
}

提示

效果对比

  • 没有注解时 Jackson可能将Date序列化为时间戳(如 1640995200000),可读性差。
  • 添加注解后 日期会被格式化为易读的字符串(如 "2022-12-31"
2. 处理集合和复杂对象
// 序列化列表
List<Person> people = Arrays.asList(
    new Person("张三", 25),
    new Person("李四", 30)
);
String json = mapper.writeValueAsString(people);

// 反序列化列表
List<Person> peopleList = mapper.readValue(json, 
    new TypeReference<List<Person>>() {});
3. 配置ObjectMapper
ObjectMapper mapper = new ObjectMapper();
// 美化输出
mapper.enable(SerializationFeature.INDENT_OUTPUT);
// 允许单引号
mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
// 允许未知字段
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
4. 自定义序列化/反序列化
public class CustomSerializer extends JsonSerializer<Date> {
    @Override
    public void serialize(Date value, JsonGenerator gen, SerializerProvider provider) 
        throws IOException {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        String formattedDate = formatter.format(value);
        gen.writeString(formattedDate);
    }
}

// 使用自定义序列化器
SimpleModule module = new SimpleModule();
module.addSerializer(Date.class, new CustomSerializer());
mapper.registerModule(module);

性能优化

  1. 重用ObjectMapper:ObjectMapper是线程安全的,应该重用而不是每次创建新实例
  2. 使用JsonFactory:对于高性能场景,可以创建JsonFactory实例
  3. 使用流式API:处理大JSON时,使用JsonParser和JsonGenerator
JsonFactory factory = mapper.getFactory();
try (JsonParser parser = factory.createParser(new File("large.json"))) {
    while (parser.nextToken() != null) {
        // 流式处理
    }
}

与其他格式的序列化

Jackson不仅支持JSON,还支持其他数据格式:

<!-- 添加YAML支持 -->
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-yaml</artifactId>
    <version>2.13.0</version>
</dependency>

<!-- 添加XML支持 -->
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
    <version>2.13.0</version>
</dependency>

使用方式类似:

// YAML
ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory());
String yaml = yamlMapper.writeValueAsString(person);

// XML
ObjectMapper xmlMapper = new XmlMapper();
String xml = xmlMapper.writeValueAsString(person);

Jackson提供了强大而灵活的序列化功能,是Java生态系统中处理JSON的首选库之一。