การสะท้อน Java - ผลกระทบของ setAccessible (จริง)


106

ฉันกำลังใช้คำอธิบายประกอบเพื่อกำหนดค่าของเขตข้อมูลในชั้นเรียนแบบไดนามิก เนื่องจากฉันต้องการทำสิ่งนี้ไม่ว่าจะเป็นสาธารณะป้องกันหรือส่วนตัวฉันจึงเรียกsetAccessible(true)วัตถุฟิลด์ทุกครั้งก่อนที่จะเรียกใช้set()เมธอด คำถามของฉันคือการsetAccessible()โทรมีผลกระทบอย่างไรต่อสนาม?

setAccessible(true)โดยเฉพาะอย่างยิ่งบอกว่ามันเป็นข้อมูลส่วนตัวและชุดของสายรหัสนี้ หากสถานที่อื่นในรหัสเพื่อดึงฟิลด์เดียวกันผ่านการสะท้อนกลับฟิลด์นั้นจะสามารถเข้าถึงได้หรือไม่? หรือgetDeclaredFields()และgetDeclaredField()วิธีการส่งคืนอินสแตนซ์ใหม่ของวัตถุฟิลด์ทุกครั้งหรือไม่

ฉันเดาอีกวิธีหนึ่งในการระบุคำถามคือถ้าฉันโทรsetAccessible(true)ไปการตั้งค่าให้กลับเป็นค่าเดิมหลังจากทำเสร็จแล้วมีความสำคัญเพียงใด

คำตอบ:


85

เมื่อsetAccessible()คุณเปลี่ยนพฤติกรรมของAccessibleObjectเช่นFieldอินสแตนซ์ แต่ไม่ใช่ฟิลด์จริงของคลาส นี่คือเอกสาร (ข้อความที่ตัดตอนมา):

ค่าtrueบ่งชี้ว่าอ็อบเจ็กต์ที่สะท้อนควรระงับการตรวจสอบการควบคุมการเข้าถึงภาษา Java เมื่อถูกใช้

และตัวอย่างที่รันได้:

public class FieldAccessible {
    public static class MyClass {
        private String theField;
    }

    public static void main(String[] args) throws Exception {
        MyClass myClass = new MyClass();
        Field field1 = myClass.getClass().getDeclaredField("theField");
        field1.setAccessible(true);
        System.out.println(field1.get(myClass)); // no exception
        Field field2 = myClass.getClass().getDeclaredField("theField");
        System.out.println(field2.get(myClass)); // IllegalAccessException
    }

}

@PhilipRego คุณต้องเขียนประกาศการนำเข้าด้วยตัวคุณเอง ฉันหวังว่าคุณจะรู้วิธีการทำเช่นนั้น
Moritz Petersen

พบปัญหา คุณต้องโยนหรือจัดการ NoSuchFieldException หรือผู้ปกครอง
Philip Rego

ใช่นี่เป็นเพียงตัวอย่างโค้ด ฉันหมายถึงthrows ExceptionจัดการNoSuchFieldExceptionด้วย แต่คุณอาจต้องการจัดการด้วยวิธีที่ละเอียดกว่านี้
Moritz Petersen

ฉันได้รับ Exception บน: Field field1 = myClass.getClass (). getDeclaredField ("theField"); มันจึงไม่ได้รวบรวมเช่น setAccessible จะไม่สำคัญด้วยซ้ำ?
user2796104

32

getDeclaredFieldวิธีการที่มีการส่งคืนวัตถุใหม่ในแต่ละครั้งว่าเนื่องจากวัตถุนี้มีแน่นอนaccessibleธง ดังนั้นจึงไม่จำเป็นต้องรีเซ็ตค่าสถานะ คุณสามารถดูรายละเอียดทั้งหมดได้ในโพสต์บล็อกนี้


3

ตามที่ผู้โพสต์อื่นระบุไว้setAccessibleใช้ได้กับอินสแตนซ์ของคุณjava.lang.reflect.Fieldเท่านั้นดังนั้นจึงไม่จำเป็นต้องตั้งค่าการช่วยการเข้าถึงให้กลับสู่สถานะเดิม

อย่างไรก็ตาม ...

หากคุณต้องการโทรของคุณเพื่อfield.setAccessible(true)ที่จะติดตาคุณจำเป็นต้องใช้วิธีการพื้นฐานในและjava.lang.Class java.lang.reflect.Fieldวิธีการเปิดเผยต่อสาธารณะจะส่งสำเนาของFieldอินสแตนซ์ให้คุณดังนั้นจึง"ลืม"ทุกครั้งที่คุณทำสิ่งที่ชอบclass.getField(name)

import java.lang.reflect.*;
import sun.reflect.FieldAccessor;

public class Reflect {
    private static Method privateGetDeclaredFields;
    private static Method getFieldAccessor;

    public static Field[] fields(Class<?> clazz) throws Exception {
        return (Field[]) privateGetDeclaredFields.invoke(clazz, false);
    }

    public static <T> T get(Object instance, Field field) throws Exception {
        return ((FieldAccessor) getFieldAccessor.invoke(field, instance)).get(instance);
    }

    public static void set(Object instance, Field field, Object value) throws Exception {
        ((FieldAccessor) getFieldAccessor.invoke(field, instance)).set(instance, value);
    }

    static {
        try {
            // These are used to access the direct Field instances instead of the copies you normally get through #getDeclaredFields.
            privateGetDeclaredFields = Class.class.getDeclaredMethod("privateGetDeclaredFields", boolean.class);
            privateGetDeclaredFields.setAccessible(true);
            getFieldAccessor = Field.class.getDeclaredMethod("getFieldAccessor", Object.class);
            getFieldAccessor.setAccessible(true);
        } catch (Exception e) {
            // Should only occur if the internals change.
            e.printStackTrace();
        }
    }
}

อัปเดต : การใช้งานนี้มีไว้สำหรับ Java 8 เวอร์ชันในอนาคตจะเปลี่ยนแบ็กเอนด์ซึ่งจะทำลายสิ่งนี้ แนวคิดเดียวกันนี้ยังคงใช้ได้อยู่แม้ว่าคุณจะต้องการดำเนินกลยุทธ์นี้ต่อไป


-1
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class PrivateVariableAcc {

    public static void main(String[] args) throws Exception {
        PrivateVarTest myClass = new PrivateVarTest();
        Field field1 = myClass.getClass().getDeclaredField("a");
        field1.setAccessible(true);
        System.out.println("This is access the private field-"
            + field1.get(myClass));
        Method mm = myClass.getClass().getDeclaredMethod("getA");
        mm.setAccessible(true);
        System.out.println("This is calling the private method-"
            + mm.invoke(myClass, null));
    }

}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.