为什么一个类可以加载另一个类的私有内部类?

时间:2012-12-24 10:40:27

标签: java

我有

class A {
    private static class B {
        B() {
        }
    }
}

虽然B是私有的但我从另一个类加载A $ B.class没有问题。为什么允许这样做?

class C {
    public static void main(String[] args) throws Exception {
        System.out.println(Class.forName("A$B").newInstance());
    }
}

输出

A$B@affc70

更新

我理解有意加载任何类加载的限制,但必须有合理的解释原因。

请注意,包私有B {}构造函数是故意的。如果我删除它我会得到

java.lang.IllegalAccessException:B类无法使用修饰符“private”访问A $ B类的成员

5 个答案:

答案 0 :(得分:12)

类加载器may load any class,但是在实例化时强制执行访问规则。

那么为什么你的代码能够实例化类?

内部类在字节代码级别是一个很大的问题,因为它们被实现为与Java 1.0字节码兼容。

因此,源代码级别的这个私有内部类实际上是在字节代码级别受包保护的。您可以通过将类C移动到另一个包并将B的构造函数设置为public来验证:

Exception in thread "main" java.lang.IllegalAccessException: 
Class something.C can not access a member of class A$B with modifiers "public"

Reflection允许使用setAccessible()覆盖访问规则,但这不是这种情况。

答案 1 :(得分:8)

此行为符合the javadoc

  

请注意,此方法不会检查所请求的类是否可供其调用者访问。

换句话说,forName可以访问不可访问的类,并且不会抛出IllegalAccessExcessException。并且因为构造函数是包私有的,即(我假设)可以从您的调用位置访问,newInstance也不会抛出任何异常。

如果构造函数不可访问(因为您将其移动到另一个包或将其设为私有),newInstance将抛出异常。

关于你的更新,如果删除构造函数,你将调用默认构造函数,它将具有与类相同的访问权限,在本例中为private(cf JLS 8.8.3如果声明了类private,则默认构造函数隐式赋予访问修饰符private )。

答案 2 :(得分:3)

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

这是一个可以打破一些规则的工具,可以将它扔到他的脚上或正确使用它。

Reflection提供了一种通过类和对象之间的常规交互通常无法获得的方法来访问类的信息的机制。

  

其中之一是允许从外部类访问私有字段   和对象。

但是,getDeclaredField()setAccessible()实际上都是由安全管理器检查的,如果不允许您的代码执行此操作,则会抛出异常。许多反射技巧可能无法与主动安全管理器一起使用。

答案 3 :(得分:3)

虽然反射是一个很好的比较,但类加载器超出了反射库的功能。例如,类加载器可以创建类,您无法使用反射。它也可以

  • 触发枚举实例的创建。
  • 在初始化之前为您提供类的引用。
  • “覆盖”父类加载器中的类。
  • 触发卸载时卸载的类。
  • 为您提供类的字节代码,为您提供原始实现详细信息。
  • 为类路径中的非类资源提供流。

就像反思一样,它可以加载一个私有成员的类,如果它不能,它将毫无意义。

答案 4 :(得分:0)

使用反射甚至可以更改修改器

class MyClass{
private int value;
}

Field value = MyClass.class.getDeclaredField("value");
value.setAccessible(true);