? ? 內(nèi)部類并不常用,而且使用起來有一定的定式,比如在下面的InnterDemoByTrhead.java里,我們通過內(nèi)部類的形式創(chuàng)建線程。? ??
1 public class InnerDemoByThread { 2 public static void main(String[] args) {
3 // 實現(xiàn)runnable接口,創(chuàng)建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行對應(yīng),注意需要帶分號 12 } 13 }
? ??在上述的第4行里,我們通過for循環(huán)創(chuàng)建了10個線程,在第5行里,我們通過new
Runnable定義了線程內(nèi)部的動作,具體而言,在第6到第10行的代碼里,定義了打印0到4的動作。這里第5行通過new
Thread定義的類,是在第1行定義的InnerDemoByThread類的內(nèi)部,所以叫內(nèi)部類,這也是內(nèi)部類典型的用法。
? ?
雖然內(nèi)部類出現(xiàn)的機會不多,但其中有個非常重要的知識點:當(dāng)方法的參數(shù)需要被內(nèi)部類使用時,那么這個參數(shù)必須是final,否則會報語法錯誤。我們在講線程的時候,通過內(nèi)部類比較了線程安全和不安全集合的表現(xiàn)。這里我們通過改寫這個案例,著重看下“內(nèi)部類“和“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 //
創(chuàng)建一個線程組 6 ThreadGroup group = new ThreadGroup("Group"); 7 // 通過內(nèi)部類的方法來創(chuàng)建多線程 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函數(shù)的第25行里,我們創(chuàng)建了一個線程不安全的ArrayList類型的對象,并在第27行調(diào)用了addByThreads方法返回list的長度。在addByThreads方法里,我們在第14行里,通過for循環(huán)啟動了10個線程,在這10個線程的主體邏輯(第9行的run方法)里,我們在第10行通過list.add方法給集合對象添加元素。
? ? 從功能上講,第27行的打印語句能輸出10,因為雖然ArrayList是線程不安全對象,但僅僅是10個線程同時操作,不足以發(fā)生“線程搶占”的情況。
? ?
但本代碼的重點是內(nèi)部類和final,在代碼第3行定義的addByThreads方法里,我們注意到參數(shù)list前一定得加final,否則會報語法錯誤。我們可以通過如下的思維步驟來理解這個要點。
? ?
第一,第3行的這個帶final的list對象從屬于外部的InnerFinalDemo類,并且,在第8到12行的內(nèi)部類里,也會用到這個對象,也就是說,在外部類和內(nèi)部類里,都會用到這個對象。
? ? 第二,外部類和內(nèi)部類是平行的,內(nèi)部類并不從屬于外部類,這句話隱藏的含義是,外部類有可能在內(nèi)部類之前被回收。
? ?
那么如果我們不加final,一旦外部類在內(nèi)部類之前被回收,那么外部類里所包含的list對象也會被回收,但這時,內(nèi)部類尚未使用這個list。在這種情況下,一旦內(nèi)部類使用了list,就會報空指針錯(因為這個對象已經(jīng)隨著外部類被回收了)。
? ?
為了避免這種錯誤,在指定語法時就加上了“當(dāng)方法的參數(shù)需要被內(nèi)部類使用時,那么這個參數(shù)必須是final”這個規(guī)定。一旦在此類參數(shù)前加final,那么這個參數(shù)就是常量了,存儲的位置就不是“堆區(qū)”了,而是“常量池”,這樣即使外部類被先回收,那么由于這類參數(shù)(比如list)不存在于外部類所從屬的堆空間(而是常量池),所以會繼續(xù)存在,這樣內(nèi)部類就能繼續(xù)使用。
? ?
一些資深的面試官不會面試內(nèi)部類的細(xì)節(jié)語法(因為不常用,而且使用起來有定式),而會考察上述的“參數(shù)和final”的知識點,所以大家在被問及”對內(nèi)部類的掌握程度“這類問題時,可以按如下的思路來敘述。
? ?
第一,無需敘述內(nèi)部類中各種語法,事實上,內(nèi)部類涉及到“如何定義”以及“內(nèi)部類中對象的可見性”等問題,語法相對而言比較復(fù)雜,說起來不容易,而且即使說清楚了,也無法很好體現(xiàn)大家的能力。
? ? 第二,可以直接說,“當(dāng)方法的參數(shù)需要被內(nèi)部類使用時,那么這個參數(shù)必須是final
”,同時解釋下原因。當(dāng)面試官聽到這以后,一般就不再問內(nèi)部類問題了,因為他會認(rèn)為,候選人連這么“資深”的知識也知道,那么就沒必要再細(xì)問內(nèi)部類的問題了。
? ? 第三,由于已經(jīng)引出“垃圾回收”的話題,所以大家可以找機會進(jìn)一步按本章給出的提示,展示在這方面的能力,這樣就有很大可能得到“Java
Core方面比較資深”的評價。
?
?上述敘述是針對jdk1.7以及之前版本的,如果是針對jdk1.8版本,不需要顯式地加final,但依然會被當(dāng)常量管理,具體來講,該對象的引用無法指向新的內(nèi)存空間。
?
?
?
熱門工具 換一換