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

Java 內部類完全指南:成員內部類、局部內部類、匿名內部類與最佳實踐

     編輯:Java 綜合教學

內部類核心概念

在一個類內部定義另一個類,即為內部類。編譯後產生獨立 .class 檔案,命名規則:外部類$內部類.class(如 Outer$Inner.class)。

四大類型對比

類型定義位置訪問外部成員實例化方式典型場景
成員內部類類體中、方法外直接訪問所有 (含 private)new Outer().new Inner()邏輯強綁定、需頻繁訪問外部狀態
靜態嵌套類類體中、static 修飾僅訪問 static 成員new Outer.Nested()工具類、Builder 模式、不依賴外部實例
局部內部類方法/代碼塊內訪問 final/實際不可變區域變數方法內 new Local()複雜演算法封裝、一次性邏輯
匿名內部類表達式中、無類名同局部內部類new Interface() { ... }事件監聽、回調、策略模式臨時實現

成員內部類深度解析

public class Outer {
    private int outerField = 10;
    
    class Inner {
        private int innerField = 20;
        
        public void print() {
            System.out.println("內部類字段: " + innerField);        // 20
            System.out.println("外部類字段: " + outerField);        // 10
            System.out.println("外部類字段(顯式): " + Outer.this.outerField); // 10
        }
    }
    
    // 方式1:外部類方法創建內部實例
    public void createInner() {
        Inner inner = new Inner();
        inner.print();
    }
}

// 方式2:外部類直接實例化
public class Main {
    public static void main(String[] args) {
        Outer.Inner inner = new Outer().new Inner();
        inner.print();
    }
}

關鍵規則

  1. 同名遮蔽:內部類字段遮蔽外部同名字段,用 Outer.this.field 顯式訪問外部
  2. 私有封裝:內部類 private 時,僅外部類能實例化 (方式1)
  3. static 限制:內部類含 static 非 final 字段 → 必須宣告為 static class (即靜態嵌套類)

局部內部類與 final 語義

public class Outer {
    public void method() {
        final int localVar = 30; // Java 8+ 可省略 final,但必須「實際不可變」
        
        class Local {
            public void print() {
                System.out.println("捕獲局部變數: " + localVar); // 30
            }
        }
        
        new Local().print();
        // localVar = 40; // 編譯錯誤:被內部類捕獲後不可修改
    }
}

原理:編譯器將捕獲的局部變數值複製到內部類實例隱藏字段,延長生命週期。若允許修改,副本與原值不一致違反語義。

匿名內部類:語法糖與實戰模式

前提條件

  • 必須繼承類或實現介面 (多態賦值給父類型引用)
  • 無建構子,無法顯式傳參 (可捕獲外部 final 變數)

經典用法:事件監聽 / Runnable / Comparator

// 1. 基於抽象類
abstract class Animal {
    public abstract void run();
    public abstract void sleep();
}

public class Demo {
    public void test() {
        Animal dog = new Animal() { // 匿名子類
            @Override public void run() { System.out.println("狗在跑"); }
            @Override public void sleep() { System.out.println("狗在睡"); }
        };
        dog.run();
    }
    
    // 2. 基於介面 - 線程任務
    public void threadDemo() {
        Runnable task = new Runnable() {
            @Override public void run() { System.out.println("異步任務執行"); }
        };
        new Thread(task).start();
    }
    
    // 3. 策略模式臨時實現
    public void sortDemo() {
        List list = Arrays.asList("c", "a", "b");
        list.sort(new Comparator() {
            @Override public int compare(String o1, String o2) {
                return o2.compareTo(o1); // 降序
            }
        });
    }
}

this 與 Thread.currentThread() 區別

class Task implements Runnable {
    public void run() {
        System.out.println("this (Runnable實例): " + this);           // Task@hash
        System.out.println("Thread.currentThread(): " + Thread.currentThread()); // Thread[Thread-0,5,main]
    }
}

new Thread(new Task()).start();
  • this:指向實現類實例 (Runnable 物件)
  • Thread.currentThread():指向正在執行代碼的線程對象

同步鎖進階:靜態 vs 非靜態、單例雙重檢查

鎖對象區別

方法類型同步鎖對象同步範圍
非靜態 synchronized void m()this (當前實例)同一實例的所有非靜態同步方法互斥
靜態 synchronized static void m()ClassName.class (Class 對象)全類所有靜態同步方法互斥
同步塊 synchronized(obj)obj (任意對象)自定義鎖粒度

線程安全單例:雙重檢查鎖 (DCL) 最終版

public class Singleton {
    // volatile 禁止指令重排,保證可見性
    private static volatile Singleton instance;
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {                    // 第一次檢查:無鎖,高效
            synchronized (Singleton.class) {       // 類級鎖
                if (instance == null) {            // 第二次檢查:防重複創建
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

關鍵點:volatile 確保 instance = new Singleton() (分配內存→初始化→賦值引用) 不被重排序,避免其他線程看到半初始化對象。

wait/notify 機制與 Object 而非 Thread 定義的原因

  • 鎖屬於任意對象,一個程式可有多把鎖 (如 synchronized(lockA), synchronized(lockB))
  • wait()/notify() 必須由持有該鎖的線程在同步塊內呼叫
  • 喚醒時需同一把鎖對象呼叫 notify(),故定義在 Object 通用

線程停止與中斷模式

已棄用:stop()、suspend()、resume() (破壞鎖一致性、導致死鎖/數據損壞)

標準中斷模式

public class Worker implements Runnable {
    private volatile boolean running = true;
    
    public void run() {
        while (running && !Thread.currentThread().isInterrupted()) {
            try {
                // 業務邏輯
                TimeUnit.MILLISECONDS.sleep(100);
            } catch (InterruptedException e) {
                // 睡眠/等待被中斷 → 清除中斷標誌 → 手動重設或退出
                Thread.currentThread().interrupt(); // 重設標誌供上層檢查
                break;
            }
        }
    }
    
    public void shutdown() { running = false; }
}

// 使用
Worker worker = new Worker();
Thread t = new Thread(worker);
t.start();
// ... 稍後停止
worker.shutdown();      // 正常退出標誌
t.interrupt();          // 強制中斷阻塞操作
t.join();               // 等待結束

interrupt() vs interrupted() vs isInterrupted()

方法屬性行為中斷標誌
interrupt()實例設置中斷標誌;若線程阻塞於 sleep/wait/join → 拋出 InterruptedException 並清除標誌設置/清除後重設
isInterrupted()實例檢查標誌,不清除只讀
interrupted()static檢查當前線程標誌並清除讀取並清除

JDK 1.5+ 並發升級:Lock 與 Condition

// 替代 synchronized + wait/notify
Lock lock = new ReentrantLock();
Condition notFull = lock.newCondition();
Condition notEmpty = lock.newCondition();

// 生產者
lock.lock();
try {
    while (queue.size() == capacity) notFull.await(); // 精確喚醒
    queue.add(item);
    notEmpty.signal(); // 只喚醒消費者
} finally { lock.unlock(); }

// 消費者
lock.lock();
try {
    while (queue.isEmpty()) notEmpty.await();
    Item item = queue.poll();
    notFull.signal(); // 只喚醒生產者
    return item;
} finally { lock.unlock(); }

優勢:多條件變量精確喚醒 (避免 notifyAll 驚群)、可中斷鎖獲取 (lockInterruptibly)、公平鎖選項、嘗試非阻塞獲取 (tryLock)。

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