在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
对于这段匿名内部类代码实际上是能够拆分为以下形式:code
public class WildGoose extends Bird{ public int fly() { return 10000; } public String getName() { return "大雁"; } } WildGoose wildGoose = new WildGoose(); test.test(wildGoose);
在这里系统会建立一个继承自Bird类的匿名类的对象,该对象转型为对Bird类型的引用。对象
对于匿名内部类的使用它是存在一个缺陷的,就是它仅能被使用一次,建立匿名内部类时它会当即建立一个该类的实例,该类的定义会当即消失,因此匿名内部类是不可以被重复使用。对于上面的实例,若是咱们须要对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()); } }