Java学习笔记(十七)——java序列化

【前面的话】

      做项目总是要用到很多东西,遇到一个新的知识,并不是这个知识出来的时间短,而是对于自己来说是新的,所以就需要自己去学习,希望今后可以提高学习的效率。

      这篇文章是关于Java 序列化的,选择性阅读。

【知识点】

一、什么叫序列化?

      我们都知道对象是暂时保存在内存中的,不能用U盘考走了,有时为了使用介质转移对象,并且把对象的状态保持下来,就需要把对象保存下来,这个过程就叫做序列化,通俗点,就是把人的魂(对象)收伏成一个石子(可传输的介质)。

二、什么叫反序列化?

      就是再把介质中的东西还原成对象,把石子还原成人的过程。

三、可能的使用情况

      1. 当你想把的内存中的对象写入到硬盘的时候;

      比如说你的内存不够用了,那计算机就要将内存里面的一部分对象暂时的保存到硬盘中,等到要用的时候再读入到内存中,硬盘的那部分存储空间就是所谓的虚拟内存。在比如过你要将某个特定的对象保存到文件中,我隔几天在把它拿出来用,那么这时候就要实现Serializable接口;

      2. 当你想用套接字在网络上传送对象的时候;

      在进行java的Socket编程的时候,你有时候可能要传输某一类的对象,那么也就要实现Serializable接口;最常见的你传输一个字符串,它是JDK里面的类,也实现了Serializable接口,所以可以在网络上传输。

      3. 当你想通过RMI传输对象的时候;

      如果要通过远程的方法调用(RMI)去调用一个远程对象的方法,如在计算机A中调用另一台计算机B的对象的方法,那么你需要通过JNDI服务获取计算机B目标对象的引用,将对象从B传送到A,就需要实现序列化接口。

四、Serializable接口

  1. Serializable接口:一个对象序列化的接口,一个类只有实现了Serializable接口,它的对象才是可序列化的。因此如果要序列化某些类的对象,这些类就必须实现Serializable接口。
  2. Serializable实现代码:
1 public interface Serializable {
2 }

      可以看出Serializable接口是一个空的接口,目的只有一个就是表示一个类的对象可以被序列化。这个标签是类可以被序列化的特性,表示这个类可以被序列化。

五、Externalizable接口

  1. Externalizable接口:他是Serializable接口的子类
  2. Externalizable实现代码:
1 public interface Externalizable extends java.io.Serializable {
2     void writeExternal(ObjectOutput out) throws IOException;
3     void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
4 }

六、Serializable接口和Externalizable接口区别

  1. Serializable一个对象想要被序列化,那么它的类就要实现此接口,这个对象的所有属性(包括private属性、包括其引用的对象)都可以被序列化和反序列化来保存、传递。
  2. Externalizable他是Serializable接口的子类,有时我们不希望序列化那么多,可以使用这个接口,这个接口的writeExternal()和readExternal()方法可以指定序列化哪些属性;

【学习demo And 解释】

一、Java实现序列化

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

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

      3. 对象序列化包括如下步骤:

         1)创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;

         2)通过对象输出流的writeObject()方法写对象。

      4. 对象反序列化的步骤如下:

         1)创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;

         2)通过对象输入流的readObject()方法读取对象。

      5. 代码实现:

       SerializableTest.java

 1 import java.io.*;
 2 import java.util.Date;
 3 
 4 public class SerializableTest {
 5     
 6     public static void main(String[] args) throws Exception {
 7         ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("objectFile.obj"));
 8         //序列化对象
 9         Customer customer = new Customer("阿蜜果", 24);
10         out.writeObject("你好!");
11         out.writeObject(new Date());
12         out.writeObject(customer);//写入实现了序列化的对象
13         out.writeInt(123); //写入基本类型数据
14         out.close();
15         //反序列化对象
16         ObjectInputStream in = new ObjectInputStream(new FileInputStream("objectFile.obj"));
17         System.out.println("obj1=" + (String) in.readObject());
18         System.out.println("obj2=" + (Date) in.readObject());
19         Customer obj3 = (Customer) in.readObject();
20         System.out.println("obj3=" + obj3);
21         int obj4 = in.readInt();
22         System.out.println("obj4=" + obj4);
23         in.close();
24         }
25     }
26 class Customer implements Serializable {
27     private String name;
28     private int age;
29     public Customer(String name, int age) {
30         this.name = name;
31         this.age = age;
32         }
33     public String toString() {
34         return "name=" + name + ", age=" + age;
35         }
36     }

      6. 运行结果:

1 obj1=你好!
2 obj2=Thu Apr 03 09:12:09 CST 2014
3 obj3=name=阿蜜果, age=24
4 obj4=123

二、Java实现序列化的时候使用关键字

      1. transient是Java语言的关键字,用来表示一个域不是该对象序列化的一部分。当一个对象被序列化的时候,transient型变量的值不包括在序列化的表示中,然而非transient型的变量是被包括进去的。  

      2. demo代码

     其中Password定义为transient型,在输出的时候,就会不被序列化。输出null。

     SerializableTest.java

  1 import java.io.*; 
  2 
  3 public class SerializableTest { 
  4     public static void main(String args[]) { 
  5         testObjectSeri(); 
  6         testObjectInSeri(); 
  7     } 
  8     /** 
  9      * 对象序列化测试 
 10      */ 
 11     public static void testObjectSeri() { 
 12         Person person = new Person("熔岩", "341022225562156", "lavasoft"); 
 13         FileOutputStream fos = null; 
 14         ObjectOutputStream oos = null; 
 15         try { 
 16             fos = new FileOutputStream("person.dat"); 
 17             oos = new ObjectOutputStream(fos); 
 18             oos.writeObject(person); 
 19         } catch (FileNotFoundException e) { 
 20             System.out.println("找不到指定的文件!"); 
 21             e.printStackTrace(); 
 22         } catch (IOException e) { 
 23             e.printStackTrace(); 
 24         } finally { 
 25             try { 
 26                 oos.flush(); 
 27                 oos.close(); 
 28             } catch (IOException e) { 
 29                 e.printStackTrace(); 
 30             } 
 31         } 
 32     } 
 33     /** 
 34      * 对象反序列化测试 
 35      */ 
 36     public static void testObjectInSeri() { 
 37         FileInputStream fis = null; 
 38         ObjectInputStream ois = null; 
 39         Person person = null; 
 40         try { 
 41             fis = new FileInputStream("person.dat"); 
 42             ois = new ObjectInputStream(fis); 
 43             person = (Person) ois.readObject(); 
 44         } catch (FileNotFoundException e) { 
 45             e.printStackTrace(); 
 46         } catch (IOException e) { 
 47             e.printStackTrace(); 
 48         } catch (ClassNotFoundException e) { 
 49             e.printStackTrace(); 
 50         } finally { 
 51             try { 
 52                 ois.close(); 
 53             } catch (IOException e) { 
 54                 e.printStackTrace(); 
 55             } 
 56         } 
 57         System.out.println(person.toString()); 
 58     } 
 59 } 
 60 /** 
 61 * 测试序列化所用的类 
 62 */ 
 63 class Person implements Serializable { 
 64     private String username; 
 65     private String cardNumber; 
 66     private transient String password; 
 67     public Person(String username, String cardNumber, String password) { 
 68         this.username = username; 
 69         this.cardNumber = cardNumber; 
 70         this.password = password; 
 71     } 
 72     public String getUsername() { 
 73         return username; 
 74     } 
 75     public void setUsername(String username) { 
 76         this.username = username; 
 77     } 
 78     public String getCardNumber() { 
 79         return cardNumber; 
 80     } 
 81     public void setCardNumber(String cardNumber) { 
 82         this.cardNumber = cardNumber; 
 83     } 
 84     public String getPassword() { 
 85         return password; 
 86     } 
 87     public void setPassword(String password) { 
 88         this.password = password; 
 89     } 
 90     public String toString() { 
 91         StringBuffer sb = new StringBuffer(this.getClass().getName()); 
 92         sb.append("["); 
 93         sb.append("
	"); 
 94         sb.append("username=" + this.username); 
 95         sb.append("
	"); 
 96         sb.append("cardNumber=" + this.cardNumber); 
 97         sb.append("
	"); 
 98         sb.append("password=" + this.password); 
 99         sb.append("]"); 
100         return sb.toString(); 
101     } 
102 }

  4. 运行结果:

1 Person[
2     username=熔岩
3     cardNumber=341022225562156
4     password=null]

【几个问题】

一、序列化版本serialVersionUID

      1. serialVersionUID作用: 

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

        类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的serialVersionUID,也有可能相同。为了提高serialVersionUID的独立性和确定性,强烈建议在一个可序列化类中显示的定义serialVersionUID,为它赋予明确的值。显式地定义serialVersionUID有两种用途:

       1)在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;

       2)在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。

      2. 有两种生成方式: 

        一个是默认的1L,比如:private static final long serialVersionUID = 1L;

        一个是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,比如: 

        private static final long serialVersionUID = xxxxL; 

       当你一个类实现了Serializable接口,如果没有定义serialVersionUID,Eclipse会提供这个 ,提示功能告诉你去定义 。在Eclipse中点击类中warning的图标一下,Eclipse就会自动给定两种生成的方式。如果不想定义它,在Eclipse的设置中也 可以把它关掉的,设置如下: 

       Window ==> Preferences ==> Java ==> Compiler ==> Error/Warnings ==> Potential programming problems 

       将Serializable class without serialVersionUID的warning改成ignore即可。 

二、其他说明:

  1. 基本类型的数据可以直接序列化
  2. 对象要被序列化,它的类必须要实现Serializable接口;如果一个类中有引用类型的实例变量,这个引用类型也要实现Serializable接口。

      如果不想让引用类实现Serializable接口,并且让本类成功序列化也可以,使用transient关键字。

【参考资料】

  1. Serializable 作用
  2. Java对象的序列化和反序列化实践
  3. java.io 序列化 总结(一)---Serializable 和 Externalizable 的区别与联系

【后面的话】

     随波逐流虽易,努力生活不易,且行且珍惜。

——TT

原文地址:https://www.cnblogs.com/xt0810/p/3642904.html