Mam zajęcia z private static final
polem, które niestety muszę zmienić w czasie wykonywania.
Za pomocą odbicia otrzymuję ten błąd: java.lang.IllegalAccessException: Can not set static final boolean field
Czy jest jakiś sposób na zmianę wartości?
Field hack = WarpTransform2D.class.getDeclaredField("USE_HACK");
hack.setAccessible(true);
hack.set(null, true);
System.out/in/err
są tak „wyjątkowe”, że Java Memory Model musi o nich szczególnie wspomnieć. Nie są to przykłady, którymi należy się kierować.Odpowiedzi:
Zakładając, że nie przeszkadza
SecurityManager
ci to zrobić, możesz użyćsetAccessible
do obejściaprivate
i zresetowania modyfikatora, aby się go pozbyćfinal
i faktycznie zmodyfikowaćprivate static final
pole.Oto przykład:
Zakładając, że nie
SecurityException
zostanie wyrzucony, powyższy kod zostanie wydrukowany"Everything is true"
.To, co faktycznie tutaj zrobiono, wygląda następująco:
boolean
wartościtrue
ifalse
wmain
są autoboxed do typu odniesieniaBoolean
„stałych”Boolean.TRUE
iBoolean.FALSE
public static final Boolean.FALSE
odniesienia do,Boolean
o którym mowa wBoolean.TRUE
false
jest autoboxowaneBoolean.FALSE
, odnosi się do tego samegoBoolean
jak ten, zwaną przezBoolean.TRUE
"false"
teraz, jest"true"
Powiązane pytania
static final File.separatorChar
w testach jednostkowychInteger
pamięcią podręczną, mutowaniaString
itpOstrzeżenia
Za każdym razem, gdy robisz coś takiego, należy zachować szczególną ostrożność. Może nie działać, ponieważ
SecurityManager
może być obecny, ale nawet jeśli nie działa, w zależności od wzorca użytkowania może działać lub nie.Zobacz też
private static final boolean
, ponieważ jest ona nieodłączna jako stała czasowa kompilacji, a zatem „nowa” wartość może nie być obserwowalnaDodatek: o manipulacji bitowej
Głównie,
wyłącza bit odpowiadający
Modifier.FINAL
odfield.getModifiers()
.&
jest bitowo-i, i~
jest bit-dopełniaczem.Zobacz też
Pamiętaj o stałych wyrażeniach
Nadal nie jesteś w stanie rozwiązać tego ?, popadłeś w depresję tak jak ja? Czy twój kod wygląda tak?
Czytając komentarze do tej odpowiedzi, szczególnie tej autorstwa @Pshemo, przypomniałem sobie, że Wyrażenia stałe są obsługiwane inaczej, więc nie będzie można ich modyfikować. Dlatego musisz zmienić kod, aby wyglądał następująco:
jeśli nie jesteś właścicielem klasy ... Czuję cię!
Aby uzyskać więcej informacji o tym, dlaczego to zachowanie czyta to ?
źródło
getDeclaredField()
zamiastgetField()
do klasy docelowejfinal String myConstant = "x";
i nie powiedzie się: pamiętaj, że kompilator wstawi stałe czasowe kompilacji, więc kiedy napiszesz taki kodSystem.out.println(myConstant);
, zostanie on skompilowany,System.out.println("x");
ponieważ kompilator zna wartość stałej w czasie kompilacji. Aby pozbyć się tego problemu, musisz zainicjować swoje stałe w czasie wykonywania, npfinal String myConstant = new String("x");
. Również w przypadku prymitywów takich jakfinal int myField = 11
użyciefinal int myField = new Integer(11);
lubfinal Integer myField = 11;
Jeśli wartość przypisana do
static final boolean
pola jest znana w czasie kompilacji, jest to stała. Pola pierwotne lubString
typu mogą być stałymi czasu kompilacji. Stała zostanie wstawiona w każdym kodzie, który odwołuje się do pola. Ponieważ pole nie jest w rzeczywistości odczytywane w czasie wykonywania, zmiana tego pola nie przyniesie żadnego efektu.Specyfikacja języka Java mówi:
Oto przykład:
Jeśli dekompilujesz
Checker
, zobaczysz, że zamiast odwoływania sięFlag.FLAG
, kod po prostu wypycha wartość 1 (true
) na stos (instrukcja nr 3).źródło
public static final Boolean FALSE = new Boolean(false)
nie jestpublic static final boolean FALSE = false
Trochę ciekawości ze specyfikacji języka Java, rozdział 17, sekcja 17.5.4 „Pola chronione przed zapisem”:
Źródło: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.4
źródło
Zintegrowałem go również z biblioteką joor
Po prostu użyj
Naprawiłem także problem, z
override
którym wydaje się, że brakuje poprzednich rozwiązań. Jednak używaj tego bardzo ostrożnie, tylko gdy nie ma innego dobrego rozwiązania.źródło
Wraz z odpowiedzią na najwyższym miejscu możesz zastosować nieco najprostsze podejście.
FieldUtils
Klasa Apache commons ma już określoną metodę, która potrafi to zrobić. Proszę spojrzeć naFieldUtils.removeFinalModifier
metodę. Należy określić docelową instancję pola i flagę wymuszania dostępności (jeśli grasz z polami niepublicznymi). Więcej informacji można znaleźć tutaj .źródło
java.lang.UnsupportedOperationException: In java 12+ final cannot be removed.
W przypadku obecności Security Managera można skorzystać z
AccessController.doPrivileged
Biorąc ten sam przykład z zaakceptowanej odpowiedzi powyżej:
W wyrażeniu lambda
AccessController.doPrivileged
można uprościć:źródło
Przyjęta odpowiedź działała dla mnie do momentu wdrożenia na JDK 1.8u91. Potem zdałem sobie sprawę, że nie powiodło się na
field.set(null, newValue);
linii, kiedy odczytałem wartość przez odbicie przed wywołaniemsetFinalStatic
metody.Prawdopodobnie odczyt spowodował w jakiś sposób inną konfigurację wewnętrznych elementów odbicia Java (mianowicie
sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl
w przypadku niepowodzenia zamiastsun.reflect.UnsafeStaticObjectFieldAccessorImpl
sukcesu), ale nie rozwinąłem go dalej.Ponieważ musiałem tymczasowo ustawić nową wartość w oparciu o starą wartość, a później cofnąć starą wartość, nieco zmieniłem podpis, aby udostępnić funkcję obliczeniową na zewnątrz, a także zwrócić starą wartość:
Jednak w ogólnym przypadku nie byłoby to wystarczające.
źródło
Nawet pomimo tego, że
final
pole może być modyfikowane poza statycznym inicjatorem i (przynajmniej JVM HotSpot) doskonale wykona kod bajtowy.Problem polega na tym, że kompilator Java nie pozwala na to, ale można to łatwo obejść za pomocą
objectweb.asm
. Oto doskonale poprawny plik klasy, który przechodzi weryfikację kodu bajtowego i pomyślnie załadował i zainicjował w JVM HotSpot OpenJDK12:W Javie klasa wygląda mniej więcej w następujący sposób:
których nie można skompilować
javac
, ale można je załadować i wykonać za pomocą JVM.JVM HotSpot ma specjalne traktowanie takich klas w tym sensie, że uniemożliwia takim „stałym” udział w ciągłym zwijaniu. Ta kontrola jest wykonywana w fazie przepisywania kodu bajtowego inicjalizacji klasy :
Jedynym ograniczeniem, które sprawdza JVM HotSpot, jest to, że
final
pole nie powinno być modyfikowane poza klasą, w którejfinal
deklarowane jest pole.źródło
Właśnie zobaczyłem to pytanie w jednym z pytań podczas rozmowy kwalifikacyjnej, jeśli to możliwe, aby zmienić zmienną końcową za pomocą refleksji lub w czasie wykonywania. Zainteresowało mnie to, czym się stałem:
Niektóre proste klasy z końcową zmienną String. Tak więc w głównej klasie import java.lang.reflect.Field;
Dane wyjściowe będą następujące:
Zgodnie z dokumentacją https://docs.oracle.com/javase/tutorial/reflect/member/fieldValues.html
źródło
static
ostatniego pola, więc ten kod nie działa.setAccessible(true)
działa tylko w przypadku ustawiania pól instancji końcowej.Jeśli twoje pole jest po prostu prywatne, możesz to zrobić:
i wrzuć / obsłuż NoSuchFieldException
źródło
Cały punkt a
final
pola polega na tym, że nie można go ponownie przypisać po ustawieniu. JVM korzysta z tej gwarancji, aby zachować spójność w różnych miejscach (np. Klasy wewnętrzne odwołujące się do zmiennych zewnętrznych). Więc nie. Możliwość tego złamałaby JVM!Rozwiązaniem jest nie deklarowanie tego
final
w pierwszej kolejności.źródło
final
odgrywa szczególną rolę w wykonywaniu wielowątkowym - zmianafinal
wartości złamałaby również model pamięci Java.final
nie powinno być deklarowanestatic
.