Java 序列化

Java序列化详解

Java 序列化有两种方式.

1. 实现Serializable 接口

// 1: implements serializable
public class Person implements Serializable {
    int age;
    String name;
    transient String hobby = "string";

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
        this.hobby = "hobby";
    }

    @Override
    public String toString() {
        return String.format("age: %d name %s %s", age, name, hobby);
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {

        String fileName = "o.out";
        Person p = new Person(22, "hello");
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(fileName));
        out.writeObject(p);
        out.close();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream(fileName));
        Person p2 = (Person) in.readObject();
        System.out.println(p2);
    }
}

如果要让Serializable 像 Externalizable 一样实现操作transient属性,可以添加 readObject 和 writeObject 方法,如下:

import java.io.*;

// 1: implements serializable
public class Person implements Serializable {
    int age;
    String name;
    transient String hobby ;

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
        this.hobby = "ping pong";
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        hobby = (String) in.readObject();
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeObject(hobby);
    }

    @Override
    public String toString() {
        return String.format("age: %d name %s %s", age, name, hobby);
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {

        String fileName = "o.out";
        Person p = new Person(22, "hello");
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(fileName));
        out.writeObject(p);
        out.close();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream(fileName));
        Person p2 = (Person) in.readObject();
        System.out.println(p2);
    }
}

2. 实现 Externalizable 接口

注意点:

a> 实现Externalizable 的类的构造函数必须是public的

import java.io.*;

class Blip1 implements Externalizable{

    public Blip1(){

    }
    public void writeExternal(ObjectOutput out) throws IOException {

    }

    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

    }
}

class Blip2 implements Externalizable{

    // pay attention: the constructor must be public
    public Blip2(){

    }
    public void writeExternal(ObjectOutput out) throws IOException {

    }

    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

    }
}


public class Main {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Blip1 b1 = new Blip1();
        Blip2 b2 = new Blip2();

        final String fileName = "blips.out";
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(fileName));
        out.writeObject(b1);
        out.writeObject(b2);
        out.close();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream(fileName));
        b1 = (Blip1) in.readObject();
        b2 = (Blip2) in.readObject();
    }
}

总结,两者的区别:

1. Serializable 在 readObject 的时候不需要调用类的默认构造函数,以此如果有transitent 属性,就会是null,数字就是0.

2. Externalizable 在readObject 的时候必须要调用类的构造函数,以此要求类的默认的构造函数必须要是public的.

3. 如果不想讲一个类里面的属性序列化,在Serializable 里面可以加上 transient 关键字修饰这个属性,而在Externalizable 里面可以在 writeExternal 和 readExternal 方法中不操作这个属性, 不能在Externalizable 中使用transient 关键字.

4. 如果是将序列化对象写入到同一个文件中,那么对象间的引用关系还是存在的,如果是不同的文件中,那么对象间的引用关系就丢失了.(参考 https://www.ibm.com/developerworks/cn/java/j-lo-serial/index.html )

5. java 无法序列化 类中的静态属性,如果只能自己写一些方法来解决这个问题.


### 序列化的含义 序列化指将堆内存中的 Java 对象数据,通过某种方式存储到磁盘文件中,或者传递给其他网络节点(网络传输),即将对象转化为二进制,用于保存或网络传输。反序列化则是从 IO 流中恢复对象[^1][^4]。 ### 序列化的意义 序列化机制允许将实现序列化Java 对象转换为字节序列,这些字节序列可以保存在磁盘上,或通过网络传输,以达到以后恢复成原来的对象。序列化机制使得对象可以脱离程序的运行而独立存在[^1]。 ### 序列化的使用场景 所有可在网络上传输的对象都必须是可序列化的,比如 RMI(remote method invoke,即远程方法调用),传入的参数或返回的对象都是可序列化的,否则会出错;所有需要保存到磁盘的 Java 对象都必须是可序列化的。通常建议程序创建的每个 JavaBean 类都实现 Serializable 接口[^1]。 ### 序列化实现的方式 #### Serializable 接口 - **普通序列化**:实现 Serializable 接口后,可使用 ObjectOutputStream 将对象写入流,使用 ObjectInputStream 从流中读取对象。 ```java import java.io.*; class Person implements Serializable { String name; int age; public Person(String name, int age) { this.name = name; this.age = age; } } public class SerializationExample { public static void main(String[] args) { Person person = new Person("John", 30); try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) { oos.writeObject(person); } catch (IOException e) { e.printStackTrace(); } try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) { Person deserializedPerson = (Person) ois.readObject(); System.out.println(deserializedPerson.name + " " + deserializedPerson.age); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } } ``` - **成员是引用的序列化**:若类的成员是引用类型,该引用类型也必须是可序列化的,否则当前类无法实现序列化。 - **同一对象序列化多次的机制**:Java 序列化算法会保证同一对象多次序列化时,不会重复序列化对象的内容,而是只记录对象的引用。 - **Java 序列化算法潜在的问题**:例如序列化版本号不匹配、性能问题等。 - **可选的自定义序列化**:可通过实现 `writeObject` 和 `readObject` 方法来自定义序列化和反序列化过程。 #### Externalizable 接口 Externalizable 接口强制自定义序列化,需要实现 `writeExternal` 和 `readExternal` 方法。 ```java import java.io.*; class Employee implements Externalizable { String name; int id; public Employee() {} public Employee(String name, int id) { this.name = name; this.id = id; } @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(name); out.writeInt(id); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { name = (String) in.readObject(); id = in.readInt(); } } ``` ### 序列化版本号 serialVersionUID serialVersionUID 用于确保序列化和反序列化时类的版本一致性。若没有显式定义 serialVersionUID,Java 会根据类的结构自动生成一个。当类的结构发生变化时,可能会导致 serialVersionUID 改变,从而在反序列化时抛出 `InvalidClassException`。建议显式定义 serialVersionUID。 ```java import java.io.Serializable; class MyClass implements Serializable { private static final long serialVersionUID = 1L; // 类的成员和方法 } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值