2013-04-11 13 views
11

Ho decompilato po 'di codice Java, l'altro giorno ed ho trovato questo:Java - stringhe uguale quando decompilato

String s1 = "something"; 
String s2 = "something_else"; 

if (s1 == s2) { 
// Path 1 
} else { 
// Path 2 
} 

Ovviamente usando '==' per verificare l'uguaglianza stringa è male

Ma mi sono chiesto - Questo il codice è stato compilato e decompilato. Se tutte le stringhe sono state definite in fase di compilazione e internate e il codice è stato compilato, è possibile che s1.equals (s2) sia stato ottimizzato fino a 's1 == s2'?

+0

hai provato? – durron597

+5

Un modo sarebbe compilare un codice proprio così e vedere cosa viene restituito dal decompilatore :) – dasblinkenlight

+0

Quale codice compilato è stato decompilato da? –

risposta

8

Lo dubito fortemente. Di norma, i compilatori Java eseguono molto poco tramite l'ottimizzazione bytecode, lasciando l'ottimizzazione alla fase JIT.

Ho sperimentato questo un po ', e il mio compilatore non fa nulla di interessante con il seguente:

public class Clazz { 

    public static void main(String args[]) { 
     final String s1 = "something"; 
     final String s2 = "something_else"; 
     if (s1.equals(s2)) { 
      System.out.println("yes"); 
     } else { 
      System.out.println("no"); 
     } 
    } 

} 

Questo sarebbe probabilmente il caso più facile da ottimizzare. Tuttavia, i bytecode sono:

public static void main(java.lang.String[]); 
    Code: 
     0: ldc   #16     // String something 
     2: astore_1  
     3: ldc   #18     // String something_else 
     5: astore_2  
     6: ldc   #16     // String something 
     8: ldc   #18     // String something_else 
     10: invokevirtual #20     // Method java/lang/String.equals:(Ljava/lang/Object;)Z 
     13: ifeq   27 
     16: getstatic  #26     // Field java/lang/System.out:Ljava/io/PrintStream; 
     19: ldc   #32     // String yes 
     21: invokevirtual #34     // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
     24: goto   35 
     27: getstatic  #26     // Field java/lang/System.out:Ljava/io/PrintStream; 
     30: ldc   #40     // String no 
     32: invokevirtual #34     // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
     35: return   

quindi con forza il sospetto che la == faceva parte del codice sorgente originale.

1

No, non sembra che Java lo ottimizzi (impostazione predefinita).

Ho appena confrontato entrambe le soluzioni. Se non è ottimizzato, ci aspettiamo di vedere s1.equals(s2) più lento di s1 == s2. Questo è esattamente ciò che vediamo. Se fosse ottimizzato, lo s1.equals(s2) richiederebbe lo stesso tempo di s1==s2. Tuttavia, richiedono tempi diversi (dell'ordine di 50.000 nanosecondi). Questo è non una misura diretta di questa compilation, ma è una deduzione ragionevole.

Il motivo per cui questo non sarà ottimizzato per == è perché l'operatore di uguale, per gli oggetti, confronterà l'indirizzo di memoria dell'oggetto, non il contenuto dell'oggetto stesso. Quindi, se si modifica s1, quindi, se il compilatore ha ottimizzato questo, si cambierebbe anche s2.

Tuttavia, questo rischia di infrangere il codice, quindi il compilatore non lo farà. Lascerà gli indirizzi di memoria di s1 e s2 essere.

+0

Perché è down down di questo? Mi stavo solo chiedendo. – RaptorDotCpp

+0

@RaptorDotCpp Penso che sia stato tattico. Oh bene. – Zyerah

+0

Sono leggermente sorpreso che il JIT non ottimizzi questo. Hai scritto un benchmark sensato con warmup, metodo personale, ecc. Ecc.? – Voo

0

La regola principale è che se il compilatore può dedurre il valore esatto dal codice sorgente che ha nella singola classe. Perché fa tutte le ottimizzazioni usando solo la più piccola unità di compilazione - classe. Se scrivo un codice

public class Test 
{ 
    private static final String i = "1"; 
    public static void main(String[] args) 
    { 
     if(i == "2") 
      System.out.println("hello"); 
     System.out.println("world"); 
    } 
} 

Il compilatore vede tutto il codice relativo alla dichiarazione in questa classe e ottimizza la se la condizione. Dopo de-compilatore del codice è simile

public class Test 
{ 
    private static final String i = "1"; 

    public static void main(String[] paramArrayOfString) 
    { 
    System.out.println("world"); 
    } 
} 

(ho usato jd-gui)

Tuttavia, se si sostituisce == dal .equals, compilatore non può assumere come il metodo .equals funziona.Perché, dopo la compilazione della classe Test, è possibile modificare il JDK e inserire un'altra versione della classe che restituisce true per "1".equals("2").

Quindi, pensando all'ottimizzazione che il compilatore può fare, prima di tutto pensa a come il compilatore può comportarsi se una classe può essere ricompilata in seguito.

Come altro esempio, è possibile vedere how enum is implemented e perché ha bisogno di un modo "strano".

Problemi correlati