博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
浅谈Java的匿名类
阅读量:2190 次
发布时间:2019-05-02

本文共 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. {...}:大括号中间的代码表示这个类内部的一些结构。在这里可以定义变量名称、方法。跟普通的类一样。

访问权限

那么匿名内部类能访问哪些东西呢?按照规则,可以访问如下内容:
1. 访问外层Class里面的字段。
2. 不能访问外层方法中的本地变量。除非变量是final。
3. 如果内部类的名称和外面能访问的名称相同,则会把名称覆盖掉。
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. 本地类

为什么不能定义静态初始化代码

事实上,内部类中不能定义任何静态的东西。
关键字:Inner class cannot have static declarations
参考资料:http://stackoverflow.com/questions/975134/why-cant-we-have-static-method-in-a-non-static-inner-class
StackOverFlow上看起来有一种解释如下。
首先来看一个内部类。
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/

你可能感兴趣的文章
初探Java设计模式1:创建型模式(工厂,单例等)
查看>>
初探Java设计模式2:结构型模式(代理模式,适配器模式等)
查看>>
初探Java设计模式3:行为型模式(策略,观察者等)
查看>>
初探Java设计模式4:一文带你掌握JDK中的设计模式
查看>>
初探Java设计模式5:一文了解Spring涉及到的9种设计模式
查看>>
Java集合详解1:一文读懂ArrayList,Vector与Stack使用方法和实现原理
查看>>
Java集合详解2:一文读懂Queue和LinkedList
查看>>
Java集合详解3:一文读懂Iterator,fail-fast机制与比较器
查看>>
Java集合详解4:一文读懂HashMap和HashTable的区别以及常见面试题
查看>>
Java集合详解5:深入理解LinkedHashMap和LRU缓存
查看>>
Java集合详解6:这次,从头到尾带你解读Java中的红黑树
查看>>
Java集合详解7:一文搞清楚HashSet,TreeSet与LinkedHashSet的异同
查看>>
Java集合详解8:Java集合类细节精讲,细节决定成败
查看>>
Java并发指南1:并发基础与Java多线程
查看>>
Java并发指南2:深入理解Java内存模型JMM
查看>>
Java并发指南3:并发三大问题与volatile关键字,CAS操作
查看>>
Java并发指南4:Java中的锁 Lock和synchronized
查看>>
Java并发指南5:JMM中的final关键字解析
查看>>
Java并发指南6:Java内存模型JMM总结
查看>>
Java并发指南7:JUC的核心类AQS详解
查看>>