浅谈Java的匿名类

在实际的项目中看到一个很奇怪的现象,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.test
InnerClasses:
     #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为了保持清晰的语法,不允许这种有歧义的语法存在。

时间: 2024-10-26 03:49:40

浅谈Java的匿名类的相关文章

浅谈Java中的类与对象

面向对象基本概念:封装.继承.多态 封装:就是把数据和行为结合在一起形成统一的整体,并对对象使用者隐藏数据的实现过程. 继承:Java继承是使用已存在的类的定义作为基础建立新类的技术,继承避免了对一般类和特殊类之间共同特征进行的重复描述. 多态:多态指同一个实体同时具有多种形式. Java作为一种面向对象语言.支持以下基本概念: 多态 继承 封装 抽象 类 对象 实例 方法 消息解析 1. 什么是类? 答:类是客观存在的,抽象的,概念的东西. 2. 什么是对象? 答:对象是具体的,实际的,代表一

浅谈JAVA中“增强”类的某个方法的几个中方法!

一.继承 使用场景:能够控制这个类的构造的时候,才可以使用继承. 优点:简单容易使用, 缺点:耦合性大大的增强,不利于后期的维护,所以对于继承这种方法,谨慎使用. 代码实现: 二.装饰者模式 使用场景:1.包装对象和被包装对象实现相同的接口 2.包装的对象中需要获得到被包装对象的引用. 缺点:如果接口的方法比较多,增强其中某个方法,其他的功能的方法需要原有的调用: 代码实现:    /**      * 装饰者模式:     *           增强request的getParameter方

浅谈java类集框架和数据结构(2)

继续上一篇浅谈java类集框架和数据结构(1)的内容 上一篇博文简介了java类集框架几大常见集合框架,这一篇博文主要分析一些接口特性以及性能优化. 一:List接口 List是最常见的数据结构了,主要有最重要的三种实现:ArrayList,Vector,LinkedList,三种List均来自AbstracList的实现,而AbstracList直接实现了List接口,并拓展自AbstractCollection. 在三种实现中,ArrayList和Vector使用了数组实现,可以认为这两个是

浅谈 Java Printing

浅谈 Java  Printing 其实怎么说呢?在写这篇博文之前,我对java printing 可以说是一无所知的.以至于我在敲文字时, 基本上是看着api文档翻译过来的.这虽然看起来非常的吃力,但是我相信,有道大哥不会辜负我的.嘻 嘻! Java Printing 技术,也就是我们平时所接触的打印,只不过是说可以用Java实现而已. 一.Java Printing 打印简介 Java Printing API能够使java应用程序实现相关的打印功能,如: 1.打印所有 Java 2D 和

【转】浅谈Java中的equals和==

浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: 1 String str1 = new String("hello"); 2 String str2 = new String("hello"); 3 4 System.out.println(str1==str2); 5 System.out.println(str1.equals(str2)); 为什么第4行和第5行的输出结果不一样?==和equals方法之间的区别是什么?如果在初

浅谈java异常[Exception]

本文转自:focusJ 一. 异常的定义 在<java编程思想>中这样定义 异常:阻止当前方法或作用域继续执行的问题.虽然java中有异常处理机制,但是要明确一点,决不应该用"正常"的态度来看待异常.绝对一点说异常就是某种意义上的错误,就是问题,它可能会导致程序失败.之所以java要提出异常处理机制,就是要告诉开发人员,你的程序出现了不正常的情况,请注意. 记得当初学习java的时候,异常总是搞不太清楚,不知道这个异常是什么意思,为什么会有这个机制?但是随着知识的积累逐渐也

浅谈Java中的对象和引用

浅谈Java中的对象和对象引用 在Java中,有一组名词经常一起出现,它们就是"对象和对象引用",很多朋友在初学Java的时候可能经常会混淆这2个概念,觉得它们是一回事,事实上则不然.今天我们就来一起了解一下对象和对象引用之间的区别和联系. 1.何谓对象? 在Java中有一句比较流行的话,叫做"万物皆对象",这是Java语言设计之初的理念之一.要理解什么是对象,需要跟类一起结合起来理解.下面这段话引自<Java编程思想>中的一段原话: "按照通

浅谈Java中的equals和==

浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: 1 String str1 = new String("hello"); 2 String str2 = new String("hello"); 3 4 System.out.println(str1==str2); 5 System.out.println(str1.equals(str2)); 为什么第4行和第5行的输出结果不一样?==和equals方法之间的区别是什么?如果在初

浅谈Java throw, throws, try catch异常处理

1.throws关键字通常被应用在声明方法时,用来指定可能抛出的异常.多个异常可以使用逗号隔开.当在主函数中调用该方法时,如果发生异常,就会将异常抛给指定异常对象.如下面例子所示: public class Shoot {   创建类 static void pop() throws NegativeArraySizeException { //定义方法并抛出NegativeArraySizeException异常 int [] arr = new int[-3];//创建数组 } public