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

Java 反射實作物件美化列印:ByteArrayOutputStream/InputStream 緩存優化技巧

     編輯:Java 相關知識

反射實作通用物件 ToString 工具類

利用 Java 反射機制,遞迴遍歷物件屬性,支援基本類型、集合、Map、嵌套物件、日期等,格式化輸出至控制台或日誌。

核心類:MyTestUtil

package utils;

import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.*;

public class MyTestUtil {
    private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
    
    // 公開靜態方法:預設開啟遞迴
    public static String objToStr(Object obj) {
        return objToStr(obj, true);
    }
    
    // 核心遞迴方法
    private static String objToStr(Object object, boolean recursion) {
        if (object == null) return "null";
        if (object instanceof Date) {
            return new SimpleDateFormat(DATE_FORMAT).format((Date) object);
        }
        
        Class clazz = object.getClass();
        
        // 基本類型與 String 直接輸出
        if (isSimpleType(clazz)) return String.valueOf(object);
        
        // 陣列/集合
        if (clazz.isArray() || object instanceof Collection) {
            return collectionToStr(object, recursion);
        }
        
        // Map
        if (object instanceof Map) {
            return mapToStr((Map) object, recursion);
        }
        
        // 普通物件 (Bean)
        return beanToStr(object, recursion);
    }
    
    // 基本類型判斷
    private static boolean isSimpleType(Class clazz) {
        return clazz.isPrimitive() || 
               clazz == String.class ||
               Number.class.isAssignableFrom(clazz) ||
               clazz == Boolean.class || clazz == Character.class;
    }
    
    // 集合/陣列處理
    private static String collectionToStr(Object object, boolean recursion) {
        Object[] arr = clazz.isArray() ? (Object[]) object : ((Collection) object).toArray();
        if (!recursion || isSimpleArray(arr)) return Arrays.toString(arr);
        return complexArrayToStr(arr);
    }
    
    private static String complexArrayToStr(Object[] arr) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < arr.length; i++) {
            sb.append("[").append(i).append("] -> ")
              .append(objToStr(arr[i], false)).append(",\n");
        }
        return sb.toString();
    }
    
    // Map 處理
    private static String mapToStr(Map map, boolean recursion) {
        if (!recursion || isSimpleMap(map)) return simpleMapToStr(map);
        return complexMapToStr(map);
    }
    
    // Bean 物件處理:反射獲取所有宣告欄位
    private static String beanToStr(Object object, boolean recursion) {
        StringBuilder sb = new StringBuilder();
        sb.append(object.getClass().getSimpleName()).append("[\n");
        
        Field[] fields = object.getClass().getDeclaredFields();
        // 簡單類型優先顯示 (可選排序)
        fields = sortFieldsByType(fields);
        
        for (int i = 0; i < fields.length; i++) {
            Field f = fields[i];
            f.setAccessible(true); // 破壞封裝,訪問 private
            if (f.getName().equals("serialVersionUID")) continue;
            
            try {
                Object val = f.get(object);
                sb.append("  ").append(f.getName()).append(" = ");
                if (isSimpleType(val) || !recursion) {
                    sb.append(val);
                } else {
                    sb.append("\n    ").append(objToStr(val, false));
                }
            } catch (IllegalAccessException e) {
                sb.append("");
            }
            if (i < fields.length - 1) sb.append(",\n");
        }
        sb.append("\n]");
        return sb.toString();
    }
    
    // 控制台美化列印
    public static void print(Object obj) {
        print(obj, "DEBUG", objToStr(obj));
    }
    public static void printWithSign(String sign, Object obj) {
        print(obj, sign, objToStr(obj));
    }
    private static void print(Object obj, String sign, String content) {
        String line = "=".repeat(50);
        System.out.println(line + " " + obj.getClass().getSimpleName() + " >> " + sign + " " + line);
        System.out.println(content);
        System.out.println(line.repeat(2));
    }
}

ByteArrayOutputStream / ByteArrayInputStream 緩存優化實戰

場景:避免重複讀取配置檔 (Mycat 專案優化案例)

問題代碼:頻繁開啟/關閉 InputStream

private static Element loadRoot() {
    InputStream dtd = ConfigFactory.class.getResourceAsStream("/mycat.dtd");
    InputStream xml = ConfigFactory.class.getResourceAsStream("/mycat.xml");
    return ConfigUtil.getDocument(dtd, xml).getDocumentElement();
}
// 多個業務方法反覆呼叫 loadRoot(),每次都重新讀取檔案

優化方案一:ByteArrayOutputStream 緩存位元組陣列

// 靜態初始化塊:啟動時讀一次,緩存到 byte[]
private static byte[] xmlBuffer, dtdBuffer;
private static ByteArrayOutputStream xmlBaos, dtdBaos;

static {
    bufferFile(ConfigFactory.class.getResourceAsStream("/mycat.dtd"), dtdBuffer, dtdBaos);
    bufferFile(ConfigFactory.class.getResourceAsStream("/mycat.xml"), xmlBuffer, xmlBaos);
}

private static void bufferFile(InputStream in, byte[] buf, ByteArrayOutputStream baos) {
    try {
        int len;
        while ((len = in.read(buf)) > -1) baos.write(buf, 0, len);
        baos.flush();
    } catch (IOException e) { /* log error */ }
}

// 後續使用:從 byte[] 重建 InputStream,無需重讀磁碟
private static Element loadRoot() {
    InputStream xml = new ByteArrayInputStream(xmlBaos.toByteArray());
    InputStream dtd = new ByteArrayInputStream(dtdBaos.toByteArray());
    return ConfigUtil.getDocument(dtd, xml).getDocumentElement();
}

原理:ByteArrayOutputStream 將輸入流寫入記憶體緩衝區 (byte[]);ByteArrayInputStream 從 byte[] 建立新輸入流。同一 byte[] 可無限次建立獨立 InputStream。

優化方案二:直接緩存 Document 物件 (更佳)

private static Document document; // 緩存解析後的 DOM 樹

private static Element loadRoot() {
    if (document == null) {
        InputStream dtd = getResource("/mycat.dtd");
        InputStream xml = getResource("/mycat.xml");
        document = ConfigUtil.getDocument(dtd, xml); // 只解析一次
    }
    return document.getDocumentElement();
}

優勢:避免重複建立 InputStream、重複解析 XML,效能最佳,語義最清晰。

ByteArrayOutputStream/InputStream 核心 API

類別建構子關鍵方法
ByteArrayOutputStream () (32 bytes)、(int size) write(byte[], offset, len)、toByteArray()、toString()、reset()
ByteArrayInputStream (byte[] buf)、(byte[] buf, offset, length) 繼承 InputStream:read()、available()、reset() (回到起始)

實用場景總結

  1. 多次讀取同一流:網路回應同時顯示與存檔
  2. 單元測試模擬輸入:new ByteArrayInputStream("test data".getBytes())
  3. 記憶體中組裝資料再輸出:序列化、壓縮、加密管線
  4. 配置/資源檔緩存:啟動讀一次,運行期多次用

現代替代方案

  • Jackson/Gson:objectMapper.writeValueAsString(obj) 產出 JSON,結構化、可攜帶、前端友善
  • toString() 重寫 / Lombok @ToString:簡單物件首選
  • Log 框架:Log4j2/Logback 配合 %msg、%ex、JSON Layout
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved