精华内容
下载资源
问答
  • 我们在实体类中引用了Serializable这个接口,那么这个接口到底什么?细心的你会发现我们还定义了个serialVersionUID变量。这个变量到底什么作用? 什么Serializable接口 一个对象序列化的接口,一个类只有实现...

    实体类 实现序列化接口 Serializable

    一般情况下,我们在定义实体类时会继承Serializable接口,类似这样:
    在这里插入图片描述
    我们在实体类中引用了Serializable这个接口,那么这个接口到底有什么?细心的你会发现我们还定义了个serialVersionUID变量。这个变量到底有什么作用?

    什么是Serializable接口

    一个对象序列化的接口,一个类只有实现了Serializable接口,它的对象才
    能被序列化。
    

    什么是序列化?

    序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的
    是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存
    储和传输数据。
    

    为什么要序列化对象

    1. 把对象转换为字节序列的过程称为对象的序列化
    2. 把字节序列恢复为对象的过程称为对象的反序列化

    什么情况下需要序列化?

    当我们需要把对象的状态信息通过网络进行传输,或者需要将对象的状态信息持久化,以便将来使用时都需要把对象进行序列化

    那为什么还要继承Serializable。那是存储对象在存储介质中,以便在下次使用的时候,可以很快捷的重建一个副本。

    或许你会问,我在开发过程中,实体并没有实现序列化,但我同样可以将数据保存到mysql、Oracle数据库中,为什么非要序列化才能存储呢?

    我们来看看Serializable到底是什么,跟进去看一下,我们发现Serializable接口里面竟然什么都没有,只是个空接口
    在这里插入图片描述
    一个接口里面什么内容都没有,我们可以将它理解成一个标识接口。
    比如在课堂上有位学生遇到一个问题,于是举手向老师请教,这时老师帮他解答,那么这位学生的举手其实就是一个标识,自己解决不了问题请教老师帮忙解决。在Java中的这个Serializable接口其实是给jvm看的,通知jvm,我不对这个类做序列化了,你(jvm)帮我序列化就好了。

    Serializable接口就是Java提供用来进行高效率的异地共享实例对象的机
    制,实现这个接口即可。
    

    什么是JVM?

    JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

    为什么要定义serialversionUID变量

    简单看一下 Serializable接口的说明
    在这里插入图片描述
    从说明中我们可以看到,如果我们没有自己声明一个serialVersionUID变量,接口会默认生成一个serialVersionUID。

    However, it is <em>strongly recommended</em> that all serializable classes explicitly declare 
    serialVersionUID values, since the default serialVersionUID computation is highly sensitive to 
    class details that may vary depending on compiler implementations, and can thus result in 
    unexpected<code>InvalidClassException</code>s during deserialization.
    

    但是强烈建议用户自定义一个serialVersionUID,因为默认的serialVersinUID对于class的细节非常敏感,反序列化时可能会导致InvalidClassException这个异常。

    在前面我们已经新建了一个实体类User实现Serializable接口,并且定义了serialVersionUID变量。

    测试文本

    我们把User写到文件,然后读取出来。文件路径是:F:\test\test.txt

    1. 测试代码

    目录:
    在这里插入图片描述

    public class User{
        private static final long serialVersionUID = 1L;
        private String userId;
        private String userName;
        private Integer age;
        public User() {
        }
        public User(String userId, String userName, Integer age) {
            this.userId = userId;
            this.userName = userName;
            this.age = age;
        }
        public String getUserId() {
            return userId;
        }
    
        public void setUserId(String userId) {
            this.userId = userId;
        }
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    }
    

    WriterUser.java

    public class WriteUser {
    
        public static void main(String[] args) {
            writerUser();
        }
        private static void writerUser(){
    
            try {
                User user = new User("1", "冯凡利", 13);
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(new File("F:\\test\\test.txt")));
                objectOutputStream.writeObject(user);
                System.out.println("序列化成功!!!");
                objectOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    在这里插入图片描述
    ReadUser.java

    public class ReadUser {
    
        public static void main(String[] args) {
            readUser();
        }
    
        private static void readUser(){
            User user = null;
            try {
                ObjectInputStream objectOutputStream = new ObjectInputStream(new FileInputStream(new File("F:\\test\\test.txt")));
                user = (User) objectOutputStream.readObject();
                System.out.println("用户编号:"+user.getUserId()+"\n用户姓名:"+user.getUserName()+"\n用户年龄:"+user.getAge());
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    

    2. 实现 Serializable 接口时

    先执行 writeUser()写操作;
    在这里插入图片描述
    实际中的文本截图:
    在这里插入图片描述
    在执行readUser()读操作;
    在这里插入图片描述
    序列化与反序列化操作过程就是这么的简单。只需要将User写入到文件中,然后再从文件中进行恢复,恢复后得到的内容与之前完全一样,但是两者是不同的对象。前面提到过一个问题,如果将serialVersionUID变量去掉,我们来看看,会发生什么事情。

    刚开始提到了,serialVersionUID要不要指定呢?如果不指定会出现什么样的后果?如果指定了以后后边的值又代表着什么意思呢?既然系统指定了这个字段,那么肯定是有它的作用的。

    这个serialVersionUID是用来辅助对象的序列化与反序列化的,原则上序列化后的数据当中的serialVersionUID与当前类当中的serialVersionUID一致,那么该对象才能被反序列化成功。这个serialVersionUID的详细的工作机制是:在序列化的时候系统将serialVersionUID写入到序列化的文件中去,当反序列化的时候系统会先去检测文件中的serialVersionUID是否跟当前的文件的serialVersionUID是否一致,如果一直则反序列化成功,否则就说明当前类跟序列化后的类发生了变化,比如是成员变量的数量或者是类型发生了变化,那么在反序列化时就会发生crash,并且回报出错误。

    3. 当不实现 Serializable 接口时

    首先执行写操作writeUser();时,报错
    在这里插入图片描述

    展开全文
  • Serializable做什么的?

    千次阅读 2011-03-28 22:00:00
    1、序列化是干什么的?   简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来。虽然你可以用你自己的各种各样的方法来保 存object states,但是Java给...

    1、序列化是干什么的?
           简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来。虽然你可以用你自己的各种各样的方法来保存object states,但是Java给你提供一种应该比你自己好的保存对象状态的机制,那就是序列化。

    2、什么情况下需要序列化  
        a)当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
        b)当你想用套接字在网络上传送对象的时候;
        c)当你想通过RMI传输对象的时候;

    3、当对一个对象实现序列化时,究竟发生了什么?
        在没有序列化前,每个保存在堆(Heap)中的对象都有相应的状态(state),即实例变量(instance ariable)比如:

       

    java 代码
    1. Foo  myFoo =  new  Foo();  
    2. myFoo .setWidth(37 );  
    3. myFoo.setHeight(70 );  

         
           当 通过下面的代码序列化之后,MyFoo对象中的width和Height实例变量的值(37,70)都被保存到foo.ser文件中,这样以后又可以把它 从文件中读出来,重新在堆中创建原来的对象。当然保存时候不仅仅是保存对象的实例变量的值,JVM还要保存一些小量信息,比如类的类型等以便恢复原来的对 象。

    java 代码
    1. FileOutputStream fs =  new  FileOutputStream( "foo.ser" );  
    2. ObjectOutputStream os = new  ObjectOutputStream(fs);  
    3. os.writeObject(myFoo);  


    4、实现序列化(保存到一个文件)的步骤
          
    a)Make a FileOutputStream            

    java 代码
    1. FileOutputStream fs =  new  FileOutputStream( "foo.ser" );    
           b)Make a ObjectOutputStream

    java 代码
    1. ObjectOutputStream os =   new  ObjectOutputStream(fs);   

           c)write the object

    java 代码
    1. os.writeObject(myObject1);  
    2. os.writeObject(myObject2);  
    3. os.writeObject(myObject3);  

        d) close the ObjectOutputStream

    java 代码
    1. os.close();  



    5、举例说明

    java 代码
    1. import  java.io.*;
    2.   
    3. public   class   Box  implements  Serializable  
    4. {  
    5.     private   int  width;  
    6.     private   int  height;  
    7.   
    8.     public   void  setWidth( int  width){  
    9.         this .width  = width;  
    10.     }  
    11.     public   void  setHeight( int  height){  
    12.         this .height = height;  
    13.     }  
    14.   
    15.     public   static   void  main(String[] args){  
    16.         Box myBox = new  Box();  
    17.         myBox.setWidth(50 );  
    18.         myBox.setHeight(30 );  
    19.   
    20.         try {  
    21.             FileOutputStream fs = new  FileOutputStream( "foo.ser" );  
    22.             ObjectOutputStream os =  new  ObjectOutputStream(fs);  
    23.             os.writeObject(myBox);  
    24.             os.close();  
    25.         }catch (Exception ex){  
    26.             ex.printStackTrace();  
    27.         }  
    28.     }  
    29.       


    6、相关注意事项
        a)序列化时,只对对象的状态进行保存,而不管对象的方法;
        b)当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;
        c)当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;
        d)并非所有的对象都可以序列化,,至于为什么不可以,有很多原因了,比如:
            1.安全方面的原因,比如一个对象拥有private,public等field,对于一个要传输的对象,比如写到文件,或者进行rmi传输  等等,在序列化进行传输的过程中,这个对象的private等域是不受保护的。
           2. 资源分配方面的原因,比如socket,thread类,如果可以序列化,进行传输或者保存,也无法对他们进行重新的资源分  配,而且,也是没有必要这样实现。

    展开全文
  • Serializable

    万次阅读 多人点赞 2017-02-27 00:00:58
    最近在阅读JDK源码中的集合,看到很多集合类实现了Serializable接口,Cloneable接口。在阅读了很多关于Serializable接口的博客...那么这个接口的作用是什么呢。网上找了一些博客看过之后,知道这个接口的作用是实现序列

    最近在阅读JDK源码中的集合,看到很多集合类实现了Serializable接口,Cloneable接口。在阅读了很多关于Serializable接口的博客后,浅谈下我对Serializable接口的理解。

    序列化

    查看 官方文档 就会发现 Serializable接口中一个成员函数或者成员变量也没有。那么这个接口的作用是什么呢。网上找了一些博客看过之后,知道这个接口的作用是实现序列化。

    序列化:对象的寿命通常随着生成该对象的程序的终止而终止,有时候需要把在内存中的各种对象的状态(也就是实例变量,不是方法)保存下来,并且可以在需要时再将对象恢复。虽然你可以用你自己的各种各样的方法来保存对象的状态,但是Java给你提供一种应该比你自己的好的保存对象状态的机制,那就是序列化。

    总结:Java 序列化技术可以使你将一个对象的状态写入一个Byte 流里(系列化),并且可以从其它地方把该Byte 流里的数据读出来(反序列化)。

    系列化的用途

    • 想把的内存中的对象状态保存到一个文件中或者数据库中时候
    • 想把对象通过网络进行传播的时候

    如何序列化

    只要一个类实现Serializable接口,那么这个类就可以序列化了。

    例如有一个 Person类,实现了Serializable接口,那么这个类就可以被序列化了。

    class Person implements Serializable{   
        private static final long serialVersionUID = 1L; //一会就说这个是做什么的
        String name;
        int age;
        public Person(String name,int age){
            this.name = name;
            this.age = age;
        }   
        public String toString(){
            return "name:"+name+"\tage:"+age;
        }
    }

    通过ObjectOutputStream 的writeObject()方法把这个类的对象写到一个地方(文件),再通过ObjectInputStream 的readObject()方法把这个对象读出来。

        File file = new File("file"+File.separator+"out.txt");
        
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);
            ObjectOutputStream oos = null;
            try {
                oos = new ObjectOutputStream(fos);
                Person person = new Person("tom", 22);
                System.out.println(person);
                oos.writeObject(person);            //写入对象
                oos.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }finally{
                try {
                    oos.close();
                } catch (IOException e) {
                    System.out.println("oos关闭失败:"+e.getMessage());
                }
            }
        } catch (FileNotFoundException e) {
            System.out.println("找不到文件:"+e.getMessage());
        } finally{
            try {
                fos.close();
            } catch (IOException e) {
                System.out.println("fos关闭失败:"+e.getMessage());
            }
        }
                                
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
            ObjectInputStream ois = null;
            try {
                ois = new ObjectInputStream(fis);
                try {
                    Person person = (Person)ois.readObject();   //读出对象
                    System.out.println(person);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } 
            } catch (IOException e) {
                e.printStackTrace();
            }finally{
                try {
                    ois.close();
                } catch (IOException e) {
                    System.out.println("ois关闭失败:"+e.getMessage());
                }
            }
        } catch (FileNotFoundException e) {
            System.out.println("找不到文件:"+e.getMessage());
        } finally{
            try {
                fis.close();
            } catch (IOException e) {
                System.out.println("fis关闭失败:"+e.getMessage());
            }
        }

    输出结果为:

    name:tom    age:22
    name:tom    age:22

    结果完全一样。如果我把Person类中的implements Serializable 去掉,Person类就不能序列化了。此时再运行上述程序,就会报java.io.NotSerializableException异常。

    serialVersionUID

    注意到上面程序中有一个 serialVersionUID ,实现了Serializable接口之后,Eclipse就会提示你增加一个 serialVersionUID,虽然不加的话上述程序依然能够正常运行。

    序列化 ID 在 Eclipse 下提供了两种生成策略

    • 一个是固定的 1L
    • 一个是随机生成一个不重复的 long 类型数据(实际上是使用 JDK 工具,根据类名、接口名、成员方法及属性等来生成)

    上面程序中,输出对象和读入对象使用的是同一个Person类。

    如果是通过网络传输的话,如果Person类的serialVersionUID不一致,那么反序列化就不能正常进行。例如在客户端A中Person类的serialVersionUID=1L,而在客户端B中Person类的serialVersionUID=2L 那么就不能重构这个Person对象。

    客户端A中的Person类:

    class Person implements Serializable{   
        
        private static final long serialVersionUID = 1L;
        
        String name;
        int age;
        
        public Person(String name,int age){
            this.name = name;
            this.age = age;
        }   
        public String toString(){
            return "name:"+name+"\tage:"+age;
        }
    }

    客户端B中的Person类:

    class Person implements Serializable{   
        
        private static final long serialVersionUID = 2L;
        
        String name;
        int age;
        
        public Person(String name,int age){
            this.name = name;
            this.age = age;
        }   
        public String toString(){
            return "name:"+name+"\tage:"+age;
        }
    }

    试图重构就会报java.io.InvalidClassException异常,因为这两个类的版本不一致,local class incompatible,重构就会出现错误。

    如果没有特殊需求的话,使用用默认的 1L 就可以,这样可以确保代码一致时反序列化成功。那么随机生成的序列化 ID 有什么作用呢,有些时候,通过改变序列化 ID 可以用来限制某些用户的使用。

    静态变量序列化

    串行化只能保存对象的非静态成员交量,不能保存任何的成员方法和静态的成员变量,而且串行化保存的只是变量的值,对于变量的任何修饰符都不能保存。

    如果把Person类中的name定义为static类型的话,试图重构,就不能得到原来的值,只能得到null。说明对静态成员变量值是不保存的。这其实比较容易理解,序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。

    transient关键字

    经常在实现了 Serializable接口的类中能看见transient关键字。这个关键字并不常见。 transient关键字的作用是:阻止实例中那些用此关键字声明的变量持久化;当对象被反序列化时(从源文件读取字节序列进行重构),这样的实例变量值不会被持久化和恢复。

    当某些变量不想被序列化,同是又不适合使用static关键字声明,那么此时就需要用transient关键字来声明该变量。

    例如用 transient关键字 修饰name变量

    class Person implements Serializable{   
        
        private static final long serialVersionUID = 1L;
        
        transient String name;
        int age;
        
        public Person(String name,int age){
            this.name = name;
            this.age = age;
        }   
        public String toString(){
            return "name:"+name+"\tage:"+age;
        }
    }

    在反序列化视图重构对象的时候,作用与static变量一样: 输出结果为:

    name:null   age:22

    在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。

    注:对于某些类型的属性,其状态是瞬时的,这样的属性是无法保存其状态的。例如一个线程属性或需要访问IO、本地资源、网络资源等的属性,对于这些字段,我们必须用transient关键字标明,否则编译器将报措。

    序列化中的继承问题

    • 当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口。
    • 一个子类实现了 Serializable 接口,它的父类都没有实现 Serializable 接口,要想将父类对象也序列化,就需要让父类也实现Serializable 接口。

    第二种情况中:如果父类不实现 Serializable接口的话,就需要有默认的无参的构造函数。这是因为一个 Java 对象的构造必须先有父对象,才有子对象,反序列化也不例外。在反序列化时,为了构造父对象,只能调用父类的无参构造函数作为默认的父对象。因此当我们取父对象的变量值时,它的值是调用父类无参构造函数后的值。在这种情况下,在序列化时根据需要在父类无参构造函数中对变量进行初始化,否则的话,父类变量值都是默认声明的值,如 int 型的默认是 0,string 型的默认是 null。

    例如:

    class People{
        int num;
        public People(){}           //默认的无参构造函数,没有进行初始化
        public People(int num){     //有参构造函数
            this.num = num;
        }
        public String toString(){
            return "num:"+num;
        }
    }
    class Person extends People implements Serializable{    
        
        private static final long serialVersionUID = 1L;
        
        String name;
        int age;
        
        public Person(int num,String name,int age){
            super(num);             //调用父类中的构造函数
            this.name = name;
            this.age = age;
        }
        public String toString(){
            return super.toString()+"\tname:"+name+"\tage:"+age;
        }
    }

    在一端写出对象的时候

        Person person = new Person(10,"tom", 22); //调用带参数的构造函数num=10,name = "tim",age =22
        System.out.println(person);
        oos.writeObject(person);                  //写出对象

    在另一端读出对象的时候

        Person person = (Person)ois.readObject(); //反序列化,调用父类中的无参构函数。
        System.out.println(person);

    输出为

        num:0   name:tom    age:22

    发现由于父类中无参构造函数并没有对num初始化,所以num使用默认值为0。

    总结

    序列化给我们提供了一种技术,用于保存对象的变量。以便于传输。虽然也可以使用别的一些方法实现同样的功能,但是java给我们提供的方法使用起来是非常方便的。以上仅仅是我的一些理解,由于本人水平有限,不足之处还请指正。下篇学习Cloneable接口的用途。

    原文地址 http://blademastercoder.github.io/2015/01/29/java-Serializable.html

    展开全文
  • Serializable什么,为什么要实现Serializable接口?什么Serializable接口什么是序列化?为什么要序列化对象什么情况下需要序列化?为什么要定义serialversionUID变量序列化的使用关于serialVersionUID 一般情况...

    一般情况下,我们在定义实体类时会实现Serializable接口,例如:
    在这里插入图片描述

    什么是Serializable接口

    一个对象序列化的接口,一个类只有实现了Serializable接口,它的对象才能被序列化。

    Serializable是java.io包中定义的、用于实现Java类的序列化操作而提供的一个语义级别的接口。

    Serializable序列化接口没有任何方法或者字段,只是用于标识可序列化的语义。

    实现了Serializable接口的类可以被ObjectOutputStream转换为字节流,同时也可以通过ObjectInputStream再将其解析为对象。例如,我们可以将序列化对象写入文件后,再次从文件中读取它并反序列化成对象,也就是说,可以使用表示对象及其数据的类型信息和字节在内存中重新创建对象。

    什么是序列化?

    序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。

    为什么要序列化对象

    把对象转换为字节序列的过程称为对象的序列化

    把字节序列恢复为对象的过程称为对象的反序列化

    序列化对于面向对象的编程语言来说是非常重要的,因为无论什么编程语言,其底层涉及IO操作的部分还是由操作系统其帮其完成的,而底层IO操作都是以字节流的方式进行的,所以写操作都涉及将编程语言数据类型转换为字节流,而读操作则又涉及将字节流转化为编程语言类型的特定数据类型。

    什么情况下需要序列化?

    当我们需要把对象的状态信息通过网络进行传输,或者需要将对象的状态信息持久化,以便将来使用时都需要把对象进行序列化。

    那为什么还要继承Serializable。那是存储对象在存储介质中,以便在下次使用的时候,可以很快捷的重建一个副本。

    或许你会问,我在开发过程中,实体并没有实现序列化,但我同样可以将数据保存到mysql、Oracle数据库中,为什么非要序列化才能存储呢?

    我们来看看Serializable到底是什么,跟进去看一下,我们发现Serializable接口里面竟然什么都没有,只是个空接口
    在这里插入图片描述

    一个接口里面什么内容都没有,我们可以将它理解成一个标识接口。

    比如在课堂上有位学生遇到一个问题,于是举手向老师请教,这时老师帮他解答,那么这位学生的举手其实就是一个标识,自己解决不了问题请教老师帮忙解决。在Java中的这个Serializable接口其实是给jvm看的,通知jvm,我不对这个类做序列化了,你(jvm)帮我序列化就好了。

    Serializable接口就是Java提供用来进行高效率的异地共享实例对象的机制,实现这个接口即可。

    为什么要定义serialversionUID变量

    首先看一下接口里的说明:
    在这里插入图片描述
    可以发现如果我们不自定义serialversionUID,系统就会生成一个默认的serialversionUID。
    在这里插入图片描述
    从注释中我们可以看到,它强烈建议我们自己定义一个serialversionUID,因为默认生成的serialversionUID对class极其敏感,在反序列化的时候很容易抛出InvalidClassException异常。

    序列化的使用

    下面我们可以通过例子来实现将序列化的对象存储到文件,然后再将其从文件中反序列化为对象,代码示例如下:

    先定义一个序列化对象User:

      public class User implements Serializable { 
            private static final long serialVersionUID = 1L; 
         
            private String userId; 
            private String userName; 
         
            public User(String userId, String userName) { 
                this.userId = userId; 
                this.userName = userName; 
            } 
        } 
    

    然后我们编写测试类,来对该对象进行读写操作,我们先测试将该对象写入一个文件:

     public class SerializableTest { 
         
            /** 
             * 将User对象作为文本写入磁盘 
             */ 
            public static void writeObj() { 
                User user = new User("1001", "Joe"); 
                try { 
                    ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("/Users/guanliyuan/user.txt")); 
                    objectOutputStream.writeObject(user); 
                    objectOutputStream.close(); 
                } catch (IOException e) { 
                    e.printStackTrace(); 
                } 
            } 
         
            public static void main(String args[]) { 
                writeObj(); 
            } 
        } 
    

    运行上述代码,我们就将User对象及其携带的数据写入了文本user.txt中。

    接下来,我们继续编写测试代码,尝试将之前持久化写入user.txt文件的对象数据再次转化为Java对象,代码如下:

    public class SerializableTest {
        /**
         * 将类从文本中提取并赋值给内存中的类
         */
        public static void readObj() {
            try {
                ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("/Users/guanliyuan/user.txt"));
                try {
                    Object object = objectInputStream.readObject();
                    User user = (User) object;
                    System.out.println(user);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    
        public static void main(String args[]) {
            readObj();
        }
    }
    

    通过反序列化操作,可以再次将持久化的对象字节流数据通过IO转化为Java对象,结果如下:

    cn.wudimanong.serializable.User@6f496d9f
    

    序列化与反序列化操作过程就是这么的简单。只需要将User写入到文件中,然后再从文件中进行恢复,恢复后得到的内容与之前完全一样,但是两者是不同的对象。

    关于serialVersionUID

    对于JVM来说,要进行持久化的类必须要有一个标记,只有持有这个标记JVM才允许类创建的对象可以通过其IO系统转换为字节数据,从而实现持久化,而这个标记就是Serializable接口。而在反序列化的过程中则需要使用serialVersionUID来确定由那个类来加载这个对象,所以我们在实现Serializable接口的时候,一般还会要去尽量显示地定义serialVersionUID。

    这个serialVersionUID的详细的工作机制是:在序列化的时候系统将serialVersionUID写入到序列化的文件中去,当反序列化的时候系统会先去检测文件中的serialVersionUID是否跟当前的文件的serialVersionUID是否一致,如果一直反序列化不成功,就说明当前类跟序列化后的类发生了变化,比如是成员变量的数量或者是类型发生了变化,那么在反序列化时就会发生crash,并且回报出错误:

    java.io.InvalidClassException: User; local class incompatible: stream classdesc serialVersionUID = -1451587475819212328, local class serialVersionUID = -3946714849072033140at 
    java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)at 
    java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1885)at 
    java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1751)at
    java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2042)at 
    java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)at 
    java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)at 
    Main.readUser(Main.java:32)at Main.main(Main.java:10)
    

    刚开始提到了,serialVersionUID要不要指定呢?如果不指定会出现什么样的后果?如果指定了以后后边的值又代表着什么意思呢?既然系统指定了这个字段,那么肯定是有它的作用的。

    如果我们在序列化中没有显示地声明serialVersionUID,则序列化运行时将会根据该类的各个方面计算该类默认的serialVersionUID值。但是,Java官方强烈建议所有要序列化的类都显示地声明serialVersionUID字段,因为如果高度依赖于JVM默认生成serialVersionUID,可能会导致其与编译器的实现细节耦合,这样可能会导致在反序列化的过程中发生意外的InvalidClassException异常。因此,为了保证跨不同Java编译器实现的serialVersionUID值的一致,实现Serializable接口的必须显示地声明serialVersionUID字段。

    此外serialVersionUID字段地声明要尽可能使用private关键字修饰,这是因为该字段的声明只适用于声明的类,该字段作为成员变量被子类继承是没有用处的!有个特殊的地方需要注意的是,数组类是不能显示地声明serialVersionUID的,因为它们始终具有默认计算的值,不过数组类反序列化过程中也是放弃了匹配serialVersionUID值的要求。

    转自:

    https://baijiahao.baidu.com/s?id=1633305649182361563&wfr=spider&for=pc

    https://mp.weixin.qq.com/s/Zpb2OuZxJpWX2mow3qd-xg

    展开全文
  • 为啥有些类要加[Serializable],不加会怎样 加了又怎样,什么时候加。网上看到的回答都太官方了,理解力有限还不能理解,希望得到点好的回答
  • 而实际上,Serializable是一个空接口,没有什么具体内容,它的目的只是简单的标识一个类的对象可以被序列化。 什么情况下需要序列化 a)当你想把的内存中的对象写入到硬盘的时候; b)当你想用套接字在网络上...
  • Serializable的作用 前几天看到别人使用枚举Emum implements serializable这个东西,之前并没有见过这种写法,就去了解了一下原因 import java.io.Serializable; public enum ConstantEmum implements Serializable ...
  • 综合网友资料和自己理解 工作中我们经常在进行持久化操作和返回数据时都会使用到...我一些项目的时候,没有实现序列化,同样没什么影响,到底什么时候应该进行序列化操作呢? 首先第一个问题,实现序列化的...
  • java pojo 层实现 Serializable接口有什么意义?
  • 什么要实现Serializable.pdf
  • # Serializable什么用?

    2019-04-21 20:29:08
    Serializable什么用? Serializable,之前一直有使用,默认的实体类就会实现Serializable接口,对具体原因一直不是很了解,同时如果没有实现序列化,同样没什么影响,什么时候应该进行序列化操作呢?今天查了下资料...
  • 什么要实现Serializable

    千次阅读 2015-05-21 19:44:48
    在一些类中,为什么要实现Serializable接口?它的作用是什么什么时候用?谢谢。如public class UpSend implements Serializable {} 一个对象序列化的接口,一个类只有实现了Serializable接口,它的对象...
  • implements Serializable

    万次阅读 多人点赞 2018-08-21 20:05:12
    而实际上,Serializable是一个空接口,没有什么具体内容,它的目的只是简单的标识一个类的对象可以被序列化。 序列化的应用场景 1. 比如说你的内存不够用了,那计算机就要将内存里面的一部分对...
  • Serializable接口

    2019-12-12 13:25:05
    Serializable接口概述 Serializable是java.io包中定义的、用于实现Java类的序列化操作而提供的一个语义级别的接口。Serializable序列化接口没有任何方法或者字段,只是用于标识可序列化的语义。实现了Serializable...
  • 实体类为什么要实现Serializable序列化的作用

    万次阅读 多人点赞 2018-06-07 11:46:37
    当实现java.io.Serializable接口的实体(类)没有显式地定义一个名为serialVersionUID,类型为long的变量时,Java序列化机制会根据编译的class自动生成一个serialVersionUID作序列化版本比较用,这种情况下,只有...
  • java中serializable什么

    2013-05-14 13:12:05
    而实际上,Serializable是一个空接口,没有什么具体内容,它的目的只是简单的标识一个类的对象可以被序列化。 追问 那进行序列化有什么好处呢? 回答 什么情况下需要序列化 a)当你想把的内存中的对象...
  • 1、什么是Java序列化与反序列化Java序列化是指把Java对象保存为二进制字节码的过程,Java反序列化是指把二进制码重新转换成Java对象的过程。2、为什么要序列化把对象的字节序列永久地保存到硬盘上,通常存放在一个...
  • 而实际上,Serializable是一个空接口,没有什么具体内容,它的目的只是简单的标识一个类的对象可以被序列化。 那进行序列化有什么好处呢? 什么情况下需要序列化: a)当你想把的内存中的对象写
  • implements Serializable什么作用?

    千次阅读 2015-12-22 16:10:58
    有很多网络传输使用的都是implements Serializable,没有implements Serializable,你就不能通过rmi(包括ejb)提供远程调用。 ...serialization不但可以在本机,而且可以经由网络操作(就是猫小说
  • 遇到这个 Java Serializable...c,serialVersionUID 这个的值到底是在怎么设置的,有什么用。有的是1L,有的是一长串数字,迷惑ing。 我刚刚见到这个关键字 Serializable 的时候,就有如上的这么些问题。 在处理这个...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 370,238
精华内容 148,095
关键字:

serializable到底做了什么