以前看<Java程式設計思想>的時候,看到過巢狀類跟內部類的區別,不過後來就把它們的概念給忘了吧。昨天在看<資料結構與演算法分析(Java語言版)>的時候,又遇到了這個概念,當時就很大的疑惑:巢狀類跟內部類有什麼區別?只有是否有關鍵字static的區別嗎?
所以今天找了個時間查了一下兩者的詳細區別,總結在這篇部落格中,既方便自己的複習和學習,也啟示他人吧。
1,概念:
定義在一個類內部的類,叫作“巢狀類”。巢狀類分為兩種:static的和非static的。後者又有一個專門的名字,叫作“內部類”。所以從概念可以看出,巢狀類跟內部類是所屬關係,後者包含於前者。示例程式碼如下:
class OuterClass {
...
static class StaticNestedClass {
...
}
class InnerClass {
...
}
}
同時,巢狀類是其所在類的成員。內部類可以訪問所在類的所有成員,即使該成員是private的。而static巢狀類則不得訪問所在類的成員。同時,巢狀類,static和非static的,都可以被宣告為private、public、protected和default的。
2,為什麼要使用巢狀類?
好處應該都比較文字化吧,以後在使用的過程中去理解和體會吧:對只在一個地方使用的類進行邏輯上的分組;增加了封裝性;易於閱讀和維護。
3,static巢狀類:
因為static巢狀類不能直接訪問所在類的非static成員變數和方法,所以static巢狀類必須通過繫結所在類的例項來進行訪問。而對於所在類的靜態成員和方法包括private、protected和public的,可以訪問。因為它也有static修飾。
static巢狀類通過寫出封裝的類名來進行例項化和訪問其內部成員:
OuterClass.StaticNestedClass nestedObject =
new OuterClass.StaticNestedClass();
4,內部類:
因為內部類是所在類的成員,所以它可以訪問所在類的任意變數和方法,但是它本身卻不能定義任何static的變數或方法。
同時,內部類的例項化方式也與static巢狀類有所不同:
OuterClass outerObject=new OuterClass();
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
static巢狀類與non-static巢狀類,在形式上只有是否含有static關鍵字的區別,但是JVM在初始化時兩者還是有差別的:差別就是後者在例項化時會自動地與外圍例項建立一種聯絡,且這種聯絡不得修改。JVM在例項化non-static巢狀類時會生成一個指向外圍例項的物件引用(this),儲存這種引用將會消耗時間和空間,同時,在外圍例項滿足垃圾回收的條件時仍然得以留存。
5,內部類的分類:
以前曾經接觸過內部類的分類,這裡一併總結一下:
以前的所謂的一些面試寶典裡面差不多都是將內部類分為四個種類:
靜態內部類(既static巢狀類)、成員內部類(既上述內部類)、區域性內部類和匿名內部類。前兩者都已經介紹過了,下面專門看一下後面兩者。
5.1,區域性內部類:
定義在方法內部的類叫作“區域性內部類”。它的作用域僅限於方法作用域內,只能在方法的作用域內定義和例項化,是用處最小的類型別。和區域性變數一樣,它不能被修飾為private, public, protected和static的,並且只能訪問方法內部定義的final變數。
class LocalInner
{
int a = 1; public void doSomething()
{
int b = 2;
final int c = 3;
// 定義一個區域性內部類
class Inner3
{
public void test()
{
System.out.println("Hello World");
System.out.println(a); // 不可以訪問非final的區域性變數
// error: Cannot refer to a non-final variable b inside an inner
// class defined in a different method
// System.out.println(b); // 可以訪問final變數
System.out.println(c);
}
} // 建立區域性內部類的例項並呼叫方法
new Inner3().test();
}
} public class LocalInnerClassTest
{
public static void main(String[] args)
{
// 建立外部類物件
LocalInner inner = new LocalInner();
// 呼叫外部類的方法
inner.doSomething();
} }
5.2,匿名內部類:
顧名思義,匿名內部類就是沒有名字的區域性類。它不使用關鍵字class, extends, implements以及建構函式。
它通常作為方法的一個引數傳入,比如在android開發中對一個Button新增一個OnClickListener監聽器。
匿名內部類隱匿的繼承了一個父類或者實現了一個介面。比如:
mUiHandler.post(new Runnable{
@override
public void run(){
//
} }); AsyncClient.get(url, new JsonHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers,
JSONObject response) {
// TODO Auto-generated method stub
super.onSuccess(statusCode, headers, response);}} );
內部類通過將相關的類組織在一直,從而降低了名稱空間的複雜性。
6,內部類的序列化問題。
對任何種類內部類(包括區域性內部類和匿名內部類)的序列化都是不被鼓勵的。因為java編譯器在對內部類進行編譯的時候,將進行“合成構造”。合成構造使得java編譯器實現了java的新特性,但是卻沒有對JVM做出相應的改變。然而,不同的java編譯器對合成構造是有差別的,因而,如果對內部類進行了序列化,將使得不同的JRE實現中存在相容性問題。
本文是在參考了大量他人的勞動成果之上的而寫成的,主要的參考文獻有:
http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html;
http://www.cnblogs.com/mengdd/archive/2013/02/08/2909307.html。