在java提升篇-----詳解內部類中對匿名內部類作了一個簡單的介紹,可是內部類還存在不少其餘細節問題,因此就衍生出這篇博客。在這篇博客中你能夠了解到匿名內部類的使用、匿名內部類要注意的事項、如何初始化匿名內部類、匿名內部類使用的形參爲什麼要爲final。java
匿名內部類因爲沒有名字,因此它的建立方式有點兒奇怪。建立格式以下:android
new 父類構造器(參數列表)|實現接口() { //匿名內部類的類體部分 }
在這裏咱們看到使用匿名內部類咱們必需要繼承一個父類或者實現一個接口,固然也僅能只繼承一個父類或者實現一個接口。同時它也是沒有class關鍵字,這是由於匿名內部類是直接使用new來生成一個對象的引用。固然這個引用是隱式的。程序員
public abstract class Bird { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public abstract int fly(); } public class Test { public void test(Bird bird){ System.out.println(bird.getName() + "可以飛 " + bird.fly() + "米"); } public static void main(String[] args) { Test test = new Test(); test.test(new Bird() { public int fly() { return 10000; } public String getName() { return "大雁"; } }); } } ------------------ Output: 大雁可以飛 10000米
在Test類中,test()方法接受一個Bird類型的參數,同時咱們知道一個抽象類是沒有辦法直接new的,咱們必需要先有實現類才能new出來它的實現類實例。因此在mian方法中直接使用匿名內部類來建立一個Bird實例。函數
因爲匿名內部類不能是抽象類,因此它必需要實現它的抽象父類或者接口裏面全部的抽象方法。this
對於這段匿名內部類代碼實際上是能夠拆分爲以下形式:spa
public class WildGoose extends Bird{ public int fly() { return 10000; } public String getName() { return "大雁"; } } WildGoose wildGoose = new WildGoose(); test.test(wildGoose);
在這裏系統會建立一個繼承自Bird類的匿名類的對象,該對象轉型爲對Bird類型的引用。code
對於匿名內部類的使用它是存在一個缺陷的,就是它僅能被使用一次,建立匿名內部類時它會當即建立一個該類的實例,該類的定義會當即消失,因此匿名內部類是不可以被重複使用。對於上面的實例,若是咱們須要對test()方法裏面內部類進行屢次使用,建議從新定義類,而不是使用匿名內部類。對象
在使用匿名內部類的過程當中,咱們須要注意以下幾點:blog
一、使用匿名內部類時,咱們必須是繼承一個類或者實現一個接口,可是二者不可兼得,同時也只能繼承一個類或者實現一個接口。繼承
二、匿名內部類中是不能定義構造函數的。
三、匿名內部類中不能存在任何的靜態成員變量和靜態方法。
四、匿名內部類爲局部內部類,因此局部內部類的全部限制一樣對匿名內部類生效。
五、匿名內部類不能是抽象的,它必需要實現繼承的類或者實現的接口的全部抽象方法。
參考文件:http://android.blog.51cto.com/268543/384844
咱們給匿名內部類傳遞參數的時候,若該形參在內部類中須要被使用,那麼該形參必需要爲final。也就是說:當所在的方法的形參須要被內部類裏面使用時,該形參必須爲final。
爲何必需要爲final呢?
首先咱們知道在內部類編譯成功後,它會產生一個class文件,該class文件與外部類並非同一class文件,僅僅只保留對外部類的引用。當外部類傳入的參數須要被內部類調用時,從java程序的角度來看是直接被調用:
public class OuterClass { public void display(final String name,String age){ class InnerClass{ void display(){ System.out.println(name); } } } }
從上面代碼中看好像name參數應該是被內部類直接調用?其實否則,在java編譯以後實際的操做以下:
public class OuterClass$InnerClass { public InnerClass(String name,String age){ this.InnerClass$name = name; this.InnerClass$age = age; } public void display(){ System.out.println(this.InnerClass$name + "----" + this.InnerClass$age ); } }
因此從上面代碼來看,內部類並非直接調用方法傳遞的參數,而是利用自身的構造器對傳入的參數進行備份,本身內部方法調用的實際上時本身的屬性而不是外部方法傳遞進來的參數。
直到這裏尚未解釋爲何是final?在內部類中的屬性和外部方法的參數二者從外表上看是同一個東西,但實際上卻不是,因此他們二者是能夠任意變化的,也就是說在內部類中我對屬性的改變並不會影響到外部的形參,而然這從程序員的角度來看這是不可行的,畢竟站在程序的角度來看這兩個根本就是同一個,若是內部類該變了,而外部方法的形參卻沒有改變這是難以理解和不可接受的,因此爲了保持參數的一致性,就規定使用final來避免形參的不改變。
簡單理解就是,拷貝引用,爲了不引用值發生改變,例如被外部類的方法修改等,而致使內部類獲得的值不一致,因而用final來讓該引用不可改變。
故若是定義了一個匿名內部類,而且但願它使用一個其外部定義的參數,那麼編譯器會要求該參數引用是final的。
咱們通常都是利用構造器來完成某個實例的初始化工做的,可是匿名內部類是沒有構造器的!那怎麼來初始化匿名內部類呢?使用構造代碼塊!利用構造代碼塊可以達到爲匿名內部類建立一個構造器的效果。
public class OutClass { public InnerClass getInnerClass(final int age,final String name){ return new InnerClass() { int age_ ; String name_; //構造代碼塊完成初始化工做 { if(0 < age && age < 200){ age_ = age; name_ = name; } } public String getName() { return name_; } public int getAge() { return age_; } }; } public static void main(String[] args) { OutClass out = new OutClass(); InnerClass inner_1 = out.getInnerClass(201, "chenssy"); System.out.println(inner_1.getName()); InnerClass inner_2 = out.getInnerClass(23, "chenssy"); System.out.println(inner_2.getName()); } }
鞏固基礎,提升技術,不懼困難,攀登高峯!!!!!!