Modificiranje privatnog statičkog posljednjeg polja pomoću Java refleksije

Imam klasu s private static final poljem, koje, nažalost, moram promijeniti tijekom izvođenja.

Koristeći razmišljanje, dobivam ovu pogrešku: java.>

Je li moguće promijeniti vrijednost?

 Field hack = WarpTransform2D.class.getDeclaredField("USE_HACK"); hack.setAccessible(true); hack.set(null, true); 
404
21 июля '10 в 19:35 2010-07-21 19:35 fixitagain je postavljen 21. srpnja '10 u 19:35 2010-07-21 19:35
@ 10 odgovora

Uz pretpostavku da vam SecurityManager ne dopušta da to učinite, možete koristiti setAccessible za zaobilaženje private i reset modifikatora kako biste se riješili final i zapravo promijenili private static final polje.

Evo primjera:

 import java.> 

Uz pretpostavku da nije izbačena iznimka SecurityException , gornji kod ispisuje "Everything is true" .

Što se zapravo ovdje radi:

  • Početne boolean vrijednosti true i false u main Boolean.TRUE za referentne vrste Boolean "konstante" Boolean.TRUE i Boolean.FALSE
  • Refleksija se koristi za promjenu public static final Boolean.FALSE za referencu na Boolean referencu Boolean.TRUE
  • Kao rezultat toga, nakon toga, kad god je false Boolean.FALSE na Boolean.FALSE , ona se odnosi na isti Boolean kao Boolean.TRUE
  • Sada je sve to "false" "true"

Povezana pitanja


Mjere opreza

Ekstremnu pažnju treba poduzeti kad god učinite nešto takvo. Možda neće raditi jer je možda prisutan SecurityManager , ali čak i ako to ne učini, ovisno o obrascu korištenja, može ili ne mora raditi.

JLS 17.5.3 Naknadna modifikacija konačnih polja

U nekim slučajevima, kao što je deserijalizacija, sustav će morati promijeniti final polja objekta nakon izgradnje. final polja mogu se mijenjati refleksijom i drugim sredstvima, ovisno o provedbi. Jedini uzorak u kojem ima razumnu semantiku je onaj u kojem je objekt izgrađen, a zatim se ažuriraju final polja objekta. Objekt ne bi trebao biti vidljiv drugim niti, a final polja se ne bi trebala čitati dok se ne dovrše sva ažuriranja final polja objekta. final zamrzavanje polja događa se i na kraju konstruktora, u kojem je zadano final polje, i odmah nakon svake izmjene final polja kroz refleksiju ili drugi poseban mehanizam.

Čak i tada postoje brojne komplikacije. Ako se final polje inicijalizira konstantom vremena kompajliranja u deklaraciji polja, promjene u final polju možda se neće promatrati, budući da se korištenje ovog final polja zamjenjuje tijekom kompilacije s konstantom vremena kompajliranja.

Drugi problem je što specifikacija omogućuje agresivnu optimizaciju final polja. Unutar potoka dopušteno je promjeniti redoslijed čitanja final polja s onim promjenama konačnog polja koje nemaju mjesto u konstruktoru.

Vidi također

  • JLS 15.28 Stalni izraz
    • Malo je vjerojatno da ova metoda radi s primitivnim private static final boolean , budući da je ugrađena kao konstanta vremena prevođenja i, stoga, "nova" vrijednost možda neće biti promatrana.

border=0

Dodatak: bitovska manipulacija

U osnovi,

 field.getModifiers()  ~Modifier.FINAL 

onemogućuje bit koji odgovara Modifier.FINAL iz field.getModifiers() . > je bitovski i, i ~ je bitovska dopuna.

Vidi također


border=0

Zapamtite uporne izraze

Još uvijek ne mogu riješiti problem ?, pao u depresiju, kako sam to učinio za nju? Da li vaš kôd izgleda ovako?

 public class A { private final String myVar = "Some Value"; } 

Čitajući komentare na ovaj odgovor, posebno onaj koji je bio @Pshemo, podsjetio me je da se konstantni izrazi obrađuju drugačije, tako da ga neće biti moguće promijeniti. Stoga morate promijeniti kôd tako da izgleda ovako:

 public class A { private final String myVar; private A() { myVar = "Some Value"; } } 

ako niste vlasnik razreda ... osjećam vas!

Za više informacija o tome zašto se ovo ponašanje čita ?

765
21 июля '10 в 19:46 2010-07-21 19:46 Odgovor se daje poligenskim mazivima 21. srpnja '10. u 19:46 2010-07-21 19:46

Ako je vrijednost dodijeljena static final boolean polju poznata u vrijeme prevođenja, ona je konstantna. Polja primitivnog ili String mogu biti konstanti vremena prevođenja. Konstanta će biti ugrađena u bilo koji kod koji upućuje na polje. Budući da se polje u vrijeme izvođenja zapravo ne može čitati, njegovo mijenjanje neće imati učinka.

Specifikacija jezika Java kaže sljedeće:

Ako je polje konstantna varijabla (§4.12.4), brisanje konačne ključne riječi ili promjena njezine vrijednosti neće prekinuti kompatibilnost s već postojećim binarnim datotekama, uzrokujući da se ne pokreću, ali neće vidjeti nikakvu novu vrijednost koju će upotrijebiti ako se ne ponovno kompajliraju. To je istina čak i ako sama uporaba nije izraz konstante vremena kompajliranja (§15.28)

Evo primjera:

border=0
 class Flag { static final boolean FLAG = true; } class Checker { public static void main(String... argv) { System.out.println(Flag.FLAG); } } 

Ako dekompilirate Checker , vidjet ćete da umjesto da se poziva na Flag.FLAG kod, on jednostavno gura vrijednost 1 ( true ) Flag.FLAG stog (instrukcija # 3).

 0: getstatic #2; //Field java/> 
47
21 июля '10 в 19:57 2010-07-21 19:57 odgovor je dao erickson 21. srpnja '10 u 19:57 2010-07-21 19:57

Malo znatiželje o specifikaciji jezika Java, poglavlje 17, odjeljak 17.5.4 "Polja zaštićena od pisanja":

Obično polje koje je konačno i statično ne može se promijeniti. Međutim, System.in, System.out i System.err su statična završna polja koja bi, zbog zastarjelih razloga, trebala biti dopuštena za modificiranje metodama System.setIn, System.setOut i System.setErr. Ta polja označavamo kao zaštićena od pisanja kako bismo ih razlikovali od uobičajenih konačnih polja.

Izvor: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.4

12
21 нояб. Odgovor Stephana Markwaldera 21. studenog 2012-11-21 22:21 '12 u 22:21 2012-11-21 22:21

Također sam ga integrirao s joor knjižnicom.

Samo upotrijebite

  Reflect.on(yourObject).set("finalFieldName", finalFieldValue); 

Također sam riješio problem s override koje je, očito, propustilo prethodne odluke. Međutim, koristite ga vrlo pažljivo, samo kada ne postoji drugo dobro rješenje.

5
07 июля '15 в 15:47 2015-07-07 15:47 odgovor je dat iirekm 7. srpnja '15 u 15:47 2015-07-07 15:47

Uz najvišu ocjenu, možete koristiti i jednostavan pristup. Apocal class Commons FieldUtils već ima određenu metodu koju materijal može učiniti. Molimo pogledajte metodu FieldUtils.removeFinalModifier . Morate navesti zastavicu odredišta ciljnog polja i dostupnost (ako igrate s nejavnim poljima). Više informacija možete pronaći ovdje .

4
07 июня '17 в 13:32 2017-06-07 13:32 odgovor je dan nndru 07. lipnja 2006. u 13:32 2017-06-07 13:32

Ako imate upravitelja sigurnosti, možete koristiti AccessController.doPrivileged

Primjenom istog primjera iz gore prihvaćenog odgovora:

 import java.> 

U lambda izrazu AccessController.doPrivileged može se pojednostavniti:

 AccessController.doPrivileged((PrivilegedAction) () -> { modifiersField.setAccessible(true); return null; }); 
4
25 марта '17 в 9:06 2017-03-25 09:06 odgovor je dat VanagaS 25. ožujka '17 u 9:06 2017-03-25 09:06

Prihvaćeni odgovor radio je za mene prije implementacije na JDK 1.8u91. Tada sam shvatio da to nije moguće u nizu field.set(null, newValue); kada sam pročitao vrijednost kroz refleksiju prije pozivanja metode setFinalStatic .

Vjerojatno, čitanje izazvalo nekako drugačiju konfiguraciju unutarnjih elemenata Java refleksije (naime sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl u slučaju neuspjeha umjesto sun.reflect.UnsafeStaticObjectFieldAccessorImpl u slučaju uspjeha), ali nisam ga odredio dalje.

Budući da sam morao privremeno postaviti novu vrijednost na temelju stare vrijednosti i kasnije postaviti staru vrijednost, malo sam promijenio potpis kako bih osigurao vanjsku funkciju izračuna, i vratio staru vrijednost:

 public static <T> T assignFinalField(Object object, Class<?> clazz, String fieldName, UnaryOperator<T> newValueFunction) { Field f = null, ff = null; try { f = clazz.getDeclaredField(fieldName); final int oldM = f.getModifiers(); final int newM = oldM  ~Modifier.FINAL; ff = Field.class.getDeclaredField("modifiers"); ff.setAccessible(true); ff.setInt(f,newM); f.setAccessible(true); T result = (T)f.get(object); T newValue = newValueFunction.apply(result); f.set(object,newValue); ff.setInt(f,oldM); return result; } ... 

Međutim, za opći slučaj to ne bi bilo dovoljno.

2
10 мая '17 в 15:45 2017-05-10 15:45 Odgovor je dao Tomáš Záluský 10. svibnja 2006. u 15:45 2017-05-10 15:45

Ako je vaše polje samo osobno, možete to učiniti:

 MyClass myClass= new MyClass(); Field aField= myClass.getClass().getDeclaredField("someField"); aField.setAccessible(true); aField.set(myClass, "newValueForAString"); 

i bacanje / proces NoSuchFieldException

0
23 янв. Odgovor daje Philip Rego 23. siječnja 2019-01-23 19:34 '19 u 19:34 2019-01-23 19:34

Upravo sam vidio ovo pitanje na jednom od pitanja za intervju, ako je moguće promijeniti konačnu varijablu s refleksijom ili tijekom izvršenja. Jako sam zainteresiran, pa sam postao s:

   public class SomeClass { private final String str; SomeClass(){ this.str = "This is the string that never changes!"; } public String getStr() { return str; } @Override public String toString() { return "Class name: " + getClass() + " Value: " + getStr(); } } 

Jednostavna klasa s konačnom varijablom String. Dakle, u osnovi klasa uvoza java.>

  public class Main { public static void main(String[] args) throws Exception{ SomeClass someClass = new SomeClass(); System.out.println(someClass); Field field = someClass.getClass().getDeclaredField("str"); field.setAccessible(true); field.set(someClass, "There you are"); System.out.println(someClass); } } 

Izlaz će biti sljedeći:

 Class name: class SomeClass Value: This is the string that never changes! Class name: class SomeClass Value: There you are Process finished with exit code 0 

Prema dokumentaciji https://docs.oracle.com/javase/tutorial/reflect/member/fieldValues.html

-1
04 марта '16 в 12:21 2016-03-04 12:21 odgovor je dao hasskell 04 ožujak '16 u 12:21 2016-03-04 12:21

Čitava točka final polja je da se ne može preraspodijeliti nakon instalacije. JVM koristi ovo jamstvo kako bi osigurao dosljednost na različitim mjestima (na primjer, unutarnje klase koje se odnose na vanjske varijable). Dakle, ne. Biti u stanju to učiniti razbiti će JVM!

Rješenje nije prvo proglasiti ga final .

-5
21 июля '10 в 19:38 2010-07-21 19:38 odgovor je dao thecoop 21. srpnja '10 u 19:38 2010-07-21 19:38