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("\n\t"); 

 94         sb.append("username=" + this.username); 

 95         sb.append("\n\t"); 

 96         sb.append("cardNumber=" + this.cardNumber); 

 97         sb.append("\n\t"); 

 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

你可能感兴趣的:(java序列化)