เปลี่ยนฟิลด์สแตติกสุดท้ายส่วนตัวโดยใช้การสะท้อน Java


479

ฉันมีคลาสที่มีprivate static finalฟิลด์ที่โชคไม่ดีที่ฉันต้องเปลี่ยนมันในเวลาทำงาน

ใช้การสะท้อนฉันได้รับข้อผิดพลาดนี้: java.lang.IllegalAccessException: Can not set static final boolean field

มีวิธีการเปลี่ยนแปลงค่าหรือไม่?

Field hack = WarpTransform2D.class.getDeclaredField("USE_HACK");
hack.setAccessible(true);
hack.set(null, true);

4
ช่างเป็นความคิดที่เลว ฉันพยายามรับซอร์สและคอมไพล์ใหม่ (หรือแม้แต่คอมไพล์ / คอมไพล์ซ้ำ) แทน
Bill K

System.out เป็นฟิลด์สุดท้ายสแตติกสาธารณะ แต่สามารถเปลี่ยนแปลงได้เช่นกัน
เถียงไม่ได้

19
@ โต้แย้งSystem.out/in/errได้จึงเป็น "พิเศษ" ที่ Java Memory Model ต้องกล่าวถึงเป็นพิเศษ ไม่ใช่ตัวอย่างที่ควรติดตาม
Tom Hawtin - tackline

8
จุดของฉันคือการหาแฮ็คในระหว่างที่จะมีแอปของฉันทำงานจนกว่า lib รับผิดชอบทำการเปลี่ยนแปลงในรุ่นถัดไปดังนั้นฉันไม่จำเป็นต้องแฮ็คอีกต่อไป ...
fixitagain

1
@Bill K จากสิบปีที่ผ่านมา: มันจะยอดเยี่ยมในการคอมไพล์ใหม่ แต่มันอยู่ในระบบที่ปรับใช้และฉันเพียงแค่ต้องแก้ไขมันจนกว่าเราจะสามารถอัปเดตแอปที่ใช้งานได้!
Bill K

คำตอบ:


888

สมมติว่าไม่มีการSecurityManagerป้องกันไม่ให้คุณทำเช่นนี้คุณสามารถใช้setAccessibleเพื่อหลีกเลี่ยงprivateและรีเซ็ตตัวแก้ไขเพื่อกำจัดfinalและแก้ไขprivate static finalฟิลด์จริง

นี่คือตัวอย่าง:

import java.lang.reflect.*;

public class EverythingIsTrue {
   static void setFinalStatic(Field field, Object newValue) throws Exception {
      field.setAccessible(true);

      Field modifiersField = Field.class.getDeclaredField("modifiers");
      modifiersField.setAccessible(true);
      modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

      field.set(null, newValue);
   }
   public static void main(String args[]) throws Exception {      
      setFinalStatic(Boolean.class.getField("FALSE"), true);

      System.out.format("Everything is %s", false); // "Everything is true"
   }
}

สมมติว่าไม่มีการSecurityExceptionโยนรหัสด้านบนจะพิมพ์"Everything is true"ออกมา

สิ่งที่ทำจริงที่นี่มีดังนี้:

  • booleanค่าดั้งเดิมtrueและค่าfalseในmainจะถูกล็อกอัตโนมัติไปยังประเภทอ้างอิงBoolean"ค่าคงที่" Boolean.TRUEและBoolean.FALSE
  • การสะท้อนจะใช้ในการเปลี่ยนการpublic static final Boolean.FALSEอ้างอิงถึงการBooleanอ้างอิงโดยBoolean.TRUE
  • ด้วยเหตุนี้เมื่อใดก็ตามที่ a falseถูกออโต้Boolean.FALSEมันก็จะหมายถึงเช่นเดียวBooleanกับที่อ้างอิงโดยBoolean.TRUE
  • ทุกอย่างที่เป็นอยู่ใน"false"ปัจจุบัน"true"

คำถามที่เกี่ยวข้อง


คำเตือน

ควรระมัดระวังทุกครั้งที่คุณทำสิ่งนี้ อาจไม่ทำงานเนื่องจากSecurityManagerอาจมีอยู่ แต่แม้ว่าจะไม่ได้ขึ้นอยู่กับรูปแบบการใช้งาน แต่อาจใช้งานได้หรือไม่ก็ได้

JLS 17.5.3 การปรับเปลี่ยนเขตข้อมูลสุดท้ายในภายหลัง

ในบางกรณีเช่นการดีซีเรียลไลเซชันระบบจะต้องเปลี่ยนfinalฟิลด์ของวัตถุหลังการก่อสร้าง finalฟิลด์สามารถเปลี่ยนแปลงได้ผ่านการสะท้อนและวิธีการขึ้นอยู่กับการใช้งานอื่น ๆ รูปแบบเดียวที่สิ่งนี้มีความหมายที่สมเหตุสมผลคือรูปแบบหนึ่งที่วัตถุถูกสร้างขึ้นจากนั้นจึงfinalอัปเดตฟิลด์ของวัตถุ วัตถุไม่ควรปรากฏให้เห็นในเธรดอื่นและไม่ควรfinalอ่านฟิลด์จนกว่าการอัพเดตทั้งหมดของfinalฟิลด์ของวัตถุจะเสร็จสมบูรณ์ การค้างของfinalเขตข้อมูลเกิดขึ้นทั้งในตอนท้ายของตัวสร้างที่finalตั้งค่าเขตข้อมูลและทันทีหลังจากการแก้ไขแต่ละfinalเขตข้อมูลผ่านการสะท้อนกลับหรือกลไกพิเศษอื่น ๆ

ถึงอย่างนั้นก็มีจำนวนของโรคแทรกซ้อน ถ้าfinalเขตข้อมูลถูกเตรียมใช้งานเป็นค่าคงที่เวลาคอมไพล์ในการประกาศเขตข้อมูลการเปลี่ยนแปลงในfinalเขตข้อมูลอาจไม่สามารถสังเกตเห็นได้เนื่องจากการใช้งานของfinalเขตข้อมูลนั้นจะถูกแทนที่ในเวลารวบรวมด้วยค่าคงที่เวลารวบรวม

ปัญหาอีกประการหนึ่งก็คือสเปคช่วยให้การเพิ่มประสิทธิภาพเชิงรุกของfinalเขตข้อมูล ภายในเธรดอนุญาตให้เรียงลำดับการอ่านfinalฟิลด์ใหม่ด้วยการแก้ไขฟิลด์สุดท้ายที่ไม่ได้เกิดขึ้นในตัวสร้าง

ดูสิ่งนี้ด้วย

  • JLS 15.28 การแสดงออกอย่างต่อเนื่อง
    • มันไม่น่าเป็นไปได้ที่เทคนิคนี้จะทำงานร่วมกับแบบดั้งเดิมprivate static final booleanเพราะมันไม่สามารถระบุได้ว่าเป็นค่าคงที่เวลารวบรวมและทำให้ค่า "ใหม่" อาจไม่สามารถสังเกตได้

ภาคผนวก: ในการจัดการระดับบิต

โดยพื้นฐานแล้ว

field.getModifiers() & ~Modifier.FINAL

จะปิดบิตที่สอดคล้องกับการจากModifier.FINAL คือ bitwise-and และBitwise-complementfield.getModifiers()&~

ดูสิ่งนี้ด้วย


จำนิพจน์คงที่

ยังไม่สามารถแก้ปัญหานี้ได้หรือไม่? รหัสของคุณมีลักษณะเช่นนี้?

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

การอ่านความคิดเห็นเกี่ยวกับคำตอบนี้โดยเฉพาะ @Pshemo ทำให้ฉันนึกถึงว่าConstant Expressionsมีการจัดการที่แตกต่างกันดังนั้นจึงเป็นไปไม่ได้ที่จะแก้ไข ดังนั้นคุณจะต้องเปลี่ยนรหัสของคุณเป็นดังนี้:

public class A {
    private final String myVar;

    private A() {
        myVar = "Some Value";
    }
}

ถ้าคุณไม่ใช่เจ้าของคลาส ... ฉันรู้สึกว่าคุณ!

สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับสาเหตุพฤติกรรมนี้อ่านหนังสือเล่มนี้ ?


41
@thecoop, @HalfBrian: ไม่ต้องสงสัยเลยว่านี่เป็นสิ่งที่เลวร้ายมาก แต่ตัวอย่างนี้ได้รับเลือกจากการออกแบบ คำตอบของฉันแสดงให้เห็นว่าในบางสถานการณ์เป็นไปได้เพียงใด ตัวอย่างที่น่าขยะแขยงที่สุดที่ฉันคิดได้คือเลือกอย่างไตร่ตรองด้วยความหวังว่าบางทีผู้คนจะถูกรังเกียจโดยทันทีแทนที่จะตกหลุมรักกับเทคนิค
polygenelubricants

59
โย่ ฉันได้ยินว่าคุณชอบภาพสะท้อนดังนั้นฉันจึงสะท้อนบนสนามเพื่อให้คุณสามารถสะท้อนในขณะที่คุณสะท้อนภาพได้
Matthew Flaschen

11
โปรดทราบว่า Boolean.FALSE ไม่เป็นส่วนตัวคุณสามารถใช้งานกับสมาชิก "private final static" ได้หรือไม่
ออกแบบ

15
@mgaert มันไม่ แต่คุณต้องใช้getDeclaredField()แทนgetField()สำหรับการเรียนเป้าหมาย
EIS

11
+1 สำหรับผู้ที่จะพยายามเปลี่ยนสิ่งที่ชอบfinal String myConstant = "x";และจะล้มเหลวโปรดจำไว้ว่าค่าคงที่เวลาคอมไพล์จะถูกคอมไพล์โดยอินไลน์ดังนั้นเมื่อคุณจะเขียนโค้ดเช่นSystem.out.println(myConstant);นั้นจะถูกคอมไพล์System.out.println("x");เพราะคอมไพเลอร์รู้ค่าของค่าคงที่ ณ เวลารวบรวม final String myConstant = new String("x");เพื่อกำจัดปัญหานี้คุณจะต้องเริ่มต้นค่าคงที่ของคุณที่รันไทม์เช่น นอกจากนี้ในกรณีของดั้งเดิมเช่นfinal int myField = 11ใช้final int myField = new Integer(11);หรือfinal Integer myField = 11;
Pshemo

58

หากค่าที่กำหนดให้กับstatic final booleanเขตข้อมูลเป็นที่รู้จักกันในเวลารวบรวมมันเป็นค่าคงที่ สาขาดั้งเดิมหรือ Stringประเภทสามารถรวบรวมค่าคงที่เวลา ค่าคงที่จะถูกแทรกในโค้ดใด ๆ ที่อ้างอิงถึงฟิลด์ เนื่องจากฟิลด์ไม่ได้อ่านจริงที่รันไทม์การเปลี่ยนมันจะไม่มีผลใด ๆ

ข้อกำหนดภาษา Javaบอกว่านี่:

หากฟิลด์เป็นตัวแปรคงที่ (§4.12.4) การลบคำหลักสุดท้ายหรือการเปลี่ยนค่าจะไม่ทำลายความเข้ากันได้กับไบนารีที่มีอยู่ก่อนโดยทำให้พวกเขาไม่สามารถทำงานได้ แต่พวกเขาจะไม่เห็นคุณค่าใหม่สำหรับการใช้งาน ของสนามเว้นแต่พวกเขาจะ recompiled สิ่งนี้เป็นจริงแม้ว่าการใช้งานจะไม่ใช่การแสดงออกคงที่ในเวลาคอมไพล์ (§15.28)

นี่คือตัวอย่าง:

class Flag {
  static final boolean FLAG = true;
}

class Checker {
  public static void main(String... argv) {
    System.out.println(Flag.FLAG);
  }
}

หากคุณถอดรหัสCheckerคุณจะเห็นว่าแทนที่จะอ้างอิงFlag.FLAGรหัสก็แค่กดค่า 1 ( true) ลงในสแต็ก (คำแนะนำ # 3)

0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3:   iconst_1
4:   invokevirtual   #3; //Method java/io/PrintStream.println:(Z)V
7:   return

นั่นเป็นความคิดแรกของฉัน แต่จากนั้นฉันจำได้ว่า Java คอมไพล์ตอนรันไทม์ถ้าคุณต้องรีเซ็ตบิตมันก็แค่คอมไพล์ใหม่เป็นตัวแปรแทนที่จะเป็นค่าคงที่
Bill K

4
@Bill K - ไม่ไม่ได้หมายถึงการรวบรวม JIT ไฟล์คลาสที่ขึ้นต่อกันจะมีค่าอินไลน์และไม่มีการอ้างอิงถึงคลาสอิสระ เป็นการทดลองที่ค่อนข้างง่ายในการทดสอบ ฉันจะเพิ่มตัวอย่าง
erickson

1
@ polygenelubricants ตอบได้อย่างไรที่เขานิยามคำว่า Boolean.false - แต่คุณถูกต้องฉันเห็นพฤติกรรมนี้เมื่อสิ่งต่าง ๆ ไม่คอมไพล์ซ้ำได้อย่างถูกต้อง
Bill K

26
@Bill K - ในคำตอบของ polygenlubricants ฟิลด์ไม่ได้เป็นค่าคงที่เวลารวบรวม มันpublic static final Boolean FALSE = new Boolean(false)ไม่ได้public static final boolean FALSE = false
เอริก

17

ความอยากรู้เล็กน้อยจากข้อกำหนดภาษาจาวาบทที่ 17 ส่วนที่ 17.5.4 "เขตข้อมูลที่ป้องกันการเขียน":

โดยปกติแล้วฟิลด์ที่เป็นที่สุดและคงที่อาจไม่สามารถแก้ไขได้ อย่างไรก็ตาม System.in, System.out และ System.err เป็นฟิลด์สุดท้ายแบบสแตติกที่ต้องได้รับอนุญาตให้เปลี่ยนด้วยวิธี System.setIn, System.setOut และ System.setErr ด้วยเหตุผลแบบเดิม เราอ้างถึงฟิลด์เหล่านี้ว่าป้องกันการเขียนเพื่อแยกความแตกต่างจากฟิลด์สุดท้ายปกติ

ที่มา: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.4


9

ฉันยังรวมเข้ากับห้องสมุด joor

เพียงแค่ใช้

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

ฉันยังแก้ไขปัญหาoverrideที่การแก้ไขก่อนหน้านี้ดูเหมือนจะพลาด อย่างไรก็ตามใช้สิ่งนี้อย่างระมัดระวังเฉพาะเมื่อไม่มีวิธีแก้ปัญหาที่ดีอื่น ๆ


เมื่อฉันลอง (JDK12) ฉันได้รับการยกเว้น: "ไม่สามารถตั้งค่าฟิลด์ ___ สุดท้าย" ได้
Aaron Iba

@AaronIba ไม่อนุญาตให้ใช้ใน Java 12+ อีกต่อไป
NateS

7

พร้อมกับคำตอบที่ติดอันดับคุณอาจใช้วิธีที่ง่ายที่สุด Apache Commons FieldUtilsclass มีวิธีการเฉพาะที่สามารถทำสิ่งต่างๆได้ โปรดดูFieldUtils.removeFinalModifierวิธีการ คุณควรระบุอินสแตนซ์ของฟิลด์เป้าหมายและบังคับให้มีการเข้าถึงการตั้งค่าสถานะ (ถ้าคุณเล่นกับฟิลด์ที่ไม่ใช่แบบสาธารณะ) ข้อมูลอื่น ๆ ที่คุณสามารถหาได้ที่นี่


นี่เป็นวิธีที่ง่ายกว่าคำตอบที่ได้รับการยอมรับในปัจจุบัน
เบอร์นี

4
ใช่ไหม? การทำสำเนาวิธีหนึ่งดูเหมือนเป็นวิธีที่ง่ายกว่าการนำเข้าทั้งไลบรารี (ซึ่งกำลังทำสิ่งเดียวกับวิธีที่คุณกำลังคัดลอก)
eski

1
ไม่ทำงานใน Java 12+:java.lang.UnsupportedOperationException: In java 12+ final cannot be removed.
MrPowerGamerBR

6

ในกรณีที่มี Security Manager สามารถใช้งานได้ AccessController.doPrivileged

รับตัวอย่างเดียวกันจากคำตอบที่ยอมรับด้านบน:

import java.lang.reflect.*;

public class EverythingIsTrue {
    static void setFinalStatic(Field field, Object newValue) throws Exception {
        field.setAccessible(true);
        Field modifiersField = Field.class.getDeclaredField("modifiers");

        // wrapping setAccessible 
        AccessController.doPrivileged(new PrivilegedAction() {
            @Override
            public Object run() {
                modifiersField.setAccessible(true);
                return null;
            }
        });

        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        field.set(null, newValue);
    }

    public static void main(String args[]) throws Exception {      
      setFinalStatic(Boolean.class.getField("FALSE"), true);
      System.out.format("Everything is %s", false); // "Everything is true"
    }
}

ในการแสดงออกแลมบ์ดาAccessController.doPrivilegedสามารถทำให้ง่ายขึ้นเพื่อ:

AccessController.doPrivileged((PrivilegedAction) () -> {
    modifiersField.setAccessible(true);
    return null;
});

1
สิ่งนี้ดูเหมือนจะไม่ทำงานกับ java 12+
dan1st

ใช่ @ dan1st คุณพูดถูก! โปรดตรวจสอบวิธีการแก้ไขนี้: stackoverflow.com/a/56043252/2546381
VanagaS

2

คำตอบที่ยอมรับใช้งานได้สำหรับฉันจนกระทั่งใช้งานกับ JDK 1.8u91 จากนั้นฉันก็รู้ว่ามันล้มเหลวที่field.set(null, newValue);บรรทัดเมื่อฉันอ่านค่าผ่านการสะท้อนก่อนเรียกsetFinalStaticวิธี

อาจเป็นเพราะการอ่านทำให้การตั้งค่า Java สะท้อนภายในแตกต่างกันอย่างใดอย่างหนึ่ง (คือsun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImplในกรณีที่ล้มเหลวแทนที่จะเป็นsun.reflect.UnsafeStaticObjectFieldAccessorImplกรณีที่ประสบความสำเร็จ) แต่ฉันไม่ได้ทำอย่างละเอียดเพิ่มเติม

เนื่องจากฉันต้องการตั้งค่าใหม่ชั่วคราวตามค่าเดิมและตั้งค่าเก่ากลับมาในภายหลังฉันเปลี่ยนลายเซ็นนิด ๆ หน่อย ๆ เพื่อให้ฟังก์ชันการคำนวณภายนอกและส่งคืนค่าเก่า:

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;
    } ...

อย่างไรก็ตามสำหรับกรณีทั่วไปสิ่งนี้จะไม่เพียงพอ


2

แม้จะfinalเป็นเขตข้อมูลก็สามารถแก้ไขได้นอก static initializer และ (อย่างน้อย JVM HotSpot) จะดำเนินการ bytecode ได้อย่างสมบูรณ์แบบ

ปัญหาคือว่า Java คอมไพเลอร์ไม่อนุญาตให้นี้ objectweb.asmแต่สามารถข้ามได้อย่างง่ายดายโดยใช้ นี่คือไฟล์คลาสที่ถูกต้องสมบูรณ์แบบที่ผ่านการตรวจสอบ bytecode และโหลดสำเร็จและเริ่มต้นภายใต้ JVM HotSpot OpenJDK12:

ClassWriter cw = new ClassWriter(0);
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "Cl", null, "java/lang/Object", null);
{
    FieldVisitor fv = cw.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL, "fld", "I", null, null);
    fv.visitEnd();
}
{
    // public void setFinalField1() { //... }
    MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "setFinalField1", "()V", null, null);
    mv.visitMaxs(2, 1);
    mv.visitInsn(Opcodes.ICONST_5);
    mv.visitFieldInsn(Opcodes.PUTSTATIC, "Cl", "fld", "I");
    mv.visitInsn(Opcodes.RETURN);
    mv.visitEnd();
}
{
    // public void setFinalField2() { //... }
    MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "setFinalField2", "()V", null, null);
    mv.visitMaxs(2, 1);
    mv.visitInsn(Opcodes.ICONST_2);
    mv.visitFieldInsn(Opcodes.PUTSTATIC, "Cl", "fld", "I");
    mv.visitInsn(Opcodes.RETURN);
    mv.visitEnd();
}
cw.visitEnd();

ใน Java ชั้นเรียนจะพูดคร่าวๆดังนี้

public class Cl{
    private static final int fld;

    public static void setFinalField1(){
        fld = 5;
    }

    public static void setFinalField2(){
        fld = 2;
    }
}

ซึ่งไม่สามารถคอมไพล์ด้วยjavacแต่สามารถโหลดและดำเนินการโดย JVM

JVM HotSpot ได้รับการดูแลเป็นพิเศษจากคลาสดังกล่าวในแง่ที่ว่าจะป้องกัน "ค่าคงที่" ดังกล่าวจากการเข้าร่วมการพับแบบคงที่ การตรวจสอบนี้ทำในขั้นตอนการเขียนทับ bytecode ของการเริ่มต้นคลาส :

// Check if any final field of the class given as parameter is modified
// outside of initializer methods of the class. Fields that are modified
// are marked with a flag. For marked fields, the compilers do not perform
// constant folding (as the field can be changed after initialization).
//
// The check is performed after verification and only if verification has
// succeeded. Therefore, the class is guaranteed to be well-formed.
InstanceKlass* klass = method->method_holder();
u2 bc_index = Bytes::get_Java_u2(bcp + prefix_length + 1);
constantPoolHandle cp(method->constants());
Symbol* ref_class_name = cp->klass_name_at(cp->klass_ref_index_at(bc_index));
if (klass->name() == ref_class_name) {
   Symbol* field_name = cp->name_ref_at(bc_index);
   Symbol* field_sig = cp->signature_ref_at(bc_index);

   fieldDescriptor fd;
   if (klass->find_field(field_name, field_sig, &fd) != NULL) {
      if (fd.access_flags().is_final()) {
         if (fd.access_flags().is_static()) {
            if (!method->is_static_initializer()) {
               fd.set_has_initialized_final_update(true);
            }
          } else {
            if (!method->is_object_initializer()) {
              fd.set_has_initialized_final_update(true);
            }
          }
        }
      }
    }
}

ข้อ จำกัด เดียวที่ JVM HotSpot ตรวจสอบคือfinalฟิลด์ไม่ควรแก้ไขนอกคลาสที่finalประกาศฟิลด์


0

เพิ่งเห็นคำถามนั้นในหนึ่งในคำถามสัมภาษณ์ถ้าเป็นไปได้ที่จะเปลี่ยนตัวแปรสุดท้ายด้วยการสะท้อนหรือในขณะทำงาน มีความสนใจจริง ๆ ดังนั้นสิ่งที่ฉันเป็น:

 /**
 * @author Dmitrijs Lobanovskis
 * @since 03/03/2016.
 */
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();
    }
}

คลาสที่เรียบง่ายพร้อมกับตัวแปรสตริงสุดท้าย ดังนั้นในการนำเข้าระดับหลัก java.lang.reflect.Field;

/**
 * @author Dmitrijs Lobanovskis
 * @since 03/03/2016.
 */
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);
    }
}

ผลลัพธ์จะเป็นดังนี้:

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

ตามเอกสาร https://docs.oracle.com/javase/tutorial/reflect/member/fieldValues.html


คุณเห็นโพสต์นี้หรือไม่?
Ravindra HV

คำถามนี้ถามเกี่ยวกับstaticเขตข้อมูลสุดท้ายดังนั้นรหัสนี้ใช้งานไม่ได้ setAccessible(true)ใช้งานได้เฉพาะสำหรับการตั้งค่าฟิลด์อินสแตนซ์สุดท้าย
Radiodef

0

หากสาขาของคุณเป็นแบบส่วนตัวคุณสามารถทำสิ่งนี้ได้:

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

และโยน / จัดการ NoSuchFieldException


-4

จุดทั้งหมดของfinalฟิลด์คือไม่สามารถกำหนดใหม่ได้เมื่อตั้งค่าแล้ว JVM ใช้การรับประกันนี้เพื่อรักษาความมั่นคงในสถานที่ต่าง ๆ (เช่นคลาสภายในอ้างอิงตัวแปรภายนอก) ไม่เลย ความสามารถในการทำเช่นนั้นจะทำลาย JVM!

การแก้ปัญหาคือไม่ต้องประกาศfinalในตอนแรก


4
นอกจากนี้ยังfinalมีบทบาทพิเศษในการดำเนินการแบบมัลติเธรด - การเปลี่ยนfinalค่าจะทำลายโมเดลหน่วยความจำ Java เช่นกัน
PéterTörök

1
และสนามไม่ได้ประกาศไม่ควรได้รับการประกาศfinal static
Tom Hawtin - tackline

2
@ ทอม: โดยทั่วไปอาจเป็นจริง แต่ฉันจะไม่ผิดกฎหมายตัวแปรที่ไม่แน่นอนคงที่ทั้งหมด
bcat

7
@ ทอม: คุณเคยอ่านไหมว่าทำไมซิงเกิลตันถึงชั่วร้าย? ฉันทำ! ตอนนี้ฉันรู้แล้วว่าพวกเขาชั่วร้ายใน Java เท่านั้น และเนื่องจากความพร้อมใช้งานของคลาสโหลดเดอร์ที่ผู้ใช้กำหนด และตั้งแต่ฉันรู้ทั้งหมดนี้และฉันไม่ได้ใช้ตัวโหลดคลาสที่ผู้ใช้กำหนดเองฉันใช้ซิงเกิลตันโดยไม่เสียใจ และเพื่อไม่ Scala ที่เดี่ยวเป็นคุณลักษณะภาษาชั้นแรก - ที่ singletons เป็นความชั่วร้ายคือเป็นที่รู้จักกันดีตำนานเท็จ
มาร์ติน

3
@ มาร์ตินฉันรู้ว่าความคิดเห็นของคุณเก่าและบางทีความคิดเห็นของคุณเปลี่ยนไปตอนนี้ แต่ฉันคิดว่าฉันแค่เพิ่มสิ่งนี้: Singletons เป็นสิ่งชั่วร้ายด้วยเหตุผลที่ไม่มีส่วนเกี่ยวข้องกับ Java มันเพิ่มความซับซ้อนที่ซ่อนอยู่ในรหัสของคุณ นอกจากนี้พวกเขาสามารถทำให้ไม่สามารถทดสอบหน่วยโดยไม่ทราบว่าต้องกำหนดค่าn singletons ก่อน พวกเขาเป็นสิ่งที่ตรงกันข้ามของการฉีดพึ่งพา ทีมของคุณอาจตัดสินใจว่าข้อผิดพลาดของการมีความซับซ้อนที่ซ่อนอยู่นั้นไม่ได้เกินดุลความสะดวกสบายของ Singletons แต่หลายทีมใช้ท่าทางที่ตรงกันข้ามเพื่อเหตุผลที่ดี
สนใจ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.