2013-08-08 10 views
9

Proszę odnieść się do poniższego kodu. Po uruchomieniu kodu mogę zmienić wartość końcowej zmiennej niestatycznej. Ale jeśli spróbuję zmienić wartość końcowej zmiennej statycznej, wówczas wyrzuca ona java.lang.IllegalAccessException.zmiana ostatecznych zmiennych poprzez odbicie, dlaczego różnica między statyczną a niestatyczną zmienną końcową

Moje pytanie brzmi: dlaczego nie wyrzuca wyjątku w przypadku niestatycznej zmiennej końcowej również lub odwrotnie. Dlaczego różnica?

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

Opublikowałem kod, który nie daje wyjątku. Ale na pewno jest to hak. –

Odpowiedz

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

To rozwiązanie nie przychodzi bez niektórych downsides, to może nie działać we wszystkich przypadkach:

W przypadku pole final jest inicjowany w czasie kompilacji stałą w deklaracji pola, zmiany do pola final może nie być widoczne, ponieważ zastosowania tego ostatniego pola są zastępowane podczas kompilacji ze stałą kompilacji.

Innym problemem jest to, że specyfikacja umożliwia agresywną optymalizację pól final. W obrębie wątku dopuszczalne jest ponowne uporządkowanie odczytów pola final przy tych modyfikacjach pola, które nie mają miejsca w konstruktorze. More na ten temat wyjaśniono również w tym podobnym pytaniu.

+0

@assylias nie widzę dodatkowego kodu część –

+2

@assylias to pozwoli Ci zmienić statyczne pole końcowe, jeśli nie ma menedżera bezpieczeństwa. –

+0

Należy zauważyć, że nie zadziała to w przypadku statycznych finalnych prymitywów inicjowanych za pomocą stałego wyrażenia. – assylias

0

Po zakończeniu może zostać przypisane różne wartości w czasie uruchamiania po zainicjowaniu.

Class Test{  
public final int a; 
} 

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

W ten sposób każde wystąpienie ma inną wartość pola a.

W przypadku statycznego finału wszystkie wystąpienia mają tę samą wartość i nie można ich zmienić po pierwszej inicjalizacji.

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. Tak, zgadzam się z tym, co mówisz. –

+0

Ale on używa tylko jednego pojedynczego wystąpienia klasy. A niestatyczna zmienna końcowa nie może być zmieniona po pierwszym przypisaniu. Dlatego to wyjaśnienie jest błędne. –

2

The javadoc jest jasne:

Jeśli pole bazowy jest ostateczna metoda zgłasza IllegalAccessException chyba setAccessible (prawda), udało się na tym polu Obiekt a pole nie jest statyczny.

Z perspektywy JLS, dokładne zachowanie jak odbicie powinno działać nie jest określony, ale w JLS 17.5.4:

Normalnie, pole, które jest ostateczna i statyczne nie mogą być modyfikowane.

Jedno obejście to remove the final modifier through reflection.

+1

Doskonała odpowiedź, ale myślę, że pytanie brzmi raczej: dlaczego projektanci java decydują o tym? – morgano

+0

@morgano, jeśli tak jest, SO prawdopodobnie nie jest najlepszym miejscem do zapytania! Z pewnością powoduje różnego rodzaju problemy. Na przykład prymitywne stałe są wstawiane w czasie kompilacji, więc nie można ich zmienić w środowisku wykonawczym, chyba że zmieni się bazowy kod bajtowy. – assylias

+0

@assylias dzięki za doskonałe informacje. ale tak, moje pytanie było mało zgodne z tym, co powiedział morgano. Ale tak, nie mogłem się bardziej zgodzić, że to nie jest najlepsze miejsce, żeby o to zapytać. Zrobię to po raz kolejny. W każdym razie prymitywny inicjał dotyczy zarówno statycznych, jak i niestatycznych zmiennych końcowych. spróbuj uruchomić zmodyfikowaną wartość kodu 5 zamiast computeRandom(). – veritas

Powiązane problemy