首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java IO 體系中裝飾器模式深度解析:從源碼看 FilterOutputStream 如何實現動態功能增強

     編輯:Java 相關知識

JDK 大量運用設計模式保證類庫可擴展性,java.io 包是裝飾器模式的教科書級應用。本文通過源碼級分析 OutputStream 體系,揭示 FilterOutputStream 如何作為抽象裝飾器,讓 BufferedOutputStream、DataOutputStream 等具體裝飾器動態組合功能,並指導你實現自定義輸出流。

裝飾器模式核心結構回顧

  • Component:定義核心介面(OutputStream)
  • ConcreteComponent:基礎實現(FileOutputStream、ByteArrayOutputStream)
  • Decorator:抽象裝飾器,持有 Component 引用,轉發請求(FilterOutputStream)
  • ConcreteDecorator:具體裝飾,轉發前後增強行為(BufferedOutputStream、DataOutputStream)

OutputStream 體系源碼級裝飾器結構

1. Component:OutputStream 抽象基類


public abstract class OutputStream implements Closeable, Flushable {
    public abstract void write(int b) throws IOException;  // 核心寫入契約
    public void write(byte[] b) throws IOException { ... } // 默認委託單字節寫入
    public void write(byte[] b, int off, int len) throws IOException { ... }
    public void flush() throws IOException { }
    public void close() throws IOException { }
}

2. ConcreteComponent:基礎輸出流

FileOutputStream:寫入文件;ByteArrayOutputStream:寫入內存字節數組(自動擴容);PipedOutputStream:線程間管道輸出。它們直接繼承 OutputStream,實現 write(int b) 完成實際輸出。

3. Decorator:FilterOutputStream 抽象裝飾器


public class FilterOutputStream extends OutputStream {
    protected final OutputStream out;  // 持有被裝飾對象引用

    protected FilterOutputStream(OutputStream out) {
        this.out = out;
    }

    // 默認轉發實現:無增強,單純透傳
    public void write(int b) throws IOException {
        out.write(b);
    }
    public void write(byte[] b) throws IOException {
        out.write(b);
    }
    public void write(byte[] b, int off, int len) throws IOException {
        out.write(b, off, len);
    }
    public void flush() throws IOException { out.flush(); }
    public void close() throws IOException { out.close(); }
    // ... 其他方法均轉發
}

關鍵設計:構造器強制傳入 OutputStream,編譯期保證被裝飾對象存在;所有核心方法默認轉發,**子類只需覆寫需增強的方法**,遵循開閉原則。

4. ConcreteDecorator:具體裝飾器實現

BufferedOutputStream:緩衝寫入增強


public class BufferedOutputStream extends FilterOutputStream {
    private byte[] buf;
    private int count;

    public BufferedOutputStream(OutputStream out) {
        this(out, 8192);  // 默認 8KB 緩衝區
    }

    @Override
    public synchronized void write(int b) throws IOException {
        if (count >= buf.length) flushBuffer();  // 滿則刷新
        buf[count++] = (byte) b;
    }

    @Override
    public synchronized void write(byte[] b, int off, int len) throws IOException {
        if (len >= buf.length) {  // 大數據直接寫入底層,避免雙重拷貝
            flushBuffer();
            out.write(b, off, len);
            return;
        }
        if (len > buf.length - count) flushBuffer();
        System.arraycopy(b, off, buf, count, len);
        count += len;
    }

    private void flushBuffer() throws IOException {
        if (count > 0) {
            out.write(buf, 0, count);
            count = 0;
        }
    }
    // flush/close 時調用 flushBuffer()
}

DataOutputStream:基本類型序列化增強


public class DataOutputStream extends FilterOutputStream implements DataOutput {
    public DataOutputStream(OutputStream out) { super(out); }

    @Override public void writeInt(int v) throws IOException {
        out.write((v >>> 24) & 0xFF);
        out.write((v >>> 16) & 0xFF);
        out.write((v >>> 8) & 0xFF);
        out.write(v & 0xFF);
    }
    // writeLong, writeDouble, writeUTF 等類似
}

實戰:自定義裝飾器 - 過濾空格輸出流

需求

寫入數據時自動過濾空格字符(' '),如 "java io" → "javaio"。

實現


import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class SkipSpaceOutputStream extends FilterOutputStream {

    public SkipSpaceOutputStream(OutputStream out) {
        super(out);
    }

    @Override
    public void write(int b) throws IOException {
        if (b != ' ') {  // 過濾空格
            super.write(b);  // 轉發給被裝飾流
        }
    }

    // 為性能考慮,亦可覆寫 write(byte[], int, int) 批量過濾
    @Override
    public void write(byte[] b, int off, int len) throws IOException {
        for (int i = off; i < off + len; i++) {
            if (b[i] != ' ') {
                out.write(b[i]);
            }
        }
    }
}

測試驗證


public class TestSkipSpace {
    public static void main(String[] args) throws IOException {
        byte[] input = "a b c d e f".getBytes();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        OutputStream out = new SkipSpaceOutputStream(new DataOutputStream(baos));

        out.write(input);
        out.close();

        System.out.println(baos.toString());  // 輸出: abcdef
    }
}

裝飾器鏈組合示例


// 鏈式裝飾:文件 → 緩衝 → 數據類型序列化 → 空格過濾
OutputStream out = new SkipSpaceOutputStream(
                        new DataOutputStream(
                            new BufferedOutputStream(
                                new FileOutputStream("test.txt"))));

out.write("h e l l o".getBytes());  // 文件實際寫入: hello
out.close();

每層裝飾器只關注單一職責,組合靈活,運行時動態決定功能疊加順序。

雙向體系:InputStream / Reader / Writer 同構設計

輸出體系輸入體系字符體系(輸出)字符體系(輸入)
OutputStreamInputStreamWriterReader
FilterOutputStreamFilterInputStreamFilterWriterFilterReader
BufferedOutputStreamBufferedInputStreamBufferedWriterBufferedReader
DataOutputStreamDataInputStreamPrintWriter-
PrintStream---
ObjectOutputStreamObjectInputStream--
GZIPOutputStreamGZIPInputStream--
CipherOutputStreamCipherInputStream--

所有體系均遵循:抽象基類 → 具體基礎流 → 抽象裝飾器 → 具體功能裝飾器 結構。

設計模式對比與選型指南

模式適用場景IO 體系例子
裝飾器動態組合單一職責功能,運行時決定行為Buffered + DataOutput + GZIP 任意組合
繼承靜態擴展,子類化行為固定FileOutputStream 繼承 OutputStream
適配器介面不兼容轉換OutputStreamWriter (字節流→字符流)
代理控制訪問、延遲初始化、遠程調用-

總結

  1. FilterOutputStream 以組合替代繼承,構造器注入被裝飾對象,默認轉發,子類選擇性增強 —— 裝飾器模式標準落地。
  2. 裝飾器鏈順序即語義:Buffered → GZIP → File 先緩衡再壓縮寫磁盤;GZIP → Buffered → File 壓縮後緩衡(通常無意義)。
  3. 自定義裝飾器只需繼承 FilterOutputStream/FilterInputStream/FilterWriter/FilterReader,覆寫核心 write/read 方法,即可無縫融入 JDK IO 體系。
  4. 理解此結構,能從根本上看透 java.io、java.nio、第三方 IO 庫(Okio、Netty ByteBuf)的設計邏輯。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved