匿名内部类详解
匿名内部类
匿名内部类就是没有名字的内部类;
注意:
匿名内部类不能定义任何静态成员、方法。
匿名内部类中的方法不能是抽象的;
匿名内部类必须实现接口或抽象父类的所有抽象方法。
匿名内部类访问的外部类成员变量或成员方法必须用static修饰;
代码:
接口
public interface Inner { public String say(); }
抽象类
public abstract class Inner1 implements Inner { }
普通类
public class Inner2 implements Inner { public String say() { return "this is Inner2"; } }
外部类
public class Outer { public static String s1 = "this is s1 in Outer"; public static String s2 = "this is s2 in Outer"; private static String s3 = "this is s3 in Outer"; public void method1(Inner inner) { System.out.println(inner.say()); } private static String method2() { return "this is method2 in Outer"; } public static void main(String[] args) { Outer outer = new Outer(); // 测试1,Inner为接口 outer.method1(new Inner() { String s1 = "this is s1 in Inner"; public String say() { // 外部类和匿名函数类中有同名变量s1 return s1; } }); // 测试2,Inner1为抽象类 outer.method1(new Inner1() { String s2 = "this is s2 in Inner1"; public String say() { // 外部类和匿名函数类中有同名变量s2 return Outer.s2; } }); // 测试3,Inner2为普通类 outer.method1(new Inner2() { public String say() { // 访问外部类私有变量s3 return s3; } }); // 测试4,Inner2为普通类 outer.method1(new Inner2() { public String say() { // 访问外部类私有方法method1() return method2(); } }); } }
打印:
this is s1 in Inner this is s2 in Outer this is s3 in Outer this is method2 in Outer
分析:
编译后自动生成四个文件:Outer$1.class、Outer$2.class、Outer$3.class、Outer$4.class。
Outer.class反编译(属性值、方法内容略)
public class jichu.Outer { public static java.lang.String s1; public static java.lang.String s2; private static java.lang.String s3; static {}; public jichu.Outer(); public void method1(jichu.Inner); private static java.lang.String method2(); public static void main(java.lang.String[]); static java.lang.String access$0(); static java.lang.String access$1(); }
Outer$1.class反编译(经优化、调整)
class Outer$1 implements Inner { String s1 = "this is s1 in Inner"; public String say() { return this.s1; } }
Outer$2.class反编译(经优化、调整)
class Outer$2 extends Inner1 { String s2 = "this is s2 in Inner1"; public String say() { return Outer.s2; } }
Outer$3.class反编译(经优化、调整)
class Outer$3 extends Inner2 { public String say() { return Outer.access$0(); } }
Outer$4.class反编译(经优化、调整)
class Outer$4 extends Inner2 { public String say() { return Outer.access$1(); } }
1、匿名内部类因为没有类名,可知匿名内部类不能定义构造器。
2、因为在创建匿名内部类的时候,会立即创建它的实例,可知匿名内部类不能是抽象类,必须实现接口或抽象父类的所有抽象方法。
3、匿名内部类会继承一个父类(有且只有一个)或实现一个接口(有且只有一个),实现父类或接口中所有抽象方法,可以改写父类中的方法,添加自定义方法。
5、当匿名内部类和外部类有同名变量(方法)时,默认访问的是匿名内部类的变量(方法),要访问外部类的变量(方法)则需要加上外部类的类名。
6、从Outer.class反编译代码中可看出自动生成了两个静态方法:access$0()和access$1(),并在测试3和测试4中通过Outer类名直接调用,这样实现了内部类对外部类私有成员变量和方法的访问。可知内部类可以访问外部类私有变量和方法。
疑问
匿名内部类不能含有static的变量和方法。但是测试发现变量可以被static final修饰,为什么?
主要是因为final类型在编译期间jvm有优化,常量池会维护这些变量。虽然非静态内部类不能脱离外部类这个上下文实例化,但是常量池使得final变量脱离了类实例化这个条件,编译期间便可确定。
总结
1、匿名内部类不能定义任何静态成员、方法。
2、匿名内部类中的方法不能是抽象的;
3、匿名内部类必须实现接口或抽象父类的所有抽象方法。
4、匿名内部类不能定义构造器;
5、匿名内部类访问的外部类成员变量或成员方法必须用static修饰;
6、内部类可以访问外部类私有变量和方法。