昨天去筆試的時候遇到了Java的內部類的創建方式與訪問權限的問題,我不懂,沒寫,故今天起來特意去試驗一下,就有了這篇總結性的文章。
Java中的內部類又分為非靜態內部類(匿名內部類也是非靜態的內部類)和靜態內部類,兩者與外部類的關系是不一樣的,創建的方式也不一樣。
1 非靜態內部類
Java的非靜態內部類在構造的時候,會將外部類的引用傳遞進來,並且作為內部類的一個屬性,因此,內部類會隱式地持有其外部類的引用。也就是非靜態內部類在構造的時候需要有一個外部類的實例傳遞進來進行構造,不能像普通的Java類那樣直接就可以通過 new來生成;一個簡單的例子如下:
1 import java.util.ArrayList; 2 import java.util.LinkedList; 3 import java.util.List; 4 import java.util.Queue; 5 import java.util.Scanner; 6 7 public class Main { 8 9 /** 10 * @param args 11 */ 12 public int k=3; 13 private static String string="Java"; 14 protected float j=1.5f; 15 public static void show(){ 16 System.out.println("show"); 17 } 18 private void add(){ 19 System.out.println("add"); 20 } 21 public static void main(String[] args) { 22 // TODO Auto-generated method stub 23 Main m=new Main(); 24 //合法的非靜態內部類的構造方式 25 Child c=m.new Child(); 26 //Child c=new Child() 這是不合法的構造方式 27 c.test(); 28 29 } 30 //內部類Child 31 class Child{ 32 public int i; 33 public void test(){ 34 System.out.println("k=:"+k); 35 System.out.println("string:"+string); 36 add(); 37 System.out.println("j=:"+j); 38 show(); 39 } 40 41 } 42 43 }
並且非靜態的內部類可以訪問外部類的所有成員變量與方法,包括靜態的成員變量與方法,執行內部類Child的test()方法就可以得到下面的結果:
1 k=:3 2 string:Java 3 add 4 j=:1.5 5 show
2 靜態內部類
Java中的靜態內部類在構造的時候並不需要外部類的引用,因此靜態的內部類不會持有外部類的引用,並且靜態內部類只能訪問外部類的靜態成員變量和方法。一個簡單的例子為(上面的代碼做一些簡單的改動):
1 public class Main { 2 3 /** 4 * @param args 5 */ 6 public int k=3; 7 private static String string="Java"; 8 protected float j=1.5f; 9 public static void show(){ 10 System.out.println("show"); 11 } 12 private void add(){ 13 System.out.println("add"); 14 } 15 public static void main(String[] args) { 16 // TODO Auto-generated method stub 17 Main m=new Main(); 18 //Child c=m.new Childe();//非法 19 Child c=new Child(); 20 c.test(); 21 22 } 23 static class Child{ 24 public int i; 25 26 public void test(){ 27 // System.out.println("k=:"+k);//不能訪問外部類非靜態變量 28 System.out.println("string:"+string); 29 //add(); //不能訪問外部類的非靜態方法 30 // System.out.println("j=:"+j); 31 show(); 32 } 33 34 } 35 36 }
從上面的代碼可以看到,靜態內部類的創建方式與普通的Java類的創建方式一樣,執行第21行代碼就可以得到如下結果:
1 string:Java 2 show
3 雜談
Java的非靜態內部類的這種創建方式,會隱式地持有外部類的引用,而且默認情況下這個引用是強引用,因此,如果內部類的生命周期長於外部類的生命周期,程序很容易就產生內存泄漏(你認為垃圾回收器會回收掉外部類的實例,但由於內部類持有外部類的引用,導致垃圾回收器不能正常工作)。為了避免這種情況的發生,你可以在內部類的內部顯示持有一個外部類的軟引用(或弱引用),並通過構造方法的方式傳遞進來,在內部類的使用過程中,先判斷一下外部類是否被回收;
關於內存泄漏的這一點,有參考技術小黑屋的兩篇文章:Android中Handler引起的內存泄露和避免Android中Context引起的內存泄露
