Ich habe eine Klasse mit einem private static final
Feld, das ich leider zur Laufzeit ändern muss.
Mit Reflection erhalte ich folgenden Fehler: java.lang.IllegalAccessException: Can not set static final boolean field
Gibt es eine Möglichkeit, den Wert zu ändern?
Field hack = WarpTransform2D.class.getDeclaredField("USE_HACK");
hack.setAccessible(true);
hack.set(null, true);
System.out/in/err
sind so "speziell", dass das Java-Speichermodell sie besonders erwähnen muss. Sie sind keine Beispiele, die befolgt werden sollten.Antworten:
Keine Angenommen
SecurityManager
wird Sie tun dies zu verhindern, können SiesetAccessible
erhalten umprivate
den Modifikator und Zurücksetzen , um loszuwerdenfinal
, und tatsächlich ein ändernprivate static final
Feld.Hier ist ein Beispiel:
Angenommen, nein
SecurityException
wird geworfen, wird der obige Code gedruckt"Everything is true"
.Was hier tatsächlich gemacht wird, ist wie folgt:
boolean
Wertetrue
undfalse
inmain
werden automatisch an den ReferenztypBoolean
"Konstanten"Boolean.TRUE
und " referenziert "Boolean.FALSE
public static final Boolean.FALSE
, um auf dasBoolean
Bezug zu nehmen, auf das verwiesen wirdBoolean.TRUE
false
Autobox gesendet wirdBoolean.FALSE
, auf dasselbeBoolean
wie das, auf das von verwiesen wirdBoolean.TRUE
"false"
jetzt war ist"true"
Verwandte Fragen
static final File.separatorChar
für Unit-TestsInteger
dem Cache, das Mutieren von aString
usw.Vorsichtsmaßnahmen
Wenn Sie so etwas tun, sollten Sie äußerst vorsichtig sein. Es funktioniert möglicherweise nicht, weil a
SecurityManager
vorhanden sein kann, aber selbst wenn dies nicht der Fall ist, kann es je nach Verwendungsmuster funktionieren oder auch nicht.Siehe auch
private static final boolean
Grundelement funktioniert , da sie als Konstante für die Kompilierungszeit inlinierbar ist und daher der "neue" Wert möglicherweise nicht beobachtbar istAnhang: Zur bitweisen Manipulation
Im Wesentlichen,
schaltet das Bit aus von
Modifier.FINAL
ausfield.getModifiers()
.&
ist das bitweise und und~
ist das bitweise Komplement.Siehe auch
Denken Sie an konstante Ausdrücke
Immer noch nicht in der Lage, dies zu lösen? Sind Sie auf Depressionen gefallen, wie ich es dafür getan habe? Sieht Ihr Code so aus?
Beim Lesen der Kommentare zu dieser Antwort, insbesondere der von @Pshemo, wurde ich daran erinnert, dass konstante Ausdrücke unterschiedlich behandelt werden, sodass es unmöglich ist , sie zu ändern. Daher müssen Sie Ihren Code so ändern, dass er so aussieht:
wenn du nicht der Besitzer der Klasse bist ... ich fühle dich!
Für weitere Details darüber, warum dieses Verhalten dies liest ?
quelle
getDeclaredField()
anstellegetField()
für Zielklasse verwendenfinal String myConstant = "x";
und fehlschlagen: Denken Sie daran, dass Konstanten für die Kompilierungszeit vom Compiler eingefügt werden. Wenn Sie also Code schreibenSystem.out.println(myConstant);
, wird dieser kompiliert,System.out.println("x");
da der Compiler den Wert der Konstanten zur Kompilierungszeit kennt. Um dieses Problem zu beheben, müssen Sie Ihre Konstanten zur Laufzeit wie initialisierenfinal String myConstant = new String("x");
. Auch beifinal int myField = 11
final int myField = new Integer(11);
final Integer myField = 11;
Wenn der einem
static final boolean
Feld zugewiesene Wert zur Kompilierungszeit bekannt ist, ist er eine Konstante. Felder des Grundelements oder desString
Typs können Konstanten zur Kompilierungszeit sein. In jedem Code, der auf das Feld verweist, wird eine Konstante eingefügt. Da das Feld zur Laufzeit nicht gelesen wird, hat eine Änderung keine Auswirkung.Die Java-Sprachspezifikation sagt dies:
Hier ist ein Beispiel:
Wenn Sie dekompilieren
Checker
, werden Sie feststellen , dassFlag.FLAG
der Code anstelle einer Referenzierung einfach den Wert 1 (true
) auf den Stapel schiebt (Anweisung 3).quelle
public static final Boolean FALSE = new Boolean(false)
nichtpublic static final boolean FALSE = false
Eine kleine Kuriosität aus der Java-Sprachspezifikation, Kapitel 17, Abschnitt 17.5.4 "Schreibgeschützte Felder":
Quelle: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.4
quelle
Ich habe es auch in die Joor-Bibliothek integriert
Benutz einfach
Außerdem habe ich ein Problem behoben, bei
override
dem die vorherigen Lösungen zu fehlen scheinen. Gehen Sie jedoch nur sehr vorsichtig damit um, wenn es keine andere gute Lösung gibt.quelle
Zusammen mit der am besten bewerteten Antwort können Sie einen etwas einfachsten Ansatz verwenden. Die Apache Commons-
FieldUtils
Klasse verfügt bereits über eine bestimmte Methode, mit der das erledigt werden kann. Bitte werfen Sie einen Blick auf dieFieldUtils.removeFinalModifier
Methode. Sie sollten die Zielfeldinstanz und das Barrierefreiheits-Forcing-Flag angeben (wenn Sie mit nicht öffentlichen Feldern spielen). Weitere Infos finden Sie hier .quelle
java.lang.UnsupportedOperationException: In java 12+ final cannot be removed.
Bei Vorhandensein eines Sicherheitsmanagers kann davon Gebrauch gemacht werden
AccessController.doPrivileged
Nehmen Sie das gleiche Beispiel aus der oben akzeptierten Antwort:
Im Lambda-Ausdruck
AccessController.doPrivileged
kann vereinfacht werden zu:quelle
Die akzeptierte Antwort funktionierte für mich bis zur Bereitstellung auf JDK 1.8u91. Dann wurde mir klar, dass es in der
field.set(null, newValue);
Zeile fehlgeschlagen war , als ich den Wert vor dem Aufruf von durch Reflektion gelesen hattesetFinalStatic
Methode .Wahrscheinlich hat das Lesen irgendwie zu einem anderen Setup von Java Reflection-Interna geführt (nämlich
sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl
im Fehlerfall stattsun.reflect.UnsafeStaticObjectFieldAccessorImpl
im Erfolgsfall), aber ich habe es nicht weiter ausgeführt.Da ich vorübergehend einen neuen Wert basierend auf dem alten Wert und später einen alten Wert zurücksetzen musste, habe ich die Signatur ein wenig geändert, um die Berechnungsfunktion extern bereitzustellen und auch den alten Wert zurückzugeben:
Für den allgemeinen Fall wäre dies jedoch nicht ausreichend.
quelle
Auch trotz des Seins
final
ein Feld außerhalb des statischen Initialisierers geändert werden kann und (zumindest JVM HotSpot) den Bytecode einwandfrei ausführt.Das Problem ist, dass der Java-Compiler dies nicht zulässt, dies kann jedoch leicht umgangen werden
objectweb.asm
. Hier ist eine perfekt gültige Klassendatei, die die Bytecode-Überprüfung besteht und erfolgreich unter JVM HotSpot OpenJDK12 geladen und initialisiert wurde:In Java sieht die Klasse ungefähr so aus:
mit denen nicht kompiliert werden kann
javac
, aber von JVM geladen und ausgeführt werden können.JVM HotSpot behandelt solche Klassen speziell in dem Sinne, dass solche "Konstanten" nicht an einer konstanten Faltung teilnehmen können. Diese Überprüfung erfolgt in der Phase des Umschreibens des Bytecodes der Klasseninitialisierung :
Die einzige Einschränkung, die JVM HotSpot überprüft, besteht darin, dass das
final
Feld nicht außerhalb der Klasse geändert werden darf, in der dasfinal
Feld deklariert ist.quelle
Ich habe diese Frage gerade in einer der Interviewfragen gesehen, wenn möglich, um die endgültige Variable mit Reflexion oder zur Laufzeit zu ändern. Wurde wirklich interessiert, so dass, was ich wurde mit:
Eine einfache Klasse mit endgültiger String-Variable. Importieren Sie also in der Hauptklasse java.lang.reflect.Field;
Die Ausgabe wird wie folgt sein:
Laut Dokumentation https://docs.oracle.com/javase/tutorial/reflect/member/fieldValues.html
quelle
static
letzten Feld, daher funktioniert dieser Code nicht.setAccessible(true)
funktioniert nur zum Festlegen von Endinstanzfeldern.Wenn Ihr Feld einfach privat ist, können Sie dies tun:
und werfen / behandeln NoSuchFieldException
quelle
Der springende Punkt eines
final
Feldes ist, dass es nach dem Setzen nicht mehr neu zugewiesen werden kann. Die JVM verwendet diese Garantie, um die Konsistenz an verschiedenen Stellen aufrechtzuerhalten (z. B. innere Klassen, die auf äußere Variablen verweisen). Also nein. In der Lage zu sein, würde die JVM brechen!Die Lösung besteht nicht darin, es überhaupt zu deklarieren
final
.quelle
final
spielt die Multithread-Ausführung eine besondere Rolle - das Ändern vonfinal
Werten würde auch das Java-Speichermodell beschädigen.final
sollte nicht deklariert werdenstatic
.