內部類、final與垃圾回收,面試時你一說,面試官就知道

    內部類並不經常使用,並且使用起來有必定的定式,好比在下面的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,但依然會被當常量管理,具體來說,該對象的引用沒法指向新的內存空間。

相關文章
相關標籤/搜索