• 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

  • 相关阅读:
    innodb的存储结构
    使用zabbix邮件发送报表
    如何使用yum下载rpm包
    redis cluster节点管理测试
    redis迁移工具-redis-migrate-tool使用测试
    redis客户端连接异常
    redis sentinel基本命令与参数
    [转]redis-cli的一些有趣也很有用的功能
    [转]为什么使用 Redis及其产品定位
    Redis多机常用架构-cluster
  • 原文地址:https://www.cnblogs.com/xt0810/p/3642904.html
Copyright © 2020-2023  润新知