本文共 2842 字,大约阅读时间需要 9 分钟。
在实际的项目中看到一个很奇怪的现象,Java可以直接new一个接口,然后在new里面粗暴的加入实现代码。就像下面这样。那么问题来了,new出来的对象没有实际的类作为载体,这不是很奇怪吗?思考以下代码的输出是什么?
Runnable x = new Runnable() { @Override public void run() { System.out.println(this.getClass()); }};x.run();实际答案是出现xxxx$1这样一个类名,它是编译器给定的名称。
匿名类相当于在定义类的同时再新建这个类的实例。我们来看看匿名类的编译结果。
这个类的代码如下:public class Test { public void test() { Runnable r = new Runnable(){ @Override public void run(){ System.out.println("hello"); } }; }}来看看它的编译结果,通过javap反向编译Test.class,得到的结果如下:
SourceFile: "Test.java"EnclosingMethod: #20.#21 // Test.testInnerClasses: #6; //class Test$1发现了一个字段叫EnclosingMethod,说明这个类是定义在Test.test方法下的。那现在有个问题,如果有两个test方法,会出现什么呢? 原来是旁边这个注释不太准确,实际上会包含函数签名的。请看Constant Pool部分,这里的#21指向了这个函数签名。
#21 = NameAndType #34:#16 // test:()V
Runnable hello = new Runnable() { public void run() { System.out.println("hello"); }};一个匿名类由以下几个部分组成: 1. new操作符 2. Runnable:接口名称。这里还可以填写抽象类、普通类的名称。 3. ():这个括号表示构造函数的参数列表。由于Runnable是一个接口,没有构造函数,所以这里填一个空的括号表示没有参数。 4. {...}:大括号中间的代码表示这个类内部的一些结构。在这里可以定义变量名称、方法。跟普通的类一样。
public class A { private int foo; public void test() { Runnable r = new Runnable() { System.out.println(foo); }; }}匿名类里面不可以有的东西: 1. 不能定义静态初始化代码块(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只能定义静态的。 3. 不能在匿名类中定义构造函数。
public class A { public void test() { Runnable r = new Runnable() { public Runnable() { } }; }}因为匿名类没有名字,而构造函数需要把类名作为方法名才能看成构造函数。 匿名类中可以包含的东西有: 1. 字段 2. 方法 3. 实例初始化代码 4. 本地类
public class A { public class B { }}它编译之后,会变成下面这种含义:
public class A { public static class B { private final A parent; public B(A parent) { this.parent = parent; } }}所以,按照这么说,内部类就是一种语法糖。 当我们定义静态变量时,就会产生下面这种歧义。 下面的代码看起来没什么问题。
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; // 这里有语法错误 } }}所以,归根结底,Java为了保持清晰的语法,不允许这种有歧义的语法存在。
转载地址:http://yvyub.baihongyu.com/