欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > > 文章正文

ArrayList中字段serialVersionUID和序列化的学习,

来源: javaer 分享于  点击 32979 次 点评:145

ArrayList中字段serialVersionUID和序列化的学习,


1、功能:serialVersionUID是用来验证版本一致性的字段。我们将一个类的二进制字节序列转为java对象,也就是反序列化时,JVM会把传进来的二进制字节流中的serialVersionUID和本地相应的实体或对象的serialVersionUID进行比较,如果相同,则认为两个类是一致的,可以进行反序列化,否则就会出现版本不一致的反序列化异常。



2、如何定义serialVersionUID:

(1)显示定义:为实现了Serializable的类添加private static final long 类型的字段serialVersionUID,格式如下:

class TestSeriable implements Serializable {
    private  static  final long serialVersionUID=1L;
}

(2)非显示定义:code中只写接口实现的语法定义,不写上面code中字段serialVersionUID的定义。此时java序列化机制会根据编译生的class文件中的方法和属性为该类自动生成一个serialVersionUID。只要该类的属性和方法不改变,则serialVersionUID不会改变。



3、将serialVersionUID指定为private  final static的好处:

因为非显示定义的serialVersionUID对类的方法和属性敏感,只要类中的属性or方法发生改变,则再次编译的class文件对应的serialVersionUID会与之前生成的不一致。如果之前生成的java实例已经被导入到磁盘进行了存储,那么被反序列化时,会出现InvalidCastException异常。

但是,显示定义的serialVersionUID一直不会改变,则不会出现上述异常。



4、序列化时,被声明为transient的字段不能被序列化,这样设置的原因?

首先,序列化的目的是为了传输数据,不管是从内存传到外存,还是从客户端经过网络传到服务器,目的都是为了保存信息。那么被保存的信息中有些信息是敏感的,比如银行卡密码等敏感信息,为了防止在传输过程中发生泄漏,我们就不对敏感信息进行传输,所以通过把这样的字段设置为transient,则不会再被序列化,自然也就不会被传输了。



5、被transient修饰的变量一定不能被序列化吗?

(1)answer:NO

(2)常识:java中序列化有两种方式,一种是实现Serializable接口,另一种是实现Externalizable接口,当然Externalizable继承自Serializable接口。并且由于Serializable序列化的方便性,不用自己手动去写哪些字段需要被序列化,而使用时候更多。

(3)测试:

import java.io.*;

class TestSeriable implements Externalizable {
    public transient String name;
    public  String pwd;
    public int age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    //序列化对象
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(name);
        out.writeObject(pwd);
        out.writeInt(age);
    }

    //反序列化对象
    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        name=(String)in.readObject();
        pwd=(String)in.readObject();
        age=in.readInt();
    }
    //被序列化的类:必有public类型的无参构造器。因反序列化时,先调用此方法实例化一个类,再调用readExternal方法读取属性值。
    public TestSeriable(){
    }
}

public class Main {
    public static void main(String[] args) throws Exception{
        //Scanner scanner=new Scanner(System.in);//在线笔试
        TestSeriable ts=new TestSeriable();
        ts.setName("cxh");
        ts.setPwd("123456");
        ts.setAge(3);
        //序列化
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("test"));
        out.writeObject(ts);

        //反序列化
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("test"));
        ts = (TestSeriable)in.readObject();

        //输出:
        System.out.println("name:"+ts.name);
        System.out.println("pwd:"+ts.pwd);
        System.out.println("age:"+ts.age);

        //关闭连接
        out.close();
        in.close();
    }
}



输出结果:
name:cxh
pwd:123456
age:3

Process finished with exit code 0



6、验证:

(1)实现Serializable接口的类自动生成serialVersionUID ,只要类的属性和方法不改变,则导出字节码就可以被反序列化

    && 被transient修饰的字段不能被序列化。

import java.io.*;

class TestSeriable implements Serializable {
    private  static  final long serialVersionUID=1L;
    private String name;
    private int age;
    private transient  String pwd;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

}

public class Main {
    public static void main(String[] args) throws Exception{
        //Scanner scanner=new Scanner(System.in);//在线笔试
        TestSeriable test=new TestSeriable();
        test.setAge(27);
        test.setName("cxh");
        test.setPwd("123456");

        ObjectOutputStream  oos=new ObjectOutputStream(new FileOutputStream("file.txt"));
        oos.writeObject(test);
        oos.close();

        ObjectInputStream ois=new ObjectInputStream(new FileInputStream("file.txt"));
        TestSeriable ts=(TestSeriable) ois.readObject();//readObject返回final类型的Object对象,需要强制转化
        System.out.println("my name:"+ts.getName());
        System.out.println("my age:"+ts.getAge());
        System.out.println("my pwd:"+ts.getPwd());


    }
}




输出结果:

my name:cxh
my age:27
my pwd:null

Process finished with exit code 0



(2)导出类的属性和方法发生改变,则原来导出的字节码不能被反序列化。

   改变:为类TestSeriable添加字段school。

import java.io.*;

class TestSeriable implements Serializable {
    private String name;
    private int age;
    private transient  String pwd;

    private String school;//新增字段

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }
    public String getSchool() {
        return school;
    }

    public void setSchool(String school) {
        this.school = school;
    }

}

public class Main {
    public static void main(String[] args) throws Exception{
        //Scanner scanner=new Scanner(System.in);//在线笔试
        TestSeriable test=new TestSeriable();
        test.setAge(27);
        test.setName("cxh");
        test.setPwd("123456");

//        ObjectOutputStream  oos=new ObjectOutputStream(new FileOutputStream("file.txt"));
//        oos.writeObject(test);
//        oos.close();

        ObjectInputStream ois=new ObjectInputStream(new FileInputStream("file.txt"));
        TestSeriable ts=(TestSeriable) ois.readObject();//readObject返回final类型的Object对象,需要强制转化
        System.out.println("my name:"+ts.getName());
        System.out.println("my age:"+ts.getAge());
        System.out.println("my pwd:"+ts.getPwd());


    }
}



报错信息:

Exception in thread "main" java.io.InvalidClassException: TestSeriable; local class incompatible: stream classdesc serialVersionUID = 4006342980489649764, local class serialVersionUID = -3721998844011576358
	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1843)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1713)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2000)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1535)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:422)
	at Main.main(Main.java:58)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

Process finished with exit code 1



(3)如果类从一开始被导出前就显示定义了serialVersionUID,则在类的属性或者方法发生更改后,仍然能进行反序列化。

导出前:

import java.io.*;

class TestSeriable implements Serializable {
    private  static  final long serialVersionUID=1L;
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    public static long getSerialVersionUIDs() {
        return serialVersionUIDs;
    }
    
}

public class Main {
    public static void main(String[] args) throws Exception{
        //Scanner scanner=new Scanner(System.in);//在线笔试
        TestSeriable test=new TestSeriable();
        test.setAge(27);
        test.setName("cxh");

        ObjectOutputStream  oos=new ObjectOutputStream(new FileOutputStream("file1.txt"));
        oos.writeObject(test);
        oos.close();

        ObjectInputStream ois=new ObjectInputStream(new FileInputStream("file1.txt"));
        TestSeriable ts=(TestSeriable) ois.readObject();//readObject返回final类型的Object对象,需要强制转化
        System.out.println("my name:"+ts.getName());
        System.out.println("my age:"+ts.getAge());
    }
}


输出结果:

my name:cxh
my age:27

Process finished with exit code 0


导出后:

import java.io.*;

class TestSeriable implements Serializable {
    private  static  final long serialVersionUID=1L;
    private String name;
    private int age;
    private String address;//新增字段


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    //新增方法
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}

public class Main {
    public static void main(String[] args) throws Exception{
        //Scanner scanner=new Scanner(System.in);//在线笔试
        TestSeriable test=new TestSeriable();
        test.setAge(27);
        test.setName("cxh");

        ObjectOutputStream  oos=new ObjectOutputStream(new FileOutputStream("file.txt"));
        oos.writeObject(test);
        oos.close();

        ObjectInputStream ois=new ObjectInputStream(new FileInputStream("file.txt"));
        TestSeriable ts=(TestSeriable) ois.readObject();//readObject返回final类型的Object对象,需要强制转化
        System.out.println("my name:"+ts.getName());
        System.out.println("my age:"+ts.getAge());
    }
}




输出结果:

my name:cxh
my age:27

Process finished with exit code 0


(4)一个变量为static修饰后,不管是否被transient修饰,均不能被序列化。

序列化:

import java.io.*;

class TestSeriable implements Serializable {
    private  static  final long serialVersionUID=1L;
    private String name;
    private int age;
    private static String address;//更改类型为static字段


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    //新增方法
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}

public class Main {
    public static void main(String[] args) throws Exception{
        //Scanner scanner=new Scanner(System.in);//在线笔试
        TestSeriable test=new TestSeriable();
        test.setAge(27);
        test.setName("cxh");
        test.setAddress("海淀");

        ObjectOutputStream  oos=new ObjectOutputStream(new FileOutputStream("file.txt"));
        oos.writeObject(test);
        oos.close();

//        ObjectInputStream ois=new ObjectInputStream(new FileInputStream("file.txt"));
//        TestSeriable ts=(TestSeriable) ois.readObject();//readObject返回final类型的Object对象,需要强制转化
//        System.out.println("my name:"+ts.getName());
//        System.out.println("my age:"+ts.getAge());
//        System.out.println("my address:"+ts.getAddress());
    }
}



查看序列化结果:

import java.io.*;

class TestSeriable implements Serializable {
    private  static  final long serialVersionUID=1L;
    private String name;
    private int age;
    private static String address;//更改类型为static字段


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    //新增方法
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}

public class Main {
    public static void main(String[] args) throws Exception{
        //Scanner scanner=new Scanner(System.in);//在线笔试
//        TestSeriable test=new TestSeriable();
//        test.setAge(27);
//        test.setName("cxh");
//        test.setAddress("海淀");
//
//        ObjectOutputStream  oos=new ObjectOutputStream(new FileOutputStream("file.txt"));
//        oos.writeObject(test);
//        oos.close();

        ObjectInputStream ois=new ObjectInputStream(new FileInputStream("file.txt"));
        TestSeriable ts=(TestSeriable) ois.readObject();//readObject返回final类型的Object对象,需要强制转化
        System.out.println("my name:"+ts.getName());
        System.out.println("my age:"+ts.getAge());
        System.out.println("my address:"+ts.getAddress());
    }
}




输出结果:

my name:cxh
my age:27
my address:null

Process finished with exit code 0



相关文章

    暂无相关文章

用户点评