为什么允许通过反射访问Java的私有字段?

45

考虑以下例子:

import java.lang.reflect.Field;

public class Test {

    public static void main(String[] args) {
        C c = new C();
        try {
            Field f = C.class.getDeclaredField("a");
            f.setAccessible(true);
            Integer i = (Integer)f.get(c);
            System.out.println(i);
        } catch (Exception e) {}
    }
}

class C {
    private Integer a =6;
}

使用反射可以访问类的私有字段似乎是不合逻辑的。为什么会提供这样的功能?允许这种访问难道不是"危险"的吗?

7个回答

61

私有是为了防止意外的误用,而不是作为一种安全机制。如果选择绕过它,则需要自行承担风险并且假设你知道自己在做什么。


4
+1 表示 private 关键字是提示其他程序员不必担心成员变量对类的运行是否有影响。它使您能够将代码分为一些合同,并让编译器检查您不直接读写这些成员变量的合同。但是,如果必须打破合同,您可以这样做,因为编程语言不应妨碍程序员的操作,即使他想自己给自己找麻烦。 - Janusz
总的来说,封装或者说数据隐藏是关于作用域而不是安全性的问题,即使你将API提供为第三方jar包。 - lowLatency

22

无论是getDeclaredField()还是setAccessible(),实际上都会受到安全管理器的检查,当您的代码不被允许执行时,它们会抛出异常。很多情况下,您可能没有注意到这一点,因为Java代码通常在没有安全管理器的情况下运行。

但有一个重要的例外,那就是Applet,它总是带有安全管理器。


你说“Java代码经常在没有安全管理器的情况下运行”。难道没有默认的安全管理器吗? - firstthumb
2
哇...“important”和“applet”在同一句话中。那些日子真是不同寻常。我会把它留下来,作为时代变迁的见证 :-) - Joachim Sauer

10

8

反射是危险的。 没有例外。

为了稍微增加安全性而限制一个非常危险的系统的效用有什么意义呢?

此外,自动序列化需要能够“吸取”任何类的“大脑”;在这种情况下,忽略访问修饰符是必要的。


4

来源:http://java.sun.com/docs/books/tutorial/reflect/

反射通常被需要检查或修改在Java虚拟机中运行的应用程序的运行时行为的程序使用。

它是一个工具,可以让人们打破一些规则,可以将其扔在脚上或正确使用它。


4

反射是一个完整的API,可以进入类内部。包括私有成员。

在你不信任正在运行的代码(如小程序等)的情况下,你可以完全阻止代码使用反射。有关详细信息,请参见这个Stack Overflow问题


3
Java.lang.reflect包的描述中可以看出:

该包中的类,连同 Java.lang.Class 可以用于 调试器、 解释器、对象检查器、类 浏览器和需要访问目标对象的公共成员(基于其运行时类)或由给定类声明的成员的服务,例如Object 序列化和JavaBeans。

反射提供了一种通过通常无法通过类和对象之间的常规交互来访问有关类信息的机制。其中之一是允许从外部类和对象访问私有字段。

是的,反射通常确实可能很危险,因为它可以暴露类和对象的内部细节。

然而,它也是一个非常强大的工具,可以用于检查类和对象的内部细节,这些细节无法通过其他标准手段访问。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接