java序列化

本文通过实例演示了Java中对象序列化的具体过程,包括如何序列化一个对象,并探讨了transient关键字的作用及static字段在序列化过程中的表现。此外,还介绍了serialVersionUID的重要性及其在版本一致性校验中的作用。
废话不多说,看实例:

先新建一个java类:

class ObjectSearilizeTest{
    private long id =1234;
    private int num;
    private String userName;
    public static int TEST_CONTANT =1234;
    private transient String cannotSee;

    public void setId(long id) {
        this.id = id;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public void setCannotSee(String cannotSee) {
        this.cannotSee = cannotSee;
    }

    public long getId() {
        return id;
    }

    public int getNum() {
        return num;
    }

    public String getUserName() {
        return userName;
    }

    public String getCannotSee() {
        return cannotSee;
    }
}

使用ObjectInput(Output)Stream序列化对象并反序列化之:

public static void main(String args[]){
        OutputStream fileStream = null;
        try {
            fileStream = new FileOutputStream(file);
            ObjectOutputStream outputStream = new ObjectOutputStream(fileStream);
            ObjectSearilizeTest test = new ObjectSearilizeTest();
            test.setId(1234L);
            test.setCannotSee("can u see me");
            test.setNum(1);
            test.setUserName("songjing");
            outputStream.writeObject(test);
            outputStream.close();
            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(file)) ;
            ObjectSearilizeTest.TEST_CONTANT =12345;
            ObjectSearilizeTest outTest = (ObjectSearilizeTest) inputStream.readObject();
            System.out.println("outTest.getId() = " + outTest.getId());
            System.out.println("outTest.getUserName() = " + outTest.getUserName());
            System.out.println("outTest.getCannotSee() = " + outTest.getCannotSee());
            System.out.println("outTest.getNum() = " + outTest.getNum());
            System.out.println("ObjectSearilizeTest.TEST_CONTANT = " + ObjectSearilizeTest.TEST_CONTANT);
        } catch (Exception e) {
            System.out.println("e = " + e);
            //todo
        }
    }

你将看到的输出是:

e = java.io.NotSerializableException: com.taobao.cms.ObjectSearilizeTest

好,问题很明显,没有实现序列化接口,做如下改动即可:

class ObjectSearilizeTest implements Serializable

再看输出结果:

outTest.getId() = 1234
outTest.getUserName() = songjing
outTest.getCannotSee() = null
outTest.getNum() = 1
ObjectSearilizeTest.TEST_CONTANT = 12345

首先,可以看出带transient关键字的属性字段不会被序列化,输入为null而非“can u see me “

其次,static字段的TEST_CONTANT输出是12345而非1234,可见static字段在序列化时的特殊之处;

另外序列化对象如果添加唯一id:

private static final long serialVersionUID =1281778809001846002L;

可以校验版本一致性问题,以下是关于序列化id的一些文字解释,转载自http://sharajava.iteye.com/blog/102886

简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。

当实现java.io.Serializable接口的实体(类)没有显式地定义一个名为serialVersionUID,类型为long的变量时,Java序列化机制会根据编译的class自动生成一个serialVersionUID作序列化版本比较用,这种情况下,只有同一次编译生成的class才会生成相同的serialVersionUID 。

如果我们不希望通过编译来强制划分软件版本,即实现序列化接口的实体能够兼容先前版本,未作更改的类,就需要显式地定义一个名为serialVersionUID,类型为long的变量,不修改这个变量值的序列化实体都可以相互进行串行化和反串行化。 

     另外,关于两个特殊的stream可参看官方api说明:

http://download.oracle.com/javase/6/docs/api/java/io/ObjectOutputStream.html

http://download.oracle.com/javase/6/docs/api/java/io/ObjectInputStream.html





### 序列化的含义 序列化指将堆内存中的 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; // 类的成员和方法 } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值