2009-09-10 4 views
18

diesen Fall vor:Ist Java garantiert String-Konstanten inline, wenn sie zum Zeitpunkt der Kompilierung bestimmt werden kann

public Class1 { 
    public static final String ONE = "ABC"; 
    public static final String TWO = "DEF"; 
} 

public Class2 { 

    public void someMethod() { 
    System.out.println(Class1.ONE + Class1.TWO); 
    } 
} 

Normalerweise würden Sie der Compiler erwarten, dass die ONE und TWO Konstanten Inline. Ist dieses Verhalten jedoch garantiert? Können Sie zur Laufzeit Class2 ohne Class1 im Klassenpfad bereitstellen und erwarten, dass es unabhängig von Compilern funktioniert, oder handelt es sich hierbei um eine optionale Compiler-Optimierung?

EDIT: Warum in aller Welt tun dies? Nun, ich habe eine Konstante, die zwischen zwei Enden einer Anwendung geteilt wird (Client und Server über RMI), und es wäre in diesem speziellen Fall sehr praktisch, die Konstante auf eine Klasse zu setzen, die nur auf einer Seite dieser Teilung sein kann (wie es logisch derjenige ist, der diesen konstanten Wert besitzt), anstatt es in einer willkürlichen Konstantenklasse zu haben, nur weil es von beiden Seiten des Codes geteilt werden muss. Zur Kompilierungszeit ist alles ein Satz von Quelldateien, aber zum Zeitpunkt der Erstellung ist es durch das Paket geteilt.

Antwort

23

Es ist garantiert als konstanter Ausdruck behandelt werden, und garantiert durch section 15.28 of the JLS interniert werden:

Ein Kompilierung-Konstante Ausdruck ist ein Ausdruck einen Wert von Urtyp oder einen String angibt, das tut nicht abrupt komplett und ist nur unter Verwendung der folgenden zusammengesetzt ist:

  • Literale Urtyp und Literale vom Typ String (§3.10.5)
  • Umwandeln in primitive Typen und Umwandlungen zum Typ String
  • Die unären Operatoren +, -, ~, und! (Aber nicht ++ oder -)
  • Die multiplikativen Operatoren *,/und%
  • Die additiven Operatoren + und -
  • ...

...

Kompilierzeitkonstanten vom Typ String werden immer "interniert", um eindeutige Instanzen zu teilen, wobei die Methode String.intern verwendet wird.

Nun, das sagt nicht ganz, dass es garantiert inline ist. Abschnitt 13.1 der Spezifikation jedoch sagt:

Verweise auf Felder, die konstant sind Variablen (§4.12.4) werden aufgelöst in Kompilierung auf den konstanten Wert , die gekennzeichnet ist. Kein Hinweis auf solche ein konstantes Feld in der Code in einer Binärdatei vorhanden sein sollte (außer in die Klasse oder Schnittstelle die konstanten Feld enthält, das Code haben es zu initialisieren), und eine solche Konstante Felder müssen immer scheint initialisiert worden zu sein; der Standardwert für den Typ eines solchen Feldes muss nie beachtet werden.

Mit anderen Worten, auch wenn der Ausdruck nicht selbst ein konstanten war, soll es zu Class1 kein Bezug. Also ja, dir geht es gut. Das garantiert nicht garantieren, dass der verkettete Wert im Bytecode verwendet wird, aber die zuvor referenzierten Bits garantieren, dass der verkettete Wert interniert ist, also würde ich harlyly überrascht sein, wenn es nicht nur den verketteten Wert inline . Selbst wenn dies nicht der Fall ist, ist garantiert, dass es ohne Class1 funktioniert.

+1

Danke Jon, aber das ist nicht genau das, was ich meinte. "interniert" bedeutet, dass es nur eine Instanz gibt, aber Sie könnten immer noch einen NoClassDefError erhalten, wenn die Klasse zur Laufzeit fehlt, wenn sie vom Compiler nicht wirklich inline war. – Yishai

+0

Vielleicht bin ich blind, aber wo sagt man das? – gshauger

+0

@gshaugher: Welches bisschen? –

0

Es wird nicht vom Compiler, sondern vom Interpreter zur Laufzeit inline und wenn möglich in Assemblercode konvertiert.

Es kann nicht garantiert werden, da nicht alle Interpreter (JVMs) gleich funktionieren. Aber die wichtigsten Implementierungen werden tun.

Leider habe ich keinen Link zu diesem :( aufrechtzuerhalten

0

Ich vermute, weiß aber nicht sicher, dass dies funktionieren wird, aber es klingt nicht wie eine gute Idee.

die „normalen“ Wege, dies zu tun sind.

  1. die Konstanten in ein Paket geschnürt, das vermutlich zwischen dem Client und dem Server freigegeben ist, gibt es ein solches Paket, weil das ist, wo die Schnittstellen gehen.
  2. Wenn es kein solches Paket gibt, erstellen Sie 2 Klassen mit den gemeinsamen Konstanten: eine für den Server und eine für den Client.
0

Siehe JLS 13.4.9. Es ist zwar nicht explizit erforderlich, dass Konstanten vom Compiler eingebunden werden, aber es deutet an, dass die bedingte Kompilierung und die Unterstützung für Konstanten in switch-Anweisungen den Compiler dazu veranlassen, immer Inline-Konstanten zu verwenden.

10

Kompilieren, dass mit javac 1.6.0_14 die folgenden Bytecode erzeugt:

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

So die Saiten bei der Kompilierung verkettet und das Ergebnis wird in Class2 Konstante Pool enthalten.

+1

+1, danke für die Forschung , aber ich wusste, dass der Compiler sich so verhielt Meine Frage war, ob die Spezifikation dies garantierte oder ob das ein optionales Verhalten war (und daher später wieder beißen könnte) – Yishai

0

Es sieht aus wie Sie Ihre eigene Version der in enum eingebaute Fähigkeit Codierung, die public static final für Sie tut, korrekte Namensgebung über name() und toString() (sowie einige andere Vorteile haben, aber vielleicht den Nachteil eines größeren mit Speicherabdruck).

Verwenden Sie eine ältere Version von Java, die Enum noch nicht enthält?

+0

Mein Anwendungsfall stellt Kontextwurzeln und URLs dar, also ist es ein String straight up. Der Zweck, eine Konstante zu machen, ist zu vermeiden, es zu duplizieren, falls es sich ändert. Also Enums wären in diesem Fall Overkill. Ich benutze Java 1.5, also habe ich Enums und benutze sie (vielleicht nicht oft genug)). – Yishai

Verwandte Themen