精华内容
下载资源
问答
  • java序列化和反序列化以及序列化ID作用分析

    万次阅读 多人点赞 2017-08-07 20:26:07
    java序列化和反序列化以及序列化ID作用分析

     java序列化和反序列化

    一、概念

           java对象序列化的意思就是将对象的状态转化成字节流,以后可以通过这些值再生成相同状态的对象。对象序列化是对象持久化的一种实现方法,它是将对象的属性和方法转化为一种序列化的形式用于存储和传输。反序列化就是根据这些保存的信息重建对象的过程。

           序列化:将java对象转化为字节序列的过程。

           反序列化:将字节序列转化为java对象的过程。


    二、为什么要序列化和反序列化

           我们知道,当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本、图片、音频、视频等, 而这些数据都会以二进制序列的形式在网络上传送。那么当两个Java进程进行通信时,能否实现进程间的对象传送呢?答案是可以的。如何做到呢?这就需要Java序列化与反序列化了。换句话说,一方面,发送方需要把这个Java对象转换为字节序列,然后在网络上传送;另一方面,接收方需要从字节序列中恢复出Java对象。当我们明晰了为什么需要Java序列化和反序列化后,我们很自然地会想Java序列化的好处。其好处一是实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上(通常存放在文件里),二是,利用序列化实现远程通信,即在网络上传送对象的字节序列。


    三、涉及到的javaAPI 

              java.io.ObjectOutputStream表示对象输出流,它的writeObject(Object obj)方法可以对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。

              java.io.ObjectInputStream表示对象输入流,它的readObject()方法源输入流中读取字节序列,再把它们反序列化成为一个对象,并将其返回。

             只有实现了Serializable或Externalizable接口的类的对象才能被序列化,否则抛出异常。


    四、序列化和反序列化的步骤

             序列化:

               步骤一:创建一个对象输出流,它可以包装一个其它类型的目标输出流,如文件输出流:

                              ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(“目标地址路径”));

             步骤二:通过对象输出流的writeObject()方法写对象:

                              out.writeObject("Hello");

                              out.writeObject(new Date());

             反序列化:        

              步骤一:创建一个对象输入流,它可以包装一个其它类型输入流,如文件输入流:

                              ObjectInputStream in = new ObjectInputStream(new fileInputStream(“目标地址路径”));

             步骤二:通过对象输出流的readObject()方法读取对象:

                            String obj1 = (String)in.readObject();

                            Date obj2 =  (Date)in.readObject();

            说明:为了正确读取数据,完成反序列化,必须保证向对象输出流写对象的顺序与从对象输入流中读对象的顺序一致。

    五、举个例子

           我们首先写个Person实现Serializable接口:

    [java]  view plain  copy
     print ?
    1. import java.io.Serializable;  
    2.   
    3. /** 
    4.  *  
    5.  * 测试序列化和反序列化 
    6.  * @author crazyandcoder 
    7.  * @date [2015-8-5 上午11:14:32] 
    8.  */  
    9. public class Person implements Serializable  {  
    10.       
    11.     private int age;  
    12.     private String name;  
    13.     //序列化ID  
    14.     private static final long serialVersionUID = -5809782578272943999L;  
    15.       
    16.     public Person() {}  
    17.       
    18.     public int getAge() {  
    19.         return age;  
    20.     }  
    21.     public void setAge(int age) {  
    22.         this.age = age;  
    23.     }  
    24.     public String getName() {  
    25.         return name;  
    26.     }  
    27.     public void setName(String name) {  
    28.         this.name = name;  
    29.     }  
    30.   
    31. }  

            

             其次,我们在main()里面写个方法,执行序列化过程:

    [java]  view plain  copy
     print ?
    1. import java.io.FileNotFoundException;  
    2. import java.io.FileOutputStream;  
    3. import java.io.IOException;  
    4. import java.io.ObjectOutputStream;  
    5. /** 
    6.  *  
    7.  * 测试序列化和反序列化 
    8.  * @author crazyandcoder 
    9.  * @date [2015-8-5 上午11:16:14] 
    10.  */  
    11. public class ObjSerializeAndDeserializeTest {   
    12.     public static void main(String[] args) {  
    13.           
    14.         //将Person对象序列化  
    15.         SerializePerson();  
    16.   
    17.     }  
    18.       
    19.     /** 
    20.      *  
    21.      * @author crazyandcoder 
    22.      * @Title: 序列化Person对象,将其存储到 E:/hello.txt文件中 
    23.      * @param   
    24.      * @return void  
    25.      * @throws  
    26.      * @date [2015-8-5 上午11:21:27] 
    27.      */  
    28.     private static void SerializePerson() {  
    29.         Person person =new Person();  
    30.         person.setAge(30);  
    31.         person.setName("SerializePerson");  
    32.         ObjectOutputStream outputStream = null;  
    33.         try {  
    34.             outputStream=new ObjectOutputStream(new FileOutputStream("E:/hello.txt"));  
    35.             outputStream.writeObject(person);  
    36.             System.out.println("序列化成功。");  
    37.         } catch (FileNotFoundException e) {  
    38.             e.printStackTrace();              
    39.         } catch (IOException e) {  
    40.             e.printStackTrace();          
    41.         } finally {  
    42.             try {  
    43.                 outputStream.close();  
    44.             } catch (IOException e) {  
    45.                 e.printStackTrace();  
    46.             }  
    47.         }         
    48.     }  
    49. }  
               代码很简单,首先创建一个对象输出流ObjectOutputStream,它可以包装一个其它类型的目标输出流,如文件输出流FileOutputStream,并指定存储的位置为“E:/hello.txt”,然后通过对象输出流的writeObject()方法写对象便执行了序列化过程。运行看一下效果,正确的话便会在控制台打印“”,并且在本地E盘下会创建一个Hello.txt文件。



            

            我们查看一下hello.txt文件中的内容,里面是一串字节序列,打开该文件的时候不要用自带的记事本打开,因为涉及到字符编码的问题,所以显示的话是一串乱码,建议用SublimeText打开。


             

            我们再写个方法来反序列化该字节成Person对象,并打印出里面的值。

    [java]  view plain  copy
     print ?
    1. import java.io.FileInputStream;  
    2. import java.io.FileNotFoundException;  
    3. import java.io.FileOutputStream;  
    4. import java.io.IOException;  
    5. import java.io.ObjectInputStream;  
    6. import java.io.ObjectOutputStream;  
    7. /** 
    8.  *  
    9.  * 测试序列化和反序列化 
    10.  * @author crazyandcoder 
    11.  * @date [2015-8-5 上午11:16:14] 
    12.  */  
    13. public class ObjSerializeAndDeserializeTest {  
    14.   
    15.        
    16.     public static void main(String[] args) {  
    17.           
    18.         //反序列化生成Person对象  
    19.         Person person=DeserializePerson();  
    20.         System.out.println("name :"+person.getName());  
    21.         System.out.println("age  :"+person.getAge());  
    22.           
    23.   
    24.     }  
    25.       
    26.     /** 
    27.      * 执行反序列化过程生产Person对象 
    28.      * @author crazyandcoder 
    29.      * @Title: DeserializePerson  
    30.      * @param @return  
    31.      * @return Person  
    32.      * @throws  
    33.      * @date [2015-8-5 下午1:30:12] 
    34.      */  
    35.     private static Person DeserializePerson() {  
    36.           
    37.         Person person=null;  
    38.         ObjectInputStream inputStream=null;  
    39.         try {  
    40.             inputStream=new ObjectInputStream(new FileInputStream("E:/hello.txt"));  
    41.             try {  
    42.                 person=(Person)inputStream.readObject();  
    43.                 System.out.println("执行反序列化过程成功。");  
    44.             } catch (ClassNotFoundException e) {  
    45.                 e.printStackTrace();  
    46.             }  
    47.         } catch (FileNotFoundException e) {  
    48.             e.printStackTrace();  
    49.         } catch (IOException e) {  
    50.             e.printStackTrace();  
    51.         } finally {  
    52.             try {  
    53.                 inputStream.close();  
    54.             } catch (IOException e) {  
    55.                 e.printStackTrace();  
    56.             }  
    57.         }  
    58.         return person;  
    59.     }  
    60. }  

             

            执行反序列化的代码也是很简单的,首先创建一个输入流对象ObjectInputStream,然后从指定的目录下“E:/hello.txt”获取它的字节序列,然后通过输入流对象的readObject()方法将其获得的对象强制转化为Person对象,这就完成了反序列化工作,正确的反序列化成功的情况下控制台打印输出为:



    Java 序列化ID的作用


           有关序列化和反序列化的概念,可以查看前一篇java序列化和反序列化使用总结的讲解,这一篇主要说明一下序列化过程中出现的问题即java序列化和反序列化中ID的作用。

            在前一篇的介绍中,我们在代码里会发现有这样一个变量:serialVersionUID,那么这个变量serialVersionUID到底具有什么作用呢?能不能去掉呢?


    [java]  view plain  copy
     print ?
    1. public class Person implements Serializable  {  
    2.       
    3.     private int age;  
    4.     private String sex;  
    5.     private String name;  
    6.     private String hobby;  
    7.     //序列化ID  
    8.     private static final long serialVersionUID = -5809782578272943999L;  
    9.         ............  
    10.   
    11. }  
           

           序列化ID的作用:  

           其实,这个序列化ID起着关键的作用,它决定着是否能够成功反序列化!简单来说,java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地实体类中的serialVersionUID进行比较,如果相同则认为是一致的,便可以进行反序列化,否则就会报序列化版本不一致的异常。等会我们可以通过代码验证一下。

           序列化ID如何产生:

           当我们一个实体类中没有显示的定义一个名为“serialVersionUID”、类型为long的变量时,Java序列化机制会根据编译时的class自动生成一个serialVersionUID作为序列化版本比较,这种情况下,只有同一次编译生成的class才会生成相同的serialVersionUID。譬如,当我们编写一个类时,随着时间的推移,我们因为需求改动,需要在本地类中添加其他的字段,这个时候再反序列化时便会出现serialVersionUID不一致,导致反序列化失败。那么如何解决呢?便是在本地类中添加一个“serialVersionUID”变量,值保持不变,便可以进行序列化和反序列化。


           验证“serialVersionUID”不一致导致反序列化失败

    [java]  view plain  copy
     print ?
    1. import java.io.Serializable;  
    2.   
    3. /** 
    4.  *  
    5.  * 测试序列化和反序列化 
    6.  * @author crazyandcoder 
    7.  * @date [2015-8-5 上午11:14:32] 
    8.  */  
    9. public class Person implements Serializable  {  
    10.       
    11.     private int age;  
    12. //  private String sex;  
    13. //  private String name;  
    14. //  private String hobby;  
    15.     //序列化ID  
    16. //  private static final long serialVersionUID = -5809782578272943999L;  
    17.       
    18. //  public String getHobby() {  
    19. //      return hobby;  
    20. //  }  
    21. //  
    22. //  public void setHobby(String hobby) {  
    23. //      this.hobby = hobby;  
    24. //  }  
    25.   
    26.     public Person() {}  
    27.       
    28.     public int getAge() {  
    29.         return age;  
    30.     }  
    31.       
    32.     public void setAge(int age) {  
    33.         this.age = age;  
    34.     }  
    35.       
    36. //  public String getSex() {  
    37. //      return sex;  
    38. //  }  
    39. //  
    40. //  public void setSex(String sex) {  
    41. //      this.sex = sex;  
    42. //  }  
    43.       
    44. //  public String getName() {  
    45. //      return name;  
    46. //  }  
    47. //  public void setName(String name) {  
    48. //      this.name = name;  
    49. //  }  
    50.   
    51. }  
            复用前篇使用到的代码,首先,我们生成一个本地Person类,里面添加一个字段age,然后将其序列化存于本地E:/hello.txt中,

    [java]  view plain  copy
     print ?
    1. import java.io.FileNotFoundException;  
    2. import java.io.FileOutputStream;  
    3. import java.io.IOException;  
    4. import java.io.ObjectOutputStream;  
    5.   
    6.   
    7. /** 
    8.  *  
    9.  * 测试序列化和反序列化 
    10.  * @author crazyandcoder 
    11.  * @date [2015-8-5 上午11:16:14] 
    12.  */  
    13. public class ObjSerializeAndDeserializeTest {  
    14.   
    15.        
    16.     public static void main(String[] args) {  
    17.           
    18.         //将Person对象序列化  
    19.         SerializePerson();  
    20.     }  
    21.       
    22.       
    23.     /** 
    24.      *  
    25.      * @author crazyandcoder 
    26.      * @Title: 序列化Person对象,将其存储到 E:/hello.txt文件中 
    27.      * @param   
    28.      * @return void  
    29.      * @throws  
    30.      * @date [2015-8-5 上午11:21:27] 
    31.      */  
    32.     private static void SerializePerson() {  
    33.         Person person =new Person();  
    34.         person.setAge(30);  
    35.         ObjectOutputStream outputStream = null;  
    36.         try {  
    37.             outputStream=new ObjectOutputStream(new FileOutputStream("E:/hello.txt"));  
    38.             outputStream.writeObject(person);  
    39.             System.out.println("序列化成功。");  
    40.         } catch (FileNotFoundException e) {  
    41.             e.printStackTrace();  
    42.                   
    43.         } catch (IOException e) {  
    44.             e.printStackTrace();  
    45.                   
    46.         } finally {  
    47.             try {  
    48.                 outputStream.close();  
    49.             } catch (IOException e) {  
    50.                 e.printStackTrace();  
    51.             }  
    52.         }  
    53.           
    54.     }  
    55. }  
            

            运行一下,会在控制台中打印“序列化成功。”,然后我们在Person类中再添加一个字段,name,然后直接从E:/hello.txt中反序列化,再运行一下,看看会出现什么问题。

    [java]  view plain  copy
     print ?
    1. import java.io.FileInputStream;  
    2. import java.io.FileNotFoundException;  
    3. import java.io.FileOutputStream;  
    4. import java.io.IOException;  
    5. import java.io.ObjectInputStream;  
    6. import java.io.ObjectOutputStream;  
    7.   
    8. /** 
    9.  *  
    10.  * 测试序列化和反序列化 
    11.  *  
    12.  * @author crazyandcoder 
    13.  * @date [2015-8-5 上午11:16:14] 
    14.  */  
    15. public class ObjSerializeAndDeserializeTest {  
    16.   
    17.     public static void main(String[] args) {  
    18.   
    19.         // 反序列化生成Person对象  
    20.         Person person = DeserializePerson();  
    21.         System.out.println("name :" + person.getName());  
    22.         System.out.println("age  :" + person.getAge());  
    23.     }  
    24.   
    25.     /** 
    26.      * 执行反序列化过程生产Person对象 
    27.      *  
    28.      * @author crazyandcoder 
    29.      * @Title: DeserializePerson 
    30.      * @param @return 
    31.      * @return Person 
    32.      * @throws 
    33.      * @date [2015-8-5 下午1:30:12] 
    34.      */  
    35.     private static Person DeserializePerson() {  
    36.   
    37.         Person person = null;  
    38.         ObjectInputStream inputStream = null;  
    39.         try {  
    40.             inputStream = new ObjectInputStream(new FileInputStream("E:/hello.txt"));  
    41.             try {  
    42.                 person = (Person) inputStream.readObject();  
    43.                 System.out.println("执行反序列化过程成功。");  
    44.             } catch (ClassNotFoundException e) {  
    45.                 e.printStackTrace();  
    46.             }  
    47.         } catch (FileNotFoundException e) {  
    48.             e.printStackTrace();  
    49.         } catch (IOException e) {  
    50.             e.printStackTrace();  
    51.         } finally {  
    52.             try {  
    53.                 inputStream.close();  
    54.             } catch (IOException e) {  
    55.                 e.printStackTrace();  
    56.             }  
    57.         }  
    58.         return person;  
    59.     }  
    60. }  

           运行一下,不出意外,报了一个异常。



          

          从上面两张图便可以看出两次的序列化ID是不一样的,导致反序列化失败。


    总结:

           虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID = 1L)。



    展开全文
  • java 序列化ID作用

    万次阅读 多人点赞 2015-08-05 21:39:20
    java 序列化ID作用


    java 序列化ID的作用


           有关序列化和反序列化的概念,可以查看前一篇java序列化和反序列化使用总结的讲解,这一篇主要说明一下序列化过程中出现的问题即java序列化和反序列化中ID的作用。

            在前一篇的介绍中,我们在代码里会发现有这样一个变量:serialVersionUID,那么这个变量serialVersionUID到底具有什么作用呢?能不能去掉呢?


    public class Person implements Serializable  {
    	
    	private int age;
    	private String sex;
    	private String name;
    	private String hobby;
    	//序列化ID
    	private static final long serialVersionUID = -5809782578272943999L;
            ............
    
    }
           

           序列化ID的作用:  

           其实,这个序列化ID起着关键的作用,它决定着是否能够成功反序列化!简单来说,java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地实体类中的serialVersionUID进行比较,如果相同则认为是一致的,便可以进行反序列化,否则就会报序列化版本不一致的异常。等会我们可以通过代码验证一下。

           序列化ID如何产生:

           当我们一个实体类中没有显示的定义一个名为“serialVersionUID”、类型为long的变量时,Java序列化机制会根据编译时的class自动生成一个serialVersionUID作为序列化版本比较,这种情况下,只有同一次编译生成的class才会生成相同的serialVersionUID。譬如,当我们编写一个类时,随着时间的推移,我们因为需求改动,需要在本地类中添加其他的字段,这个时候再反序列化时便会出现serialVersionUID不一致,导致反序列化失败。那么如何解决呢?便是在本地类中添加一个“serialVersionUID”变量,值保持不变,便可以进行序列化和反序列化。


           验证“serialVersionUID”不一致导致反序列化失败

    import java.io.Serializable;
    
    /**
     * 
     * 测试序列化和反序列化
     * @author crazyandcoder
     * @date [2015-8-5 上午11:14:32]
     */
    public class Person implements Serializable  {
    	
    	private int age;
    //	private String sex;
    //	private String name;
    //	private String hobby;
    	//序列化ID
    //	private static final long serialVersionUID = -5809782578272943999L;
    	
    //	public String getHobby() {
    //		return hobby;
    //	}
    //
    //	public void setHobby(String hobby) {
    //		this.hobby = hobby;
    //	}
    
    	public Person() {}
    	
    	public int getAge() {
    		return age;
    	}
    	
    	public void setAge(int age) {
    		this.age = age;
    	}
    	
    //	public String getSex() {
    //		return sex;
    //	}
    //
    //	public void setSex(String sex) {
    //		this.sex = sex;
    //	}
    	
    //	public String getName() {
    //		return name;
    //	}
    //	public void setName(String name) {
    //		this.name = name;
    //	}
    
    }
    
            复用前篇使用到的代码,首先,我们生成一个本地Person类,里面添加一个字段age,然后将其序列化存于本地E:/hello.txt中,

    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectOutputStream;
    
    
    /**
     * 
     * 测试序列化和反序列化
     * @author crazyandcoder
     * @date [2015-8-5 上午11:16:14]
     */
    public class ObjSerializeAndDeserializeTest {
    
    	 
    	public static void main(String[] args) {
    		
    		//将Person对象序列化
    		SerializePerson();
    	}
    	
    	
    	/**
    	 * 
    	 * @author crazyandcoder
    	 * @Title: 序列化Person对象,将其存储到 E:/hello.txt文件中
    	 * @param  
    	 * @return void 
    	 * @throws 
    	 * @date [2015-8-5 上午11:21:27]
    	 */
    	private static void SerializePerson() {
    		Person person =new Person();
    		person.setAge(30);
    		ObjectOutputStream outputStream = null;
    		try {
    			outputStream=new ObjectOutputStream(new FileOutputStream("E:/hello.txt"));
    			outputStream.writeObject(person);
    			System.out.println("序列化成功。");
    		} catch (FileNotFoundException e) {
    			e.printStackTrace();
    				
    		} catch (IOException e) {
    			e.printStackTrace();
    				
    		} finally {
    			try {
    				outputStream.close();
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    		
    	}
    }
    
            

            运行一下,会在控制台中打印“序列化成功。”,然后我们在Person类中再添加一个字段,name,然后直接从E:/hello.txt中反序列化,再运行一下,看看会出现什么问题。

    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    
    /**
     * 
     * 测试序列化和反序列化
     * 
     * @author crazyandcoder
     * @date [2015-8-5 上午11:16:14]
     */
    public class ObjSerializeAndDeserializeTest {
    
    	public static void main(String[] args) {
    
    		// 反序列化生成Person对象
    		Person person = DeserializePerson();
    		System.out.println("name :" + person.getName());
    		System.out.println("age  :" + person.getAge());
    	}
    
    	/**
    	 * 执行反序列化过程生产Person对象
    	 * 
    	 * @author crazyandcoder
    	 * @Title: DeserializePerson
    	 * @param @return
    	 * @return Person
    	 * @throws
    	 * @date [2015-8-5 下午1:30:12]
    	 */
    	private static Person DeserializePerson() {
    
    		Person person = null;
    		ObjectInputStream inputStream = null;
    		try {
    			inputStream = new ObjectInputStream(new FileInputStream("E:/hello.txt"));
    			try {
    				person = (Person) inputStream.readObject();
    				System.out.println("执行反序列化过程成功。");
    			} catch (ClassNotFoundException e) {
    				e.printStackTrace();
    			}
    		} catch (FileNotFoundException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		} finally {
    			try {
    				inputStream.close();
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    		return person;
    	}
    }
    

           运行一下,不出意外,报了一个异常。



          

          从上面两张图便可以看出两次的序列化ID是不一样的,导致反序列化失败。


    总结:

           虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID = 1L)。



    展开全文
  • 序列化ID的详解及其作用

    千次阅读 2019-07-22 10:50:09
    有关序列化和反序列化的内容前面已经整理...本文主要描述的是序列化ID 和 其作用 一、序列化实现方式 序列化Serializable的方式特别简单 实现Serializable接口,再在类中声明如下这一个属性即可。 private sta...

    有关序列化和反序列化的内容前面已经整理过,这里就不再继续,详见链接:https://blog.csdn.net/Snowing1234_5/article/details/93710280

    本文主要描述的是序列化ID 和 其作用

    一、序列化实现方式

    序列化Serializable的方式特别简单 实现Serializable接口,再在类中声明如下这一个属性即可。

    private static final long serialVersionUID = -3928832861296252415L;

     

    二、Serializable序列化的工作机制:

             序列化的时候系统会把当前类的serialVersionUID 写入序列化的文件中(也可能是其他中介),当反序列化的时候系统会去检测文件中的serialVersionUID ,看它是否和当前类的serialVersionUID 一致,如果一致就说明序列化的类的版本和当前类的版本是相同的,这个时候可以成功反序列化,否则就说明当前类和序列化的类相比发生了某些变换,比如成员变量的数量,类型可能发生了改变,这个时候就会抛异常,反序列化失败。

     

    三、serialVersionUID作用: 

    序列化时为了保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性。 

    那么serialVersionUID 是如何生成,生成规则是怎么样的呢?

     默认情况下,也就是不声明serialVersionUID 属性情况下,系统会按当前类的成员变量计算hash值并赋值给serialVersionUID 。

    所以,结论就出来了。声明serialVersionUID ,可以很大程度上避免反序列化过程的失败。比如当版本升级后,我们可能删除了某个成员变量,也可能增加了一些新的成员变量,这个时候我们的反序列化依然能够成功,程序依然能够最大程度地恢复数据,相反,如果不指定serialVersionUID ,程序就会挂掉。

    如果类结构发生了非常规性改变,比如修改了类名,类型等,这个时候尽管serialVersionUID 验证通过了,但是反序列化过程

    还是会失败,因为类结构有了毁灭性的改变。

     

    四、序列化ID的作用通俗解释

            序列化ID起着关键的作用,java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。反序列化时,JVM会把传来的字节流中的serialVersionUID与本地实体类中的serialVersionUID进行比较,如果相同则认为是一致的,便可以进行反序列化,否则就会报序列化版本不一致的异常。

     序列化id的作用是什么呢,用以下场景来解释它的作用:

           假设两年前我保存了某个类的一个对象,这两年来,我修改该类(类名保持不变),删除了某个属性和增加了另外一个属性,两年后,我又去读取那个保存的对象,会有什么结果?Sun的jdk无法获知你保存的对象到底是未修改前的类的对象还是修改后的类的对象。为此,一个解决办法就是在类中增加版本号信息,每一次类的属性修改,都应该把版本号升级一下,这样,在读取时,比较存储对象时的版本号与当前类的版本号,如果不一致,则直接报版本号不同的错!,此处的版本号其实就是序列化ID

     

    展开全文
  • 遇到这个 Java Serializable 序列化这个接口,我们可能会有如下的问题a,什么叫序列化和反序列化 b,作用。为啥要实现这个 Serializable 接口,也就是为啥要序列化 c,serialVersionUID 这个的值到底是在怎么设置的...

    遇到这个 Java Serializable 序列化这个接口,我们可能会有如下的问题
    a,什么叫序列化和反序列化
    b,作用。为啥要实现这个 Serializable 接口,也就是为啥要序列化
    c,serialVersionUID 这个的值到底是在怎么设置的,有什么用。有的是1L,有的是一长串数字,迷惑ing。

    我刚刚见到这个关键字 Serializable 的时候,就有如上的这么些问题。

    在处理这个问题之前,你要先知道一个问题,这个比较重要。
    这个Serializable接口,以及相关的东西,全部都在 Java io 里面的。
     

    1,序列化和反序列化的概念

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

    上面是专业的解释,现在来点通俗的解释。在代码运行的时候,我们可以看到很多的对象(debug过的都造吧),
    可以是一个,也可以是一类对象的集合,很多的对象数据,这些数据中,
    有些信息我们想让他持久的保存起来,那么这个就叫序列化。
    就是把内存里面的这些对象给变成一连串的字节(bytes)描述的过程。
    常见的就是变成文件
    我不序列化也可以保存文件啥的呀,有什么影响呢?我也是这么问的。

    2,什么情况下需要序列化 

    当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
    当你想用套接字在网络上传送对象的时候;
    当你想通过RMI传输对象的时候;

    (老实说,上面的几种,我可能就用过个存数据库的。)

     

    3,java如何实现序列化

    实现Serializable接口即可

    上面这些理论都比较简单,下面实际代码看看这个序列化到底能干啥,以及会产生的bug问题。

    先上对象代码,飞猪.java

    package com.lxk.model;
    
    import java.io.Serializable;
    
    /**
     * @author lxk on 2017/11/1
     */
    public class FlyPig implements Serializable {
        //private static final long serialVersionUID = 1L;
        private static String AGE = "269";
        private String name;
        private String color;
        transient private String car;
    
        //private String addTip;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getColor() {
            return color;
        }
    
        public void setColor(String color) {
            this.color = color;
        }
    
        public String getCar() {
            return car;
        }
    
        public void setCar(String car) {
            this.car = car;
        }
    
        //public String getAddTip() {
        //    return addTip;
        //}
        //
        //public void setAddTip(String addTip) {
        //    this.addTip = addTip;
        //}
    
        @Override
        public String toString() {
            return "FlyPig{" +
                    "name='" + name + '\'' +
                    ", color='" + color + '\'' +
                    ", car='" + car + '\'' +
                    ", AGE='" + AGE + '\'' +
                    //", addTip='" + addTip + '\'' +
                    '}';
        }
    }
    

    注意下,注释的代码,是一会儿要各种情况下使用的。

    下面就是main方法啦

    package com.lxk.test;
    
    import com.lxk.model.FlyPig;
    
    import java.io.*;
    
    /**
     * 序列化测试
     *
     * @author lxk on 2017/11/1
     */
    public class SerializableTest {
        public static void main(String[] args) throws Exception {
            serializeFlyPig();
            FlyPig flyPig = deserializeFlyPig();
            System.out.println(flyPig.toString());
    
        }
    
        /**
         * 序列化
         */
        private static void serializeFlyPig() throws IOException {
            FlyPig flyPig = new FlyPig();
            flyPig.setColor("black");
            flyPig.setName("naruto");
            flyPig.setCar("0000");
            // ObjectOutputStream 对象输出流,将 flyPig 对象存储到E盘的 flyPig.txt 文件中,完成对 flyPig 对象的序列化操作
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("d:/flyPig.txt")));
            oos.writeObject(flyPig);
            System.out.println("FlyPig 对象序列化成功!");
            oos.close();
        }
    
        /**
         * 反序列化
         */
        private static FlyPig deserializeFlyPig() throws Exception {
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("d:/flyPig.txt")));
            FlyPig person = (FlyPig) ois.readObject();
            System.out.println("FlyPig 对象反序列化成功!");
            return person;
        }
    }
    

    对上面的2个操作文件流的类的简单说明

    ObjectOutputStream代表对象输出流:

    它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。

    ObjectInputStream代表对象输入流:

    它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。

    具体怎么看运行情况。

    第一种:上来就这些代码,不动,直接run,看效果。

    实际运行结果,他会在 d:/flyPig.txt 生成个文件。

    从运行结果上看:

    1,他实现了对象的序列化和反序列化。

    2,transient 修饰的属性,是不会被序列化的。我设置的奥迪四个圈的车不见啦,成了null。my god。

    3,你先别着急说,这个静态变量AGE也被序列化啦。这个得另测。

     

    第二种:为了验证这个静态的属性能不能被序列化和反序列化,可如下操作。

        public static void main(String[] args) throws Exception {
            serializeFlyPig();
            //FlyPig flyPig = deserializeFlyPig();
            //System.out.println(flyPig.toString());
        }

    这个完了之后,意思也就是说,你先序列化个对象到文件了。这个对象是带静态变量的static。

    现在修改flyPig类里面的AGE的值,给改成26吧。

    然后,看下图里面的运行代码和执行结果。

    可以看到,刚刚序列化的269,没有读出来。而是刚刚修改的26,如果可以的话,应该是覆盖这个26,是269才对。

    所以,得出结论,这个静态static的属性,他不序列化。

     

    第三种:示范这个 serialVersionUID 的作用和用法

    最暴力的改法,直接把model的类实现的这个接口去掉。然后执行后面的序列化和反序列化的方法。直接报错。

    抛异常:NotSerializableException

    这个太暴力啦,不推荐这么干。

    然后就是,还和上面的操作差不多,先是单独执行序列化方法。生成文件。
    然后,打开属性 addTip ,这之后,再次执行反序列化方法,看现象。

    抛异常:InvalidClassException  详情如下。

    InvalidClassException: com.lxk.model.FlyPig; 
    local class incompatible: 
    stream classdesc serialVersionUID = -3983502914954951240, 
    local class serialVersionUID = 7565838717623951575

    解释一下:

    因为我再model里面是没有明确的给这个 serialVersionUID 赋值,但是,Java会自动的给我赋值的,

    这个值跟这个model的属性相关计算出来的。

    我保存的时候,也就是我序列化的时候,那时候还没有这个addTip属性呢,

    所以,自动生成的serialVersionUID 这个值,

    在我反序列化的时候Java自动生成的这个serialVersionUID值是不同的,他就抛异常啦。

    (你还可以反过来,带ID去序列化,然后,没ID去反序列化。也是同样的问题。)

    再来一次,就是先序列化,这个时候,把 private static final long serialVersionUID = 1L; 这行代码的注释打开。那个addTip属性先注释掉,序列化之后,再把这个属性打开,再反序列化。看看什么情况。

    这个时候,代码执行OK,一切正常。good。序列化的时候,是没的那个属性的,在发序列化的时候,对应的model多了个属性,但是,反序列化执行OK,没出异常。

     

    这个现象对我们有什么意义:

    老铁,这个意义比较大,首先,你要是不知道这个序列化是干啥的,万一他真的如开头所讲的那样存数据库(这个存db是否涉及到Java的序列化估计还的看什么数据库吧)啦,socket传输啦,rmi传输啦。虽然我也不知道这是干啥的。你就给model bean 实现了个这个接口,你没写这个 serialVersionUID 那么在后来扩展的时候,可能就会出现不认识旧数据的bug,那不就炸啦吗。回忆一下上面的这个出错情况。想想都可怕,这个锅谁来背? 

    所以,有这么个理论,就是在实现这个Serializable 接口的时候,一定要给这个 serialVersionUID 赋值,就是这么个问题。

    这也就解释了,我们刚刚开始编码的时候,实现了这个接口之后,为啥eclipse编辑器要黄色警告,需要添加个这个ID的值。而且还是一长串你都不知道怎么来的数字。

     

    下面解释这个 serialVersionUID 的值到底怎么设置才OK

    首先,你可以不用自己去赋值,Java会给你赋值,但是,这个就会出现上面的bug,很不安全,所以,还得自己手动的来。

    那么,我该怎么赋值,eclipse可能会自动给你赋值个一长串数字。这个是没必要的。

    可以简单的赋值个 1L,这就可以啦。。这样可以确保代码一致时反序列化成功。

    不同的serialVersionUID的值,会影响到反序列化,也就是数据的读取,你写1L,注意L大些。计算机是不区分大小写的,但是,作为观众的我们,是要区分1和L的l,所以说,这个值,闲的没事不要乱动,不然一个版本升级,旧数据就不兼容了,你还不知道问题在哪。。。

     

    下面是摘自 jdk api 文档里面关于接口 Serializable 的描述
    类通过实现 java.io.Serializable 接口以启用其序列化功能。
    未实现此接口的类将无法使其任何状态序列化或反序列化。
    可序列化类的所有子类型本身都是可序列化的。因为实现接口也是间接的等同于继承。
    序列化接口没有方法或字段,仅用于标识可序列化的语义。

    关于 serialVersionUID 的描述

    https://blog.csdn.net/qq_27093465?viewmode=contents

    (注意对比一下,这个截图的两段话,就是对应下面的2段中文。仔细看这2段话,就能解释43楼的问题,静态属性不会被序列化,但是却又有一个特殊的静态属性,会被序列化,没办法,这个静态属性是亲生的。自带的。)

    序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。如果接收者加载的该对象的类的 serialVersionUID 与对应的发送者的类的版本号不同,则反序列化将会导致 InvalidClassException。可序列化类可以通过声明名为 "serialVersionUID" 的字段(该字段必须是静态 (static)、最终 (final) 的 long 型字段)显式声明其自己的 serialVersionUID:

    如果可序列化类未显式声明 serialVersionUID,则序列化运行时将基于该类的各个方面计算该类的默认 serialVersionUID 值,如“Java(TM) 对象序列化规范”中所述。不过,强烈建议 所有可序列化类都显式声明 serialVersionUID 值,原因是计算默认的 serialVersionUID 对类的详细信息具有较高的敏感性,根据编译器实现的不同可能千差万别,这样在反序列化过程中可能会导致意外的 InvalidClassException。因此,为保证 serialVersionUID 值跨不同 java 编译器实现的一致性,序列化类必须声明一个明确的 serialVersionUID 值。还强烈建议使用 private 修饰符显示声明 serialVersionUID(如果可能),原因是这种声明仅应用于直接声明类 -- serialVersionUID 字段作为继承成员没有用处。数组类不能声明一个明确的 serialVersionUID,因此它们总是具有默认的计算值,但是数组类没有匹配 serialVersionUID 值的要求。

     

    最后更新一下

    1,(针对25楼的留言:序列化的类的所有成员变量是不是都要是基本类型或实现Serializable接口的类型?)

    当属性是对象的时候,如果这个对象,没实现序列化接口,那么上面的方法在序列化的时候就在执行oos.writeObject(flyPig)时候,报错了“Exception in thread "main" java.io.NotSerializableException: com.lxk.model.Bird”。然后给刚刚的属性的对象加上实现序列化的接口之后,上面的测试就正常通过了。你这个问题问的好。

    结论:要实现序列化的对象,所有涉及的引用,都需要实现序列化接口才可以。

    2,(38楼问,这个serialVersionUID的值在存数据库的时候,存哪里了?)

    大师兄

    大师兄

    好问题,答不上来呀!!!我也是郁闷了,那大概猜一下,数据库没有使用Java这一套序列化,而是不同db各自实现了一套自己的序列化,so,就跟Java的这个静态属性没关系啦。静态属性是不存db的。然后,你就发现,你即使没实现这个Java的序列化的接口,也可以正常的存db,取db。太机智了,这个解释,满分,为啥呢,这个serialVersionUID是Java给你提供的一个序列化和反序列化用的,你要是不使Java这一套的话,那就不需要考虑这个问题啦呀。在上面的测试代码里面也是使用这些个API去序列化以及反序列化的。???

    3,(43楼问的问题:既然要比较新旧serialVersionUID, 旧的serialVersionUID是不是也应该序列化到文件中, 但serialVersionUID是static类型的, 是不会被序列化到文件中的)

    这个serialVersionUID是jdk亲生的,你写或者不写,只要实现了接口,他就是存在的,在序列化的时候,他还真就序列化,存起来了。不然在反序列化的时候,就没的对比了嘛,因为他是亲生的,所以,咱手动添加的静态,和这个静态是不能比的。

     

    我写完文章,给自己点个赞,不过分吧,
    不过分,那我可就点啦啊。
    我先点为敬,你们随意。大家随意。不要客气。。。

    展开全文
  • 序列化的概念和序列化ID作用

    千次阅读 2017-05-15 23:51:16
     序列化id作用是什么呢,用以下场景来解释它的作用:  假设两年前我 保存了某个类的一个对象 ,这两年来,我 修改该类(类名保持不变) ,删除了某个属性和增加了另外一个属性,两年后,我又去读取那个保存的...
  • 序列化ID是什么?

    千次阅读 2020-11-21 11:22:38
    现在的实体类不单单要实现 Serializable,还要生成一个序列化ID,如下: 那么这个序列化ID是怎么生成的呢? File -> Settings -> Editor -> Inspections -> 搜索Serialization issues ,找到...
  • IDEA自动生成序列化ID

    万次阅读 2017-11-08 12:53:40
    我们知道,实体类继承 java.io.Serializable后,需要设置序列化ID。 java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与...
  • IDEA自动生成序列化ID【UID】

    千次阅读 2018-08-31 11:24:32
    安装插件...【继承序列化接口以后自动提示添加序列化id,类似eclipse】 按以上配置就可以自动在实体类上,按alt + enter 回车键添加序列化id 【第二种方法,直接添加插件就可以,不用配置】...
  • 如何使用idea自动生成序列化id

    万次阅读 多人点赞 2018-05-31 10:40:34
    之前在进行实体类与json转换时报了一个序列化的错误,意识到实体类是需要序列化的,后面我又碰到序列化出错,是因为序列化同一个实体类时会发生冲突,我百度结果,要让我加一个序列化id,但是有些idea没有自带的自动...
  • idea中添加序列化ID

    万次阅读 2020-08-28 22:43:09
    1.idea左上角找到File,进入setting,搜索Serializable 2.选中图中方框右侧的√ 3.双击类或者选中类按ALT+Enter
  • 使用idea自动生成序列化ID

    千次阅读 2020-04-12 11:17:25
    当我们的实体类实现Serializable接口的时候,在idea中如何快速生成序列化ID 如下操作即可: 在右侧的搜索框中输入UID,然后按照下图进行勾选,应用即可。 在idea中,提示快捷键alt+enter即可,或者点击提示...
  • 一.Java序列化作用 有的时候我们想要把一个Java对象变成字节流的形式传出去,有的时候我们想要从一个字节流中恢复一个Java对象。例如,有的时候我们想要 把一个Java对象写入到硬盘或者传输到网路上面的其它...
  • 类继承了Serializable接口之后,使用alt+enter快捷键自动创建序列化id idea默认设置没有开启:alt+enter (不同系统和自定义的配置可能会快捷键可能略有不同)发现并没有提供 进入setting→inspections→...
  • JAVA序列化ID问题

    千次阅读 2018-03-08 13:07:22
    将Java对象序列化为二进制文件的 Java 序列化技术是 Java 系列技术中一个较为重要的技术点,在大部分情况下,开发人员只需要了解被序列化的类需要实现 Serializable 接口,使用ObjectInputStream 和 ...
  • JAVA序列化ID有什么用?

    千次阅读 2018-11-26 14:24:32
    上周一个安卓的兄弟问到了我这个问题,今天我也把这个给...把一个MAP或对象转为JSON字符串,要什么数据就传什么数据,字符串不需要序列化ID的,内容变动对双方没什么影响,只要不删除依懒方所需要的业务字段就可以。
  • Intellij idea用快捷键自动生成序列化id 类继承了Serializable接口之后,使用alt+enter快捷键自动创建序列化id  进入setting→inspections→serialization issues→选择图中的选项。serializable class without ...
  • Idea自动生成序列化ID

    千次阅读 2019-02-19 23:11:03
    Idea自动生成序列化ID File -> Settings -> Editor -> Inspections -> 搜索 Serialization issues ,找到 Serializable class without ‘serialVersionUID’ ->打上勾,Apply-&...
  • 一定要定义序列化ID

    千次阅读 2016-08-20 15:54:12
    Java基础学习总结——Java对象的序列化和反序列化一、序列化和反序列化的概念 把对象转换为字节序列的过程称为对象的序列化。  把字节序列恢复为对象的过程称为对象的反序列化。  对象的序列化主要有两种用途:...
  • idea中如何添加序列化ID

    千次阅读 2019-05-12 21:16:16
    首先找到settings>>Editor>>inspections>> 搜索Serialization issues>>找到序列化id,然后OK。 点击对应的类,然后ALT+Enter回车。就可以设置序列化id ...
  • 序列化和反序列化的底层实现原理是什么?

    万次阅读 多人点赞 2018-04-07 13:53:41
    序列化和反序列化作为Java里一个较为基础的知识点,大家心里也有那么几句要说的,但我相信很多小伙伴掌握的也就是那么几句而已,如果再深究问一下Java如何实现序列化和反序列化的,就可能不知所措了!遥记当年也被问...
  • SpringBoot的序列化和反序列化

    千次阅读 2020-11-11 15:06:16
    序列化与反序列化 1、认识序列化与反序列化 Java序列化是指把Java对象转换为字节序列的过程,而Java反序列化是指把字节序列恢复为Java对象的过程。 2、为什么要实现对象的序列化和反序列化? (1)我们创建的Java...
  • 在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。  当实现java....
  • 为什么要添加序列化版本ID了(serialVersionUID)?通过判断实体类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,...
  • IntelliJIDEA设置生成序列化ID

    千次阅读 2019-04-11 21:10:44
    点击类名,按住键盘Alt+Insert,没有设置,则不会出现序列化ID的选项, 设置:点击File→Settings… 点击图中的Java 在其中找到Serialization issues这一项 勾选这一项:Serializable class without ...
  • 对象序列化需要实现Serializable接口,同时为了使其能够反序列化需要给类添加一个long类型的serialVersionUID 。 我们通常使用IntelliJ IDEA开发时,一个类实现了Serializable接口却没有添加serialVersionUID的提示...
  • Django框架中序列化和反序列化

    千次阅读 多人点赞 2018-09-29 11:35:43
    1.序列化 DRF的核心 就是 前后端分离的核心 前后端分离开发的核心: 将模型转换为json 称之为 序列化 将json转换为模型 称之为 反序列化 1.序列化器的字段 Serializer 序列化器 # 将模型转换为json 称之为 序列...
  • go语言序列化及反序列化

    千次阅读 2020-06-19 22:56:45
    参考文章: ...特别好的文章:理解 Go 中的 JSON ...一、序列化 GO提供了 Marshal 方法:Go Struct转换为JSON对象,函数签名: func Marshal(v interface{}) ([]byte, error) 举例: type Person struct { Name .

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 361,380
精华内容 144,552
关键字:

序列化id作用