2013-08-08 5 views
9

Bitte beachten Sie den folgenden Code. Wenn ich den Code ausführen, kann ich den Wert einer endgültigen nicht statischen Variable ändern. Wenn ich jedoch versuche, den Wert einer endgültigen statischen Variablen zu ändern, wird java.lang.IllegalAccessException ausgegeben.Ändern von Endvariablen durch Reflexion, warum Unterschied zwischen statischer und nicht-statischer Endvariable

Meine Frage ist, warum wirft es nicht eine Ausnahme im Falle von nicht-statischen endgültigen Variable auch oder umgekehrt. Warum der Unterschied?

import java.lang.reflect.Field; 
import java.util.Random; 

public class FinalReflection { 

    final static int stmark = computeRandom(); 
    final int inmark = computeRandom(); 

    public static void main(String[] args) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { 
     FinalReflection obj = new FinalReflection(); 
     System.out.println(FinalReflection.stmark); 
     System.out.println(obj.inmark); 
     Field staticFinalField = FinalReflection.class.getDeclaredField("stmark"); 
     Field instanceFinalField = FinalReflection.class.getDeclaredField("inmark"); 
     staticFinalField.setAccessible(true); 
     instanceFinalField.setAccessible(true); 

     instanceFinalField.set(obj, 100); 
     System.out.println(obj.inmark); 

     staticFinalField.set(FinalReflection.class, 101); 
     System.out.println(FinalReflection.stmark); 

    } 

    private static int computeRandom() { 
     return new Random().nextInt(5); 
    } 
} 
+1

Ich habe den Code gepostet, der keine Ausnahme gibt. Aber es ist sicher ein Hack. –

Antwort

10
FinalReflectionobj = new FinalReflection(); 
System.out.println(FinalReflection.stmark); 
System.out.println(obj.inmark); 
Field staticFinalField = FinalReflection.class.getDeclaredField("stmark"); 
Field instanceFinalField = FinalReflection.class.getDeclaredField("inmark"); 
staticFinalField.setAccessible(true); 
instanceFinalField.setAccessible(true); 

//EXTRA CODE 
//Modify the final using reflection 
Field modifiersField = Field.class.getDeclaredField("modifiers"); 
modifiersField.setAccessible(true); 
modifiersField.setInt(staticFinalField, staticFinalField.getModifiers() & ~Modifier.FINAL); 


instanceFinalField.set(obj, 100); 
System.out.println(obj.inmark); 
staticFinalField.set(FinalReflection.class, 101); 
System.out.println(FinalReflection.stmark); 

Diese Lösung kommt nicht ohne einige Nachteile, kann es nicht in allen Fällen funktionieren:

Falls ein final Feld auf eine Kompilierung-Konstante in der Felddeklaration initialisiert wird, ändert sich Das Feld final ist möglicherweise nicht sichtbar, da die Verwendung dieses letzten Felds zur Kompilierzeit durch die Konstante für die Kompilierzeit ersetzt wird.

Ein weiteres Problem ist, dass die Spezifikation eine aggressive Optimierung der final Felder ermöglicht. Innerhalb eines Threads ist es zulässig, Lesevorgänge eines final-Felds mit den Änderungen eines final-Felds neu anzuordnen, die nicht im Konstruktor stattfinden. More auf das wird auch in dieser ähnlichen Frage erläutert.

+0

@assylias nein siehe den zusätzlichen Codeteil –

+2

@assylias Dies ermöglicht Ihnen auch das statische Endfeld zu ändern, wenn kein Sicherheitsmanager vorhanden ist. –

+0

Beachten Sie, dass dies nicht mit statischen finalen Primitiven funktioniert, die mit einem konstanten Ausdruck initialisiert wurden. – assylias

0

Schließlich kann es bei der Initialisierung verschiedene Werte zur Laufzeit zugewiesen werden.

Class Test{  
public final int a; 
} 

Test t1 = new Test(); 
t1.a = 10; 
Test t2 = new Test(); 
t1.a = 20; 

Somit hat jede Instanz unterschiedliche Werte von Feld a.

Bei statischen Finalwerten haben alle Instanzen denselben Wert und können nach der ersten Initialisierung nicht geändert werden.

Class TestStatic{ 
    public static final int a; 
} 

Test t1 = new Test(); 
t1.a = 10; 
Test t2 = new Test(); 
t1.a = 20; // ERROR, CAN'T BE ALTERED AFTER THE FIRST INITIALIZATION. 
+0

+1. Ja, ich stimme dem zu, was du sagst. –

+0

Aber er verwendet nur eine einzige Instanz der Klasse. Und eine nicht statische finale Variable ist nach der ersten Zuweisung nicht änderbar. Daher ist diese Erklärung falsch. –

2

The javadoc ist klar:

Wenn die zugrunde liegende Feld endgültig ist, wirft die Methode eine Illegal es sei denn, setAccessible (true) für dieses Objekt Field gelungen ist, und das Feld ist nicht statisch.

Aus JLS Perspektive, das genaue Verhalten, wie Reflexion arbeiten soll, ist nicht festgelegt, aber in JLS 17.5.4:

Normalerweise ein Feld, das letzte und statisch ist, kann nicht verändert werden.

Eine Abhilfe ist zu remove the final modifier through reflection.

+1

Ausgezeichnete Antwort, aber ich denke, die Frage ist eher: Warum haben die Java-Designer das entschieden? – morgano

+0

@morgano Wenn das der Fall ist, ist SO wahrscheinlich nicht der beste Ort, um zu fragen! Es verursacht sicherlich alle möglichen Probleme. Zum Beispiel sind primitive Konstanten zur Kompilierzeit inline, so dass Sie sie zur Laufzeit nur ändern können, wenn Sie den zugrunde liegenden Bytecode ändern. – assylias

+0

@assylias danke für die ausgezeichnete Info. Aber ja, meine Frage war wenig ausgerichtet auf das, was Morgano sagte. Aber ja, ich kann nicht zustimmen, dass SO vielleicht nicht der beste Ort ist, um das zu fragen. Ich werde es vom nächsten Mal abhaben. Jedes primitive Inlining gilt sowohl für statische als auch für nicht-statische finale Variablen. Versuchen Sie, den geänderten Codewert 5 anstelle von computeRandom() auszuführen. – veritas

Verwandte Themen