2016-01-11 1 views
2

Quoi de mieux dans le contexte d'une boucle infinie: une classe anonyme ou une classe imbriquée? Comme je le vois, si nous utilisons une classe anonyme, elle est redéfinie pour chaque itération, mais en utilisant une classe imbriquée, nous créons toujours une instance, mais la classe elle-même n'est pas redéfinie. Je ne sais pas si c'est correct ... Y at-il un avantage à utiliser l'un par rapport à l'autre dans cette situation?Utilisation de la classe anonyme vs imbriquée dans une boucle infinie (Java)

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); 
     } 
    } 
} 

Répondre

4

Les classes internes anonymes ne sont pas "redéfinies pour chaque appel" - elles doivent avoir exactement le même surdébit qu'une classe interne, à l'exception que votre classe interne est statique et évite ainsi de garder une référence à la classe externe this .

Si vous compilez le programme ci-dessus, vous verrez plusieurs classes générées:

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

décompilation $ 1 (= votre classe anonyme interne) et le MyRunnable $ (= votre classe interne nommée) montre qu'ils sont essentiellement identique:

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   

Et

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

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

La seule La différence est que la classe interne anonyme ne stocke pas 'data' dans son constructeur et la lit plutôt à partir d'une variable finale statique externe.

5

Il n'y aura pas de différence entre les deux. Les deux sont convertis au moment de la compilation en fichiers de classe, qui peuvent être exécutés de la même manière que n'importe quelle autre classe, sans avoir besoin de recompiler ou de redéfinir quoi que ce soit dans la boucle.

Cependant, il existe une légère différence dans certains cas, ce qui peut rendre une classe interne légèrement plus efficace (bien qu'elle puisse être éliminée par le JIT). La classe anonyme sera compilée comme une classe non statique si elle est faite à l'intérieur d'une méthode d'instance, donc quand elle est construite, elle doit copier la référence this de sa classe contenant, qui ajoute quelques instructions de bytecode. Ce sera cependant un surcoût négligeable.

Pour le démontrer, j'ai écrit ce petit programme:

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"); 
     } 
    } 
} 

Et voici le bytecode mis par les différentes classes:

Test:

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   
} 

Test 1 $ (l'anonyme classe):

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   
} 

test $ intérieure (la classe interne):

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   
} 

test $ statique (la classe interne statique):

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   
} 

En regardant cela, vous pouvez voir que la seule différence est que la La classe statique a un constructeur légèrement plus petit.

+0

semble que nous avons écrit la même chose à peu près en même temps :-) – tucuxi

+0

@tucuxi Oui.Je vous ai donné un upvote, car j'avais complètement raté que la classe anonyme était statique dans ce cas: P – resueman