程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> JAVA IO 序列化與設計模式,io序列化

JAVA IO 序列化與設計模式,io序列化

編輯:JAVA綜合教程

JAVA IO 序列化與設計模式,io序列化


更多技術干貨請戳:聽雲博客

序列化

什麼是序列化

序列化:保存對象的狀態

反序列化:讀取保存對象的狀態

序列化和序列化是Java提供的一種保存恢復對象狀態的機制

序列化有什麼用

將數據保存到文件或數據庫中時

將數據通過套接字在網絡上傳輸時

通過 RPC RMI等傳輸對象時

如何序列化

實現Serializable接口

實現Externalizable接口

serialVersionUID的作用serialVersionUID建議給一個確定的值,不要由系統自動生成,否則在增減字段(不能修改字段類型及長度)時,如果兩邊的類的版本不同會導致反序列化失敗

默認序列化機制

如果僅僅只是讓某個類實現Serializable接口,而沒有其它任何處理的話,則就是使用默認序列化機制。使用默認機制,在序列化對象時,不僅會序列化當前對象本身,還會對該對象引用的其它對象也進行序列化,同樣地,這些其它對象引用的另外對象也將被序列化,以此類推。所以,如果一個對象包含的成員變量是容器類對象,而這些容器所含有的元素也是容器類對象,那麼這個序列化的過程就會較復雜,開銷也較大。

(01) 序列化對static和transient變量,是不會自動進行狀態保存的。

transient的作用就是,用transient聲明的變量,不會被自動序列化。

(02) 對於Socket, Thread類,不支持序列化。若實現序列化的接口中,有Thread成員;在對該類進行序列化操作時,運行會出錯。

這主要是基於資源分配方面的原因。如果Socket,Thread類可以被序列化,但是被反序列化之後也無法對他們進行重新的資源分配。

示例: 

package ioEx; 
import java.io.FileInputStream;   
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.ObjectInputStream;   
import java.io.ObjectOutputStream;   
import java.io.Serializable; 
import java.util.HashMap; 
import java.util.Iterator; 
import java.util.Map; 
import java.util.Map.Entry;   
public class Serial { 
    private static final String TMP_FILE = "text.txt";
    public static void main(String[] args) {   
        testWrite();
        testRead();
    }
    private static void testWrite() {   
        try {
            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(TMP_FILE)); 
            AnObject box = new AnObject(1, 1, "1");
             out.writeObject(box); 
            out.writeBoolean(true); 
            out.writeByte((byte)65); 
            out.writeChar('a'); 
            out.writeInt(20160415); 
            out.writeFloat(3.14F); 
            out.writeDouble(Math.PI); 
            HashMap<String,String> map = new HashMap<String,String>();
            map.put("a", "a");
            map.put("b", "b");
            map.put("c", "c");
            out.writeObject(map); 
            out.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
private static void testRead() {
        try {
                    ObjectInputStream in = new ObjectInputStream(new FileInputStream(TMP_FILE)); 
            AnObject box = (AnObject) in.readObject();
            System.out.println("testWrite box: " + box); 
            System.out.println("boolean:"+ in.readBoolean());
            System.out.println("byte:" + (in.readByte()&0xff)); 
            System.out.println("char:" + in.readChar());
            System.out.println("int:" + in.readInt());
            System.out.println("float:" + in.readFloat());
            System.out.println("double:" + in.readDouble());
            // 讀取HashMap對象
            HashMap<String,String> map = (HashMap<String,String>) in.readObject();
            Iterator<Entry<String, String>> iter = map.entrySet().iterator();
            while (iter.hasNext()) {
                Map.Entry<String,String> entry = (Entry<String, String>)iter.next();
                System.out.println(entry.getKey()+"--"+ entry.getValue());
            }
            in.close();
        } catch (Exception e) {
         e.printStackTrace();
        }
    }
}
class AnObject implements Serializable {

    private int obja;   
    private int objb; 
    private String objc;   
    private static int statica;   
    private transient int transienda; 
    
    //必須用static或transient修飾才可能序列化,否則運行報錯
    private static transient Thread  thread = new Thread() {
        @Override
        public void run() {
            System.out.println("Serializable");
             }
    };

    public AnObject(int obja, int objb, String objc) {
        this.obja = obja;
        this.objb = objb;
        this.objc = objc;
        this.statica=obja;
        this.transienda=obja;
    }
    
    //如果要使transient序列化要重寫writeObject,和readObject 方法
//    private void writeObject(ObjectOutputStream out) throws IOException{ 
//        out.defaultWriteObject();
//        out.writeInt(transienda); 
//    }
//
//    private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException{ 
//        in.defaultReadObject();
//        transienda = in.readInt(); 
//    }

    @Override
    public String toString() {
        return "obja:"+obja+","+ "objb:"+objb+","+ "objc:"+objc+","+ "statica:"+statica+","+ "transienda:"+transienda;
    }
}

JAVA IO的設計模式 

JAVA IO框架主要使用的兩種設計模式 裝飾模式和適配器模式

裝飾模式又名包裝(Wrapper)模式

裝飾模式以對客戶端透明的方式擴展對象的功能,是繼承關系的一個替代方案。

  • 裝飾模式通過創建一個包裝對象,也就是裝飾,來包裹真實的對象。

  • 裝飾模式以對客戶端透明的方式動態地給一個對象附加上更多的責任。換言之,客戶端並不會覺得對象在裝飾前和裝飾後有什麼不同。

  • 裝飾模式可以在不創造更多子類的情況下,將對象的功能加以擴展。

  • 裝飾模式把客戶端的調用委派到被裝飾類。裝飾模式的關鍵在於這種擴展是完全透明的。 

裝飾模式的角色

  • 抽象構件角色(Component):給出一個抽象接口,以規范准備接收附加責任的對象。

  • 具體構件角色(Concrete Component):定義將要接收附加責任的類。

  • 裝飾角色(Decorator):持有一個構件(Component)對象的引用,並定義一個與抽象構件接口一致的接口。

  • 具體裝飾角色(Concrete Decorator):負責給構件對象“貼上”附加的責任。

裝飾模式的特點

  • 裝飾對象和真實對象有相同的接口。這樣客戶端對象就可以以和真實對象相同的方式和裝飾對象交互。

  • 裝飾對象包含一個真實對象的引用(reference)。

  • 裝飾對象接收所有來自客戶端的請求,它把這些請求轉發給真實的對象。

  • 裝飾對象可以在轉發這些請求之前或之後附加一些功能。

這樣就確保了在運行時,不用修改給定對象的結構就可以在外部增加附加的功能。

示例:

package Test;

public class Wrapper {
    public static void main(String[] args) {
        Component c = new RealizeComponent();
        Component c1 = new RealizeDecorator1(c);
        c1.say();
        System.out.println("--");
        Component c2 = new RealizeDecorator2(c1);
        c2.say();
    }
}
interface Component{
    public void say();
}
class RealizeComponent implements Component{
    @Override
    public void say() {
        System.out.println("A");
        }
}
class Decorator implements Component{
    private Component component;
    public Decorator(Component component) {
        this.component = component;
    }
    @Override
    public void say(){
        component.say();
    }
}
class RealizeDecorator1 extends Decorator{
    public RealizeDecorator1(Component component){
        super(component);
    }
    @Override
    public void say(){
        super.say();
        this.sayAnother();
    }
     private void sayAnother(){
        System.out.println("B");
    }
}
class RealizeDecorator2 extends Decorator{
    public RealizeDecorator2(Component component){
        super(component);
    }
    @Override
    public void say(){
        super.say();
        this.sayAnother();
    }
    private void sayAnother(){
        System.out.println("C");
    }
}

裝飾模式的優點

裝飾模式與繼承關系的目的都是要擴展對象的功能,但是裝飾模式可以提供比繼承更多的靈活性。裝飾模式允許系統動態決定“貼上”一個需要的“裝飾”,或者除掉一個不需要的“裝飾”。繼承關系則不同,繼承關系是靜態的,它在系統運行前就決定了。

通過使用不同的具體裝飾類以及這些裝飾類的排列組合,設計師可以創造出很多不同行為的組合。

裝飾模式的缺點

由於使用裝飾模式,可以比使用繼承關系需要較少數目的類。使用較少的類,當然使設計比較易於進行。但是,在另一方面,使用裝飾模式會產生比使用繼承關系更多的對象。更多的對象會使得查錯變得困難,特別是這些對象看上去都很相像。

JAVA IO中的裝飾模式

抽象構件(Component)角色:由InputStream扮演。這是一個抽象類,為各種子類型提供統一的接口。

具體構件(ConcreteComponent)角色:由ByteArrayInputStream、FileInputStream、PipedInputStream、StringBufferInputStream等類扮演。它們實現了抽象構件角色所規定的接口。

抽象裝飾(Decorator)角色:由FilterInputStream扮演。它實現了InputStream所規定的接口。

具體裝飾(ConcreteDecorator)角色:由幾個類扮演,分別是BufferedInputStream、DataInputStream以及兩個不常用到的類LineNumberInputStream、PushbackInputStream。

適配器模式

34.4.png 

上圖是適配器模式的類圖。Adapter 適配器設計模式中有 3 個重要角色:被適配者 Adaptee,適配器 Adapter 和目標對象 Target。其中兩個現存的想要組合到一起的類分別是被適配者 Adaptee 和目標對象 Target 角色,按照類圖所示,我們需要創建一個適配器 Adapter 將其組合在一起。

最簡單的適配器示例:

package AdapterEx;

class Apple {
    public void getAColor(String str) {
        System.out.println("Apple color is: " + str);
    }
}
class Orange {
    public void getOColor(String str) {
        System.out.println("Orange color is: " + str);
    }
}
class AppleAdapter extends Apple {
    private Orange orange;
 
    public AppleAdapter(Orange orange) {
        this.orange = orange;
    }
 
    public void getAColor(String str) {
        orange.getOColor(str);
    }
    }
public class AdapterEx {
    public static void main(String[] args) {
        Apple apple = new Apple();
        apple.getAColor("green");
        Orange orange = new Orange();
        AppleAdapter aa = new AppleAdapter(orange);
        aa.getAColor("red");
    }
}

Java I/O 庫大量使用了適配器模式,例如 ByteArrayInputStream 是一個適配器類,它繼承了 InputStream 的接口,並且封裝了一個 byte 數組。換言之,它將一個 byte 數組的接口適配成 InputStream 流處理器的接口。

我們知道 Java 語言支持四種類型:Java 接口,Java 類,Java 數組,原始類型(即 int,float 等)。前三種是引用類型,類和數組的實例是對象,原始類型的值不是對象。也即,Java 語言的數組是像所有的其他對象一樣的對象,而不管數組中所存儲的元素類型是什麼。這樣一來的話,ByteArrayInputStream 就符合適配器模式的描述,是一個對象形式的適配器類。FileInputStream 是一個適配器類。在 FileInputStream 繼承了 InputStrem 類型,同時持有一個對 FileDiscriptor 的引用。這是將一個 FileDiscriptor 對象適配成 InputStrem 類型的對象形式的適配器模式。

同樣地,在 OutputStream 類型中,所有的原始流處理器都是適配器類。ByteArrayOutputStream 繼承了 OutputStream 類型,同時持有一個對 byte 數組的引用。它一個 byte 數組的接口適配成 OutputString 類型的接口,因此也是一個對象形式的適配器模式的應用。

FileOutputStream 繼承了 OutputStream 類型,同時持有一個對 FileDiscriptor 對象的引用。這是一個將 FileDiscriptor 接口適配成 OutputStream 接口形式的對象型適配器模式。

Reader 類型的原始流處理器都是適配器模式的應用。StringReader 是一個適配器類,StringReader 類繼承了 Reader 類型,持有一個對 String 對象的引用。它將 String 的接口適配成 Reader 類型的接口。

裝飾模式和適配器模式的對比

(1)裝飾模式和適配器模式,都是通過封裝其他對象達到設計目的的。

(2)理想的裝飾模式在對被裝飾對象進行功能增強時,要求具體構件角色、裝飾角色的接口與抽象構件角色的接口完全一致;而適配器模式則不然,一般而言,適配器模式並不要求對源對象的功能進行增強,只是利用源對象的功能而已,但是會改變源對象的接口,以便和目標接口相符合。

(3)裝飾模式有透明和半透明兩種,區別就在於接口是否完全一致。關於裝飾模式的重要的事實是,很難找到理想的裝飾模式。一般而言,對一個對象進行功能增強的同時,都會導致加入新的行為,因此,裝飾角色的接口比抽象構件角色的接口寬是很難避免的,這種現象存在於Java I/O庫中多有的類型的鏈接流處理器中。一個裝飾類提供的新的方法越多,它離純裝飾模式的距離就越遠,離適配器模式的距離也就越近。

 

原文鏈接:http://blog.tingyun.com/web/article/detail/460

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved