在无限循环中使用匿名vs嵌套类(Java)

时间:2016-01-11 15:58:06

标签: java anonymous-class

在无限循环的上下文中有什么更好的:匿名类或嵌套类? 正如我所看到的,如果我们使用匿名类,它会为每次迭代重新定义,但是使用嵌套类我们仍然会创建一个实例,但是类本身并没有重新定义。不确定这是否正确......在这种情况下,使用one vs other是否有任何优势?

public class MainThread {
    public static void main(String[] args){    
        while (true) {
        final int data = rand.nextInt();
        //Runnable task = new MyRunnable(data);
        Runnable task = new Runnable() {
            public void run() {
                printData(data);
            }
        };
        new Thread(task).start();
        }
    }

    private static class MyRunnable implements Runnable {
        private int data;

        MyRunnable(int data){
            this.data = data;
        }

        @Override
        public void run() {
            printData(data);
        }
    }
}

2 个答案:

答案 0 :(得分:5)

两者之间没有区别。两者都在编译时转换为类文件,类文件可以与任何其他类一样运行,无需重新编译或重新定义"循环内的任何东西。

但是在某些情况下会有一些细微差别,这可以使内部类稍微提高效率(虽然它可能会被JIT消除)。如果匿名类在实例方法中完成,则它将被编译为非静态类,因此在构造它时需要复制它的this引用包含类,这会添加一些字节码指令。这虽然可以忽略不计。

为了证明这一点,我写了这个小程序:

public class Test{
    public void test(){
        new Runnable(){
            public void run(){
                System.out.println("Anon");
            }
        }.run();
        new Inner().run();
    }

    class Inner implements Runnable{
        public void run(){
            System.out.println("Inner");
        }
    }

    static class Static implements Runnable{
        public void run(){
            System.out.println("Static");
        }
    }
}

这里是各个类推出的字节码:

测试:

public class Test {
  public Test();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  public void test();
    Code:
       0: new           #2                  // class Test$1
       3: dup           
       4: aload_0       
       5: invokespecial #3                  // Method Test$1."<init>":(LTest;)V
       8: invokevirtual #4                  // Method Test$1.run:()V
      11: new           #5                  // class Test$Inner
      14: dup           
      15: aload_0       
      16: invokespecial #6                  // Method Test$Inner."<init>":(LTest;)V
      19: invokevirtual #7                  // Method Test$Inner.run:()V
      22: return        
}

测试$ 1(匿名类):

class Test$1 implements java.lang.Runnable {
  final Test this$0;

  Test$1(Test);
    Code:
       0: aload_0       
       1: aload_1       
       2: putfield      #1                  // Field this$0:LTest;
       5: aload_0       
       6: invokespecial #2                  // Method java/lang/Object."<init>":()V
       9: return        

  public void run();
    Code:
       0: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #4                  // String Anon
       5: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return        
}

测试$ Inner(内部类):

class Test$Inner implements java.lang.Runnable {
  final Test this$0;

  Test$Inner(Test);
    Code:
       0: aload_0       
       1: aload_1       
       2: putfield      #1                  // Field this$0:LTest;
       5: aload_0       
       6: invokespecial #2                  // Method java/lang/Object."<init>":()V
       9: return        

  public void run();
    Code:
       0: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #4                  // String Inner
       5: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return        
}

测试$ Static(静态内部类):

class Test$Static implements java.lang.Runnable {
  Test$Static();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  public void run();
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3                  // String Static
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return        
}

从这里看,您可以看到唯一的区别是静态类的构造函数略小。

答案 1 :(得分:4)

匿名内部类不会为每次调用重新定义&#34; - 它们应该具有与内部类完全相同的开销,除了你的内部类是静态的,因此避免保留对外部myIframe的引用(如果有的话)。

如果您编译上面的程序,您将看到几个生成的类:

this

反编译$ 1(=你的内部匿名类)和$ MyRunnable(=你的内部命名类)表明它们基本相同:

MainThread$1.class
MainThread.class
MainThread$MyRunnable.class

并且

  MainThread$1(int);
    Code:
       0: aload_0       
       1: iload_1       
       2: putfield      #1                  // Field val$data:I
       5: aload_0       
       6: invokespecial #2                  // Method java/lang/Object."<init>":()V
       9: return        

  public void run();
    Code:
       0: aload_0       
       1: getfield      #1                  // Field val$data:I
       4: invokestatic  #3                  // Method MainThread.printData:(I)V
       7: return        

唯一的区别是匿名内部类不存储数据&#39;在它的构造函数中,而是从外部静态最终变量中读取它。

相关问题