• 浅谈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为了保持清晰的语法,不允许这种有歧义的语法存在。

  • 相关阅读:
    第一次团队作业
    第二次结对作业
    动态代理与AOP
    笔试题
    java并发面试题(带答案)
    线程问题——同步和死锁
    java线程的方法
    java实现多线程的方法
    使用java闭锁实现并发
    Java多线程——同步问题
  • 原文地址:https://www.cnblogs.com/caipc/p/5930236.html
Copyright © 2020-2023  润新知