2014-04-24 6 views
5

Ich habe eine Situation, in der ich Java-Konstante ändern muss.Ändern der statischen Variable funktioniert primitive Wrapper, aber nicht mit primitiven Typ

ich unten Code haben Arbeits

import java.lang.reflect.Field; 
import java.lang.reflect.Modifier; 

public class Main { 
    public static final Integer FLAG = 44; 

    static void setFinalStatic(Class<?> clazz, String fieldName, Object newValue) throws NoSuchFieldException, IllegalAccessException { 
     Field field = clazz.getDeclaredField(fieldName); 
     field.setAccessible(true); 
     Field modifiers = field.getClass().getDeclaredField("modifiers"); 
     modifiers.setAccessible(true); 
     modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL); 
     field.set(null, newValue); 
    } 

    public static void main(String... args) throws Exception { 
     System.out.printf("Everything is %s%n", FLAG); 
     setFinalStatic(Main.class, "FLAG", 33); 
     System.out.printf("Everything is %s%n", FLAG); 
    } 
} 

Wenn ich oben laufen, ich folgende Ausgabe:

Everything is 44 
Everything is 33 

Aber wenn ich FLAG Variable ändern dh

public static final int FLAG = 44; 

int Es funktioniert nicht. Die Ausgabe lautet:

Everything is 44 
Everything is 44 

Gibt es eine andere Art und Weise es int mit Primitive Art zu arbeiten.

+1

Warum deklarieren Sie die Variable endgültig, wenn Sie sie ändern möchten? Entfernen Sie das Finale. – mvmn

+0

+1 zu dem obigen Kommentar. Die einfache Antwort ist, dass Ints nicht veränderbar sind, aber ich weiß nicht genug Deep Java, um mit Zuversicht zu antworten –

+0

Kompilierzeitkonstanten sind inline. Wenn der Wert bis zur Laufzeit nicht bekannt ist, kann er geändert werden. Hinweis: Wenn Sie 'final'-Variablen ändern, führt dies zu Verwirrung, so dass Sie dies in der Produktion nicht tun würden. –

Antwort

6

Von jls-4.12.4

Eine Variable vom Urtyp oder Typ String, das ist endgültig und initialisiert mit einem Kompilierung-Konstante Ausdruck (§15.28) wird ein constant variable genannt.

Abschnitt auch 13.1 sagt (Hervorhebung von mir)

3 .. Verweise auf Felder, die konstante Variablen (§4.12.4) bei der Kompilierung auf den konstanten Wert aufgelöst werden, die bezeichnet . Kein Verweis auf ein solches Feld sollte in dem Code in einer Binärdatei vorhanden sein (außer in der Klasse oder der Schnittstelle, die das Feld enthält, das einen Code haben wird, um es zu initialisieren). Ein solches Feld muss immer als initialisiert erscheinen (§12.4.2); Der Standardwert für den Typ eines solchen Feldes darf niemals eingehalten werden.

Es bedeutet, dass Kompilierung-Konstante Ausdruck von konstanten Variablen direkt vom Compiler in Code eingefügt werden (es inlined wird) nicht von endgültiger Referenz zur Laufzeit gelesen.

Zum Beispiel, wenn Sie main Methode von Bar Klasse

class Foo{ 
    static{ 
     System.out.println("test if class will be loaded"); 
    } 
    public static final int x = 42; 
} 

class Bar{ 
    public static void main(String [] args){ 
     System.out.println(Foo.x); 
    } 
} 

Sie keine Ausgabe von statischem Block von Foo Klasse ausführen sehen, die Foo Klasse bedeutet, wird nicht eingelegt, was bedeutet, dass der Wert Foo.x ‚didn Ich komme aus dieser Klasse. In der Tat Bar wurde auf diese Weise zusammengestellt

class Bar{ 
    public static void main(String [] args){ 
     System.out.println(42); // reference Foo.x will be removed by compiler 
           // and replaced with actual value because 
           // compiler assumes that value can't/shouldn't 
           // change at runtime 
    } 
} 

Also selbst änderte Wert von Foo.x zur Laufzeit wird nicht Wert in main Methode in Bar Klasse gedruckt Affekt.

Sie können diesen Mechanismus nicht ändern.


Mögliche herum würde Ihre letzten Felder mit Werten zur Laufzeit Zeit erstellt wird initialisiert (sie würden nicht zum Zeitpunkt der Kompilierung existieren, die sie davon, dass konstanten Ausdrücke Compile-Zeit verhindern). Also statt

public static final String x = "foo"; 

versuchen

public static final String x = new String("foo"); 

oder bei primitiven Typen verwenden Unboxing wie statt

public static final int x = 42; 

Verwendung

public static final int x = new Integer(42); 
+0

Schlüsselwörter zum Nachschlagen sind konstante Ausdrücke und konstante Variablen. –

+0

@SotiriosDelimanolis Könnten Sie sich die bearbeitete Version oder meine Antwort ansehen? Ich bin definitiv kein Java-Experte, also könnte ich einige einfache, aber entscheidende Fehler machen, die mir nicht bekannt sind:/ – Pshemo

+0

Ich glaube nicht, dass irgendwas falsch ist. Das ist die richtige Antwort. –

0

Dies geschieht, weil static final Felder von primitiven oder St Ringtyp wird während der Kompilierzeit inline gesendet. Die wichtigste Methode sieht ungefähr so ​​aus nach der Kompilierung und de-Compilation

public static void main(String... args) throws Exception { 
     System.out.printf("Everything is %s%n", 44); 
     setFinalStatic(Main.class, "FLAG", 33); 
     System.out.printf("Everything is %s%n", 44); 
    } 

weil FLAG mit Ist-Wert in der Kompilierung-Zeit ersetzt wird.

1
  1. Primitive Typen werden inline.

  2. Tatsächlich werden auch nicht-primitive Konstanten, wenn sie in andere Klassen importiert werden, kopiert und der Import wird vergessen. Also wird es auch nicht funktionieren. Nur für konstante Caches wie den String-Pool und Integer-Caches (Integer.valueOf (13)) können Sie ihre Werte überschreiben.

Verwandte Themen