Java 內部類別

Java內部類別是定義在另一個類別的內部。Java內部類別可以被聲明為private、public、protected或預設存取,而外部類別只能有public或預設存取。Java嵌套類別分為兩種類型。

  1. 靜態嵌套類別

    如果嵌套類別是靜態的,則稱為靜態嵌套類別。靜態嵌套類別只能訪問外部類別的靜態成員。靜態嵌套類別與任何其他頂層類別相同,只是為了方便封裝而嵌套。可以使用以下語句創建靜態類別物件。

    OuterClass.StaticNestedClass nestedObject =
         new OuterClass.StaticNestedClass();
    
  2. java 內部類別

    在 Java 中,任何非靜態嵌套類別都被稱為內部類別。Java 內部類別與該類別的物件相關聯,它們可以訪問外部類別的所有變數和方法。由於內部類別與實例相關聯,因此無法在其中使用任何靜態變數。Java 內部類別的物件是外部類別物件的一部分,要創建內部類別的實例,我們首先需要創建外部類別的實例。Java 內部類別可以像這樣實例化;

    OuterClass outerObject = new OuterClass();
    OuterClass.InnerClass innerObject = outerObject.new InnerClass();
    

Java 內部類別有兩種特殊類型。

  1. 本地內部類別

    如果類別在方法內部被定義,則稱為本地內部類別。由於本地內部類別與物件無關,因此無法使用private、public或protected訪問修飾符。唯一允許的修飾符是abstract或final。本地內部類別可以訪問封閉類別和定義範圍內的本地final變數的所有成員。此外,它還可以訪問所在方法的非final本地變數,但無法修改它們。因此,如果嘗試在方法內部類別中更改非final本地變數的值,將獲得編譯時錯誤。本地內部類別的定義方式如下:

    package com.journaldev.innerclasses;
    
    public class MainClass {
    
    	private String s_main_class;
    
    	public void print() {
    		String s_print_method = "";
    		// 方法內的本地內部類別
    		class Logger {
    			// 能夠訪問封閉類別變數
    			String name = s_main_class; 
    			// 能夠訪問非final方法變數
    			String name1 = s_print_method; 
    
    			public void foo() {
    				String name1 = s_print_method;
    				// 以下代碼將引發編譯時錯誤:
    				// 在封閉範圍中定義的本地變數s_print_method必須是final或有效final的 
    				// s_print_method= ":";
    			}
    		}
    		// 在方法中實例化本地內部類別以使用
    		Logger logger = new Logger();
    
    	}
    }
    

    我們也可以在任何區塊內定義本地內部類別,例如靜態區塊、if-else區塊等。然而,在這種情況下,類別的範圍將非常有限。

    public class MainClass {
    
    	static {
    		class Foo {
    			
    		}
    		Foo f = new Foo();
    	}
    	
    	public void bar() {
    		if(1 < 2) {
    			class Test {
    				
    			}
    			Test t1 = new Test();
    		}
    		// 以下將引發錯誤,因為類別的範圍有限
    		//Test t = new Test();
    		//Foo f = new Foo();
    	}
    }
    
  2. 匿名內部類別

    沒有名稱的區域內部類別被稱為匿名內部類別。匿名類別在定義和實例化時是一個單一的陳述句。匿名內部類別必定是繼承一個類別或實作一個介面。由於匿名內部類別沒有名稱,所以無法為其定義建構子。匿名內部類別只能在定義的地方存取。如何建立匿名內部類別的方式有點難以定義,我們將在下面的測試程式中看到它的實際用途。

以下是一個 Java 類別示範如何定義 Java 內部類別、靜態巢狀類別、區域內部類別和匿名內部類別的程式碼:OuterClass.java

package com.journaldev.nested;

import java.io.File;
import java.io.FilenameFilter;

public class OuterClass {
    
    private static String name = "OuterClass";
    private int i;
    protected int j;
    int k;
    public int l;

    //OuterClass 建構子
    public OuterClass(int i, int j, int k, int l) {
        this.i = i;
        this.j = j;
        this.k = k;
        this.l = l;
    }

    public int getI() {
        return this.i;
    }

    //靜態巢狀類別,可以存取 OuterClass 的靜態變數/方法
    static class StaticNestedClass {
        private int a;
        protected int b;
        int c;
        public int d;

        public int getA() {
            return this.a;
        }

        public String getName() {
            return name;
        }
    }

    //內部類別,非靜態且可以存取外部類別的所有變數/方法
    class InnerClass {
        private int w;
        protected int x;
        int y;
        public int z;

        public int getW() {
            return this.w;
        }

        public void setValues() {
            this.w = i;
            this.x = j;
            this.y = k;
            this.z = l;
        }

        @Override
        public String toString() {
            return "w=" + w + ":x=" + x + ":y=" + y + ":z=" + z;
        }

        public String getName() {
            return name;
        }
    }

    //區域內部類別
    public void print(String initial) {
        //區域內部類別 inside the method
        class Logger {
            String name;

            public Logger(String name) {
                this.name = name;
            }

            public void log(String str) {
                System.out.println(this.name + ": " + str);
            }
        }

        Logger logger = new Logger(initial);
        logger.log(name);
        logger.log("" + this.i);
        logger.log("" + this.j);
        logger.log("" + this.k);
        logger.log("" + this.l);
    }

    //匿名內部類別
    public String[] getFilesInDir(String dir, final String ext) {
        File file = new File(dir);
        //匿名內部類別 implementing FilenameFilter interface
        String[] filesList = file.list(new FilenameFilter() {

            @Override
            public boolean accept(File dir, String name) {
                return name.endsWith(ext);
            }

        });
        return filesList;
    }
}

這裡是展示如何在 Java 中實例化和使用內部類別的測試程式。 InnerClassTest.java

package com.journaldev.nested;

import java.util.Arrays;
//巢狀類別可在 import 中使用,以方便實例化
import com.journaldev.nested.OuterClass.InnerClass;
import com.journaldev.nested.OuterClass.StaticNestedClass;

public class InnerClassTest {

    public static void main(String[] args) {
        OuterClass outer = new OuterClass(1,2,3,4);
        
        //靜態巢狀類別範例
        StaticNestedClass staticNestedClass = new StaticNestedClass();
        StaticNestedClass staticNestedClass1 = new StaticNestedClass();
        
        System.out.println(staticNestedClass.getName());
        staticNestedClass.d=10;
        System.out.println(staticNestedClass.d);
        System.out.println(staticNestedClass1.d);
        
        //內部類別範例
        InnerClass innerClass = outer.new InnerClass();
        System.out.println(innerClass.getName());
        System.out.println(innerClass);
        innerClass.setValues();
        System.out.println(innerClass);
        
        //使用區域內部類別呼叫方法
        outer.print("Outer");
        
        //使用匿名內部類別呼叫方法
        System.out.println(Arrays.toString(outer.getFilesInDir("src/com/journaldev/nested", ".java")));
        
        System.out.println(Arrays.toString(outer.getFilesInDir("bin/com/journaldev/nested", ".class")));
    }

}

這裡是上述 Java 內部類別範例程式的輸出。

OuterClass
10
0
OuterClass
w=0:x=0:y=0:z=0
w=1:x=2:y=3:z=4
Outer: OuterClass
Outer: 1
Outer: 2
Outer: 3
Outer: 4
[NestedClassTest.java, OuterClass.java]
[NestedClassTest.class, OuterClass$1.class, OuterClass$1Logger.class, OuterClass$InnerClass.class, OuterClass$StaticNestedClass.class, OuterClass.class]

請注意,當 OuterClass 編譯時,內部類別、區域內部類別和靜態巢狀類別會分別建立獨立的類別檔案。

Java 內部類別的好處

  1. 如果一個類別僅對一個類別有用,將其嵌套在一起是有意義的。這有助於封裝類別。
  2. Java 內部類別實現了封裝。請注意,內部類別可以訪問外部類別的私有成員,同時我們可以隱藏內部類別不讓外界訪問。
  3. 將小類別放在頂層類別內部可以使代碼更接近使用的地方,並使代碼更易讀和維護。

這就是關於 Java 內部類別的全部內容。

Source:
https://www.digitalocean.com/community/tutorials/java-inner-class