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

《編寫高質量代碼》第3章精讀:類與對象設計的 5 大核心建議

     編輯:Java 綜合教學

Java 類對象方法設計精要:建議 31-35 深度解析

《編寫高質量代碼:改善 Java 程序的 151 個建議》第 3 章聚焦類、對象與方法設計規範。本文精選建議 31-35,結合源碼分析與 JVM 原理,揭示看似簡單卻暗藏陷阱的編碼細節。

建議 31:接口中嚴禁出現實現代碼

雖然 Java 允許在接口中通過匿名內部類實現方法(如常量初始化),但這違背了接口「契約」本質。接口應只定義規範,不綁定具體實現,否則會導致實現類穩定性喪失,增加重構風險。

interface B {
    public static final S s = new S() { // 匿名內部類實現
        public void doSomeThing() { System.out.println("接口中實現"); }
    };
}

鐵律:接口僅聲明常量、抽象方法、繼承父接口,絕無具體實現。

建議 32:靜態變量必須先聲明後賦值

JVM 類初始化階段會先分配靜態字段內存空間(賦默認值),再按代碼順序執行顯式賦值與靜態塊。調換聲明與賦值順序會導致最終值違背直覺:

static { i = 100; }  // 先執行
public static int i = 1;  // 後執行,最終 i=1

根因:JVM 分兩階段處理——準備階段分配內存賦默認值,初始化階段按順序執行賦值語句。

建議 33:切勿覆寫靜態方法

子類定義與父類同名靜態方法屬於「隱藏」而非「覆寫」。靜態方法綁定於表面類型(聲明類型),非靜態方法綁定於實際類型(對象類型):

Base base = new Sub();
base.doAnything();  // 輸出:子類非靜態方法(動態綁定)
base.doSomething(); // 輸出:父類靜態方法(靜態綁定)

隱藏與覆寫本質區別:@Override 僅適用覆寫;隱藏旨在屏蔽父類行為,覆寫旨在延伸父類職責。

建議 34:構造函數應極致簡化

構造函數調用鏈中,父類構造器執行時子類字段尚未初始化。若父類構造器調用抽象方法(由子類實現),將讀取到子類字段默認值而非預期值:

abstract class Server {
    public Server() { int port = getPort(); } // 此時子類 port=0
    protected abstract int getPort();
}
class SimpleServer extends Server {
    private int port = 100;
    public SimpleServer(int _port) { port = _port; }
    protected int getPort() { return Math.random() > 0.5 ? port : DEFAULT_PORT; }
}

解決方案:將複雜邏輯遷移至單獨的 init/start 方法,構造器僅做字段初始化。

建議 35:構造函數避免初始化其他類

構造器中 new 其他類極易引發循環依賴導致 StackOverflowError。典型場景:框架父類構造器創建攔截器,攔截器構造器反向創建子類實例:

class Father { public Father() { new Other(); } }
class Other { public Other() { new Son(); } }
class Son extends Father { } // new Son() → Father() → Other() → Son() 死循環

最佳實踐:構造器僅初始化本類字段,外部依賴延遲至工廠方法或初始化器註入。

總結

這 5 條建議貫穿「契約精神」「初始化順序」「多態本質」「職責單一」「依賴解耦」五大設計哲學。優秀代碼不在於技巧炫耀,而在於對語言機制的敬畏與對維護成本的預判。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved