Phản ánh Java - tác động của setAccessible (true)


105

Tôi đang sử dụng một số chú thích để đặt động các giá trị của các trường trong các lớp. Vì tôi muốn thực hiện việc này bất kể là công khai, được bảo vệ hay riêng tư, tôi luôn gọi setAccessible(true)đối tượng Field trước khi gọi set()phương thức. Câu hỏi của tôi là setAccessible()cuộc gọi có tác động gì đến chính trường?

Cụ thể hơn, hãy nói rằng đó là một trường riêng tư và bộ mã này gọi setAccessible(true). Nếu sau đó một số nơi khác trong mã truy xuất cùng một trường thông qua phản chiếu, thì trường đó đã có thể truy cập được chưa? Hay các phương thức getDeclaredFields()và có getDeclaredField()trả về các phiên bản mới của một đối tượng Trường mỗi lần không?

Tôi đoán một cách khác để nêu câu hỏi là nếu tôi gọi setAccessible(true), điều quan trọng là đặt nó trở lại giá trị ban đầu sau khi tôi hoàn thành như thế nào?

Câu trả lời:


85

Với việc setAccessible()bạn thay đổi hành vi của AccessibleObject, tức là Fieldtrường hợp, nhưng không phải là trường thực tế của lớp. Đây là tài liệu (đoạn trích):

Giá trị của truechỉ ra rằng đối tượng được phản ánh sẽ ngăn chặn các kiểm tra đối với kiểm soát truy cập ngôn ngữ Java khi nó được sử dụng

Và một ví dụ có thể chạy được:

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 bạn cần tự viết các tờ khai nhập khẩu. Tôi hy vọng rằng bạn biết làm thế nào để làm điều đó.
Moritz Petersen

Đã tìm thấy sự cố. Bạn phải ném hoặc xử lý NoSuchFieldException hoặc cha mẹ.
Philip Rego

Vâng, đây chỉ là mã mẫu. Ý tôi là, throws Exceptioncũng xử lý NoSuchFieldException, nhưng bạn có thể muốn xử lý nó theo cách phức tạp hơn.
Moritz Petersen

Tôi nhận được Ngoại lệ trên: Field field1 = myClass.getClass (). GetDeclaredField ("theField"); vì vậy nó thậm chí không biên dịch, tức là setAccessible thậm chí sẽ không thành vấn đề?
user2796104

32

Các getDeclaredFieldphương pháp đã trở lại một đối tượng mới mỗi lần, chính xác bởi vì đối tượng này có thể thay đổi accessiblecờ. Vì vậy không cần đặt lại cờ. Bạn có thể tìm thấy chi tiết đầy đủ trong bài đăng trên blog này .


3

Như các áp phích khác đã chỉ ra, setAccessiblechỉ áp dụng cho phiên bản đó của bạn java.lang.reflect.Field, vì vậy không cần đặt khả năng truy cập trở lại trạng thái ban đầu.

Tuy nhiên...

Nếu bạn muốn cuộc gọi của mình field.setAccessible(true)được liên tục, bạn cần sử dụng các phương pháp cơ bản trong java.lang.Classjava.lang.reflect.Field. Các phương thức công khai gửi cho bạn các bản sao của Fieldphiên bản, vì vậy nó sẽ "quên" sau mỗi lần bạn làm điều gì đó như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();
        }
    }
}

Cập nhật : Việc triển khai này dành cho Java 8, các phiên bản trong tương lai sẽ thay đổi chương trình phụ trợ làm hỏng điều này. Tuy nhiên, khái niệm tương tự vẫn được áp dụng nếu bạn thực sự muốn tiếp tục chiến lược này.


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

}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.