在實際的項目中看到一個很奇怪的現(xiàn)象烤送,Java可以直接new一個接口寒随,然后在new里面粗暴的加入實現(xiàn)代碼。就像下面這樣帮坚。那么問題來了妻往,new出來的對象沒有實際的類作為載體,這不是很奇怪嗎试和?
思考以下代碼的輸出是什么讯泣?
Runnable x = new Runnable() {
@Override
public void run() {
System.out.println(this.getClass());
}
};
x.run();
實際答案是出現(xiàn)xxxx$1這樣一個類名,它是編譯器給定的名稱阅悍。
匿名類
匿名類相當(dāng)于在定義類的同時再新建這個類的實例好渠。我們來看看匿名類的編譯結(jié)果。
這個類的代碼如下:
public class Test {
public void test() {
Runnable r = new Runnable(){
@Override
public void run(){
System.out.println("hello");
}
};
}
}
來看看它的編譯結(jié)果节视,通過javap反向編譯Test.class拳锚,得到的結(jié)果如下:
SourceFile: "Test.java"
EnclosingMethod: #20.#21 // Test.test
InnerClasses:
#6; //class Test$1
發(fā)現(xiàn)了一個字段叫EnclosingMethod,說明這個類是定義在Test.test方法下的寻行。那現(xiàn)在有個問題霍掺,如果有兩個test方法,會出現(xiàn)什么呢拌蜘?
原來是旁邊這個注釋不太準(zhǔn)確杆烁,實際上會包含函數(shù)簽名的。請看Constant Pool部分简卧,這里的#21指向了這個函數(shù)簽名兔魂。
#21 = NameAndType #34:#16 // test:()V
匿名類的語法
這里舉一個簡單的例子:
Runnable hello = new Runnable() {
public void run() {
System.out.println("hello");
}
};
一個匿名類由以下幾個部分組成:
- new操作符
- Runnable:接口名稱。這里還可以填寫抽象類举娩、普通類的名稱析校。
- ():這個括號表示構(gòu)造函數(shù)的參數(shù)列表。由于Runnable是一個接口晓铆,沒有構(gòu)造函數(shù)勺良,所以這里填一個空的括號表示沒有參數(shù)。
- {...}:大括號中間的代碼表示這個類內(nèi)部的一些結(jié)構(gòu)骄噪。在這里可以定義變量名稱尚困、方法。跟普通的類一樣链蕊。
訪問權(quán)限
那么匿名內(nèi)部類能訪問哪些東西呢事甜?按照規(guī)則谬泌,可以訪問如下內(nèi)容:
- 訪問外層Class里面的字段。
- 不能訪問外層方法中的本地變量逻谦。除非變量是final掌实。
- 如果內(nèi)部類的名稱和外面能訪問的名稱相同,則會把名稱覆蓋掉邦马。
public class A {
private int foo;
public void test() {
Runnable r = new Runnable() {
System.out.println(foo);
};
}
}
匿名類里面不可以有的東西:
1.不能定義靜態(tài)初始化代碼塊(Static Initializer)贱鼻。比如下面的代碼是不符合語法的:
public class A {
public void test() {
Runnable r = new Runnable() {
static { System.out.println("hello"); }
};
}
}
2.不能在匿名類里面定義接口。
比如:
public class A {
public void test() {
Runnable r = new Runnable() {
public interface Hello { };
};
}
}
和上面一樣滋将,也是為了語義的清晰邻悬。interface只能定義靜態(tài)的。
3.不能在匿名類中定義構(gòu)造函數(shù)随闽。
public class A {
public void test() {
Runnable r = new Runnable() {
public Runnable() { }
};
}
}
因為匿名類沒有名字父丰,而構(gòu)造函數(shù)需要把類名作為方法名才能看成構(gòu)造函數(shù)。
匿名類中可以包含的東西有:
- 字段
- 方法
- 實例初始化代碼
- 本地類
為什么不能定義靜態(tài)初始化代碼
事實上掘宪,內(nèi)部類中不能定義任何靜態(tài)的東西蛾扇。
關(guān)鍵字:Inner class cannot have static declarations
參考資料:http://stackoverflow.com/questions/975134/why-cant-we-have-static-method-in-a-non-static-inner-class
StackOverFlow上看起來有一種解釋如下。
首先來看一個內(nèi)部類魏滚。
public class A {
public class B {
}
}
它編譯之后镀首,會變成下面這種含義:
public class A {
public static class B {
private final A parent;
public B(A parent) {
this.parent = parent;
}
}
}
所以,按照這么說栏赴,內(nèi)部類就是一種語法糖蘑斧。當(dāng)我們定義靜態(tài)變量時,就會產(chǎn)生下面這種歧義须眷。下面的代碼看起來沒什么問題竖瘾。
public class A {
private int a;
public class B {
public static void test() {
a = 1;
}
}
}
但是編譯之后,問題就來了花颗。
public class A {
private int a;
public static class B {
private final A parent;
public B (A parent) { this.parent = parent; }
public static void test() {
parent.a = 1; // 這里有語法錯誤
}
}
}
所以捕传,歸根結(jié)底,Java為了保持清晰的語法扩劝,不允許這種有歧義的語法存在庸论。