首頁  >  文章  >  Java  >  Java 物件序列化 NIO NIO2詳解

Java 物件序列化 NIO NIO2詳解

高洛峰
高洛峰原創
2017-02-27 15:43:461424瀏覽

Java 物件序列化NIO NIO2詳細介紹及解析

#概要:

物件序列化

物件序列化機制允許把記憶體中的Java物件轉換成與平台無關的二進位流,從而可以保存到磁碟或進行網路傳輸,其它程式獲得這個二進位流後可以將其恢復成原來的Java對象。 序列化機制可以使物件可以脫離程式的運作而對立存在

序列化的意義與意義

序列化

序列化機制可以使物件可以脫離程式的運行而對立存在

序列化(Serialize)指將一個java物件寫入IO流中,與此對應的是,物件的反序列​​化(Deserialize)則指從IO流中恢復該java物件

如果需要讓某個物件可以支援序列化機制,必須讓它的類別是可序列化(serializable),為了讓某個類別可序列化的,必須實現如下兩個接口之一:

  • Serializable:標記接口,實現該接口無須實現任何方法,只是表明該類的實例是可序列化的

  • Externalizable

所有在網路上傳輸的物件都應該是可序列化的,否則將會出現異常;所有需要保存到磁碟裡的物件的類別都必須可序列化;程式創建的每個JavaBean類別都實作Serializable;

#使用物件流實作序列化

實作Serializable實作序列化的類,程式可以透過以下兩個步驟來序列化該物件:

1.建立一個ObjectOutputStream,這個輸出流是一個處理流,所以必須建立在其他節點流的基礎之上

// 创建个ObjectOutputStream输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));

2.呼叫ObjectOutputStream物件的writeObject方法輸出可序列化物件

// 将一个Person对象输出到输出流中
oos.writeObject(per);

定義一個NbaPlayer類,實作Serializable接口,該介面標識該類別的物件是可序列化的

public class NbaPlayer implements java.io.Serializable
{
  private String name;
  private int number;
  // 注意此处没有提供无参数的构造器!
  public NbaPlayer(String name, int number)
  {
    System.out.println("有参数的构造器");
    this.name = name;
    this.number = number;
  }

  // name的setter和getter方法
  public void setName(String name)
  {
    this.name = name;
  }
  public String getName()
  {
    return this.name;
  }

  // number的setter和getter方法
  public void setNumber(int number)
  {
    this.number = number;
  }
  public int getNumber()
  {
    return this.number;
  }
}

使用ObjectOutputStream將一個NbaPlayer物件寫入磁碟檔案

import java.io.*;

public class WriteObject
{
  public static void main(String[] args)
  {
    try(
      // 创建一个ObjectOutputStream输出流
      ObjectOutputStream oos = new ObjectOutputStream(
        new FileOutputStream("object.txt")))
    {
      NbaPlayer player = new NbaPlayer("维斯布鲁克", 0);
      // 将player对象写入输出流
      oos.writeObject(player);
    }
    catch (IOException ex)
    {
      ex.printStackTrace();
    }
  }
}

反序列化

從二進位流恢復Java對象,則需要使用反序列化,程式可以透過如下兩個步驟來序列化該物件:

1.建立一個ObjectInputStream輸入流,這個輸入流是一個處理流,所以必須建立在其他節點流的基礎之上

// 创建个ObjectInputStream输出流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));

2.呼叫ObjectInputStream物件的readObject()方法讀取流中的對象,該方法傳回一個Object類型的Java對象,可進行強制類型轉換成其真實的類型

// 从输入流中读取一个Java对象,并将其强制类型转换为Person类
Person p = (Person)ois.readObject();

從object.txt檔案讀取NbaPlayer物件的步驟

import java.io.*;
public class ReadObject
{
  public static void main(String[] args)
  {
    try(
      // 创建一个ObjectInputStream输入流
      ObjectInputStream ois = new ObjectInputStream(
        new FileInputStream("object.txt")))
    {
      // 从输入流中读取一个Java对象,并将其强制类型转换为NbaPlayer类
      NbaPlayer player = (NbaPlayer)ois.readObject();
      System.out.println("名字为:" + player.getName()
        + "\n号码为:" + player.getNumber());
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
  }
}

反序列化讀取的只是Java物件的數據,而不是Java類,因此採用反序列化恢復Java物件時,必須提供Java物件所屬的class文件,否則會引發ClassNotFoundException異常;反序列化機制無須透過建構器來初始化Java物件

如果使用序列化機制向檔案中寫入了多個Java對象,則使用反序列化機制來復原物件必須按照實際寫入的順序讀取。當一個可序列化類別有多個父類別時(包括直接父類別和間接父類別),這些父類別要么有無參的建構器,要么也是可序列化的—否則反序列化將拋出InvalidClassException異常。如果父類別是不可序列化的,只是帶有無參數的建構器,則該父類別定義的Field值不會被序列化到二進位流中

物件引用的序列化

如果某個類別的Field類型不是基本型別或String型,而是另一個參考型,那麼這個參考型別必須是可序列化的,否則有用該型別的Field的類別也是不可序列化的

public class AllStar implements java.io.Serializable
{
  private String name;
  private NbaPlayer player;
  public AllStar(String name, NbaPlayer player)
  {
    this.name = name;
    this.player = player;
  }
  // 此处省略了name和player的setter和getter方法

  // name的setter和getter方法
  public String getName()
  {
    return this.name;
  }

  public void setName(String name)
  {
    this.name = name;
  }

  // player的setter和getter方法
  public NbaPlayer getPlayer() 
  {
    return player;
  }

  public void setPlayer(NbaPlayer player) 
  {
    this.player = player;
  }
}

Java特殊的序列化演算法

  • 所有儲存到磁碟中的物件都有一個序列化編號

  • 當程式試圖序列化一個物件時,程式將先檢查該物件是否已經被序列化過,只有該物件從未(在本次虛擬中機)被序列化過,系統才會將該物件轉換成位元組序列並輸出

  • 如果某個物件已經序列化過,程式將只是直接輸出一個序列化編號,而不是再次重新序列化該物件

#
import java.io.*;
public class WriteAllStar
{
  public static void main(String[] args)
  {
    try(
      // 创建一个ObjectOutputStream输出流
      ObjectOutputStream oos = new ObjectOutputStream(
        new FileOutputStream("allStar.txt")))
    {
      NbaPlayer player = new NbaPlayer("詹姆斯哈登", 13);
      AllStar allStar1 = new AllStar("西部全明星", player);
      AllStar allStar2 = new AllStar("首发后卫", player);
      // 依次将四个对象写入输出流
      oos.writeObject(allStar1);
      oos.writeObject(allStar2);
      oos.writeObject(player);
      oos.writeObject(allStar2);
    }
    catch (IOException ex)
    {
      ex.printStackTrace();
    }
  }
}

4個寫入輸出流的對象,實際上只序列化了3個,而且序列的兩個AllStar物件的player引用實際上是同一個NbaPlayer物件。以下程式讀取序列化檔案中的物件

import java.io.*;
public class ReadAllStar
{
  public static void main(String[] args)
  {
    try(
      // 创建一个ObjectInputStream输出流
      ObjectInputStream ois = new ObjectInputStream(
        new FileInputStream("allStar.txt")))
    {
      // 依次读取ObjectInputStream输入流中的四个对象
      AllStar star1 = (AllStar)ois.readObject();
      AllStar star2 = (AllStar)ois.readObject();
      NbaPlayer player = (NbaPlayer)ois.readObject();
      AllStar star3 = (AllStar)ois.readObject();
      // 输出true
      System.out.println("star1的player引用和player是否相同:"
        + (star1.getPlayer() == player));
      // 输出true
      System.out.println("star2的player引用和player是否相同:"
        + (star2.getPlayer() == player));
      // 输出true
      System.out.println("star2和star3是否是同一个对象:"
        + (star2 == star3));
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
  }
}

如果多次序列化同一個可變Java物件時,只有第一次序列化時才會把該Java物件轉換成位元組序列並輸出

当使用Java序列化机制序列化可变对象时,只有第一次调用WriteObject()方法来输出对象时才会将对象转换成字节序列,并写入到ObjectOutputStream;即使在后面程序中,该对象的实例变量发生了改变,再次调用WriteObject()方法输出该对象时,改变后的实例变量也不会被输出

import java.io.*;

public class SerializeMutable
{
  public static void main(String[] args)
  {

    try(
      // 创建一个ObjectOutputStream输入流
      ObjectOutputStream oos = new ObjectOutputStream(
        new FileOutputStream("mutable.txt"));
      // 创建一个ObjectInputStream输入流
      ObjectInputStream ois = new ObjectInputStream(
        new FileInputStream("mutable.txt")))
    {
      NbaPlayer player = new NbaPlayer("斯蒂芬库里", 30);
      // 系统会player对象转换字节序列并输出
      oos.writeObject(player);
      // 改变per对象的name实例变量
      player.setName("塞斯库里");
      // 系统只是输出序列化编号,所以改变后的name不会被序列化
      oos.writeObject(player);
      NbaPlayer player1 = (NbaPlayer)ois.readObject();  //①
      NbaPlayer player2 = (NbaPlayer)ois.readObject();  //②
      // 下面输出true,即反序列化后player1等于player2
      System.out.println(player1 == player2);
      // 下面依然看到输出"斯蒂芬库里",即改变后的实例变量没有被序列化
      System.out.println(player2.getName());
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
  }
}

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

更多Java 对象序列化 NIO NIO2详解相关文章请关注PHP中文网!


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn