內部類並不經常使用,並且使用起來有必定的定式,好比在下面的InnterDemoByTrhead.java裏,咱們經過內部類的形式建立線程。 java
1 public class InnerDemoByThread { 2 public static void main(String[] args) { 3 // 實現runnable接口,建立10個線程並啓動 4 for(int threadCnt = 0;threadCnt<10;threadCnt++) 5 new Thread(new Runnable() { 6 public void run() { 7 for (int i = 0; i < 5; i++) { 8 //在每一個線程裏,輸出0到4 System.out.println(Thread.currentThread().getName()+":"+ i); 9 } 10 } 11 }).start();//這裏的括號是和第5行對應,注意須要帶分號 12 } 13 }
在上述的第4行裏,咱們經過for循環建立了10個線程,在第5行裏,咱們經過new Runnable定義了線程內部的動做,具體而言,在第6到第10行的代碼裏,定義了打印0到4的動做。這裏第5行經過new Thread定義的類,是在第1行定義的InnerDemoByThread類的內部,因此叫內部類,這也是內部類典型的用法。面試
雖然內部類出現的機會很少,但其中有個很是重要的知識點:當方法的參數須要被內部類使用時,那麼這個參數必須是final,不然會報語法錯誤。咱們在講線程的時候,經過內部類比較了線程安全和不安全集合的表現。這裏咱們經過改寫這個案例,着重看下「內部類「和「final「的要點,請你們看下以下的InnerFinalDemo.java代碼。 安全
1 import java.util.ArrayList; 2 import java.util.List; 3 public class InnerFinalDemo { 4 public static int addByThreads(final List list) { 5 // 建立一個線程組 6 ThreadGroup group = new ThreadGroup("Group"); 7 // 經過內部類的方法來建立多線程 8 Runnable listAddTool = new Runnable() { 9 public void run() {// 在其中定義線程的主體代碼 10 list.add("0"); // 在集合裏添加元素 11 } 12 }; 13 // 啓動10個線程,同時向集合裏添加元素 14 for (int i = 0; i < 10; i++) { 15 new Thread(group, listAddTool).start(); 16 } 17 while (group.activeCount() > 0) { 18 try { Thread.sleep(10); } 19 catch (InterruptedException e) 20 { e.printStackTrace(); } 21 } 22 return list.size(); // 返回插入後的集合長度 23 } 24 public static void main(String[] args) { 25 List list = new ArrayList(); 26 //很大可能返回10 27 System.out.println(addByThreads(list)); 28 } 29 }
這段代碼的邏輯是,在main函數的第25行裏,咱們建立了一個線程不安全的ArrayList類型的對象,並在第27行調用了addByThreads方法返回list的長度。在addByThreads方法裏,咱們在第14行裏,經過for循環啓動了10個線程,在這10個線程的主體邏輯(第9行的run方法)裏,咱們在第10行經過list.add方法給集合對象添加元素。多線程
從功能上講,第27行的打印語句能輸出10,由於雖然ArrayList是線程不安全對象,但僅僅是10個線程同時操做,不足以發生「線程搶佔」的狀況。函數
但本代碼的重點是內部類和final,在代碼第3行定義的addByThreads方法裏,咱們注意到參數list前必定得加final,不然會報語法錯誤。咱們能夠經過以下的思惟步驟來理解這個要點。線程
第一,第3行的這個帶final的list對象從屬於外部的InnerFinalDemo類,而且,在第8到12行的內部類裏,也會用到這個對象,也就是說,在外部類和內部類裏,都會用到這個對象。指針
第二,外部類和內部類是平行的,內部類並不從屬於外部類,這句話隱藏的含義是,外部類有可能在內部類以前被回收。對象
那麼若是咱們不加final,一旦外部類在內部類以前被回收,那麼外部類裏所包含的list對象也會被回收,但這時,內部類還沒有使用這個list。在這種狀況下,一旦內部類使用了list,就會報空指針錯(由於這個對象已經隨着外部類被回收了)。blog
爲了不這種錯誤,在指定語法時就加上了「當方法的參數須要被內部類使用時,那麼這個參數必須是final」這個規定。一旦在此類參數前加final,那麼這個參數就是常量了,存儲的位置就不是「堆區」了,而是「常量池」,這樣即便外部類被先回收,那麼因爲這類參數(好比list)不存在於外部類所從屬的堆空間(而是常量池),因此會繼續存在,這樣內部類就能繼續使用。接口
一些資深的面試官不會面試內部類的細節語法(由於不經常使用,並且使用起來有定式),而會考察上述的「參數和final」的知識點,因此你們在被問及」對內部類的掌握程度「這類問題時,能夠按以下的思路來敘述。
第一,無需敘述內部類中各類語法,事實上,內部類涉及到「如何定義」以及「內部類中對象的可見性」等問題,語法相對而言比較複雜,提及來不容易,並且即便說清楚了,也沒法很好體現你們的能力。
第二,能夠直接說,「當方法的參數須要被內部類使用時,那麼這個參數必須是final」,同時解釋下緣由。當面試官聽到這之後,通常就再也不問內部類問題了,由於他會認爲,候選人連這麼「資深」的知識也知道,那麼就不必再細問內部類的問題了。
第三,因爲已經引出「垃圾回收」的話題,因此你們能夠找機會進一步按本章給出的提示,展現在這方面的能力,這樣就有很大可能獲得「Java Core方面比較資深」的評價。
上述敘述是針對jdk1.7以及以前版本的,若是是針對jdk1.8版本,不須要顯式地加final,但依然會被當常量管理,具體來說,該對象的引用沒法指向新的內存空間。