使用反射访问不可见类

新手上路,请多包涵

我正在尝试使用反射获取不可见类的实例,也就是包私有类。我想知道是否有办法切换修饰符以使其公开,然后使用 Class.forName 访问它。当我现在尝试这样做时,它阻止我说我做不到。不幸的是没有 setAccesible Class 类的方法。

原文由 Josh Sobel 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 958
2 个回答

嵌套类- 在其他类中定义的类(包括静态和非静态类)

内部类- 非静态嵌套类(内部类的实例需要外部类的实例存在)

非嵌套(顶级)类

根据您的问题,我们知道您要访问的构造函数不是公开的。所以你的班级可能看起来像这样( A 班级在一些不同于我们的包中)

 package package1;

public class A {
    A(){
        System.out.println("this is non-public constructor");
    }
}

要创建此类的实例,我们需要获取要调用的构造函数,并使其可访问。完成后,我们可以使用 Constructor#newInstance(arguments) 创建实例。

 Class<?> c = Class.forName("package1.A");
//full package name --------^^^^^^^^^^
//or simpler without Class.forName:
//Class<package1.A> c = package1.A.class;

//In our case we need to use
Constructor<?> constructor = c.getDeclaredConstructor();
//note: getConstructor() can return only public constructors
//so we needed to search for any Declared constructor

//now we need to make this constructor accessible
constructor.setAccessible(true);//ABRACADABRA!

Object o = constructor.newInstance();


嵌套类和内部类

如果您想使用 Class.forName 访问嵌套(静态和非静态)类,您需要使用语法:

 Class<?> clazz = Class.forName("package1.Outer$Nested");

Outer$Nested 表示 Nested 类在 Outer 类中声明。嵌套类与方法非常相似,它们可以访问其外部类的所有成员(包括私有成员)。

但是我们需要记住,内部类的实例存在需要其外部类的实例。通常我们通过以下方式创建它们:

 Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();

所以当你看到内部类的每个实例都有一些关于它的外部类的信息时(对该外部实例的引用存储在 this$0 字段中,更多信息: What does it mean if a variable has the name “this$0 “调试 Java 时在 IntelliJ IDEA 中?

So while creating instance of Inner class with Constructor#newInstance() you need to pass as first argument reference to instance of Outer class (to simulate outer.new Inner() 行为)。

这是一个例子。

在 package1

 package package1;

public class Outer {
    class Inner{
        Inner(){
            System.out.println("non-public constructor of inner class");
        }
    }
}

在 package2

 package package2;

import package1.Outer;
import java.lang.reflect.Constructor;

public class Test {
    public static void main(String[] args) throws Exception {

        Outer outerObject = new Outer();

        Class<?> innerClazz = Class.forName("package1.Outer$Inner");

        // constructor of inner class as first argument need instance of
        // Outer class, so we need to select such constructor
        Constructor<?> constructor = innerClazz.getDeclaredConstructor(Outer.class);

        //we need to make constructor accessible
        constructor.setAccessible(true);

        //and pass instance of Outer class as first argument
        Object o = constructor.newInstance(outerObject);

        System.out.println("we created object of class: "+o.getClass().getName());

    }
}

静态嵌套类

静态嵌套类的实例不需要外部类的实例(因为它们是静态的)。因此,在他们的情况下,我们不需要寻找以 Outer.class 作为第一个参数的构造函数。而且我们不需要将外部类的实例作为第一个参数传递。换句话说,代码将与非嵌套(顶级)类相同(也许除了您需要添加使用 $ 而不是 . 在类名中当引用 Class.forName() 中的嵌套类时,如 Class.forName("some.package.Outer$Nested1$NestedNested") )。

原文由 Pshemo 发布,翻译遵循 CC BY-SA 4.0 许可协议

Class.forName 应该可以。如果该类位于 "package.access" 安全属性中的包层次结构列表中,那么您将需要以适当的权限(通常是所有权限;或者没有安全管理器)执行操作。

如果您尝试使用 Class.newInstance ,请不要。 Class.newInstance 异常处理不佳。取而代之的是获得 Constructor 并调用 newInstance 。如果没有异常跟踪,很难看出您遇到了什么问题。

与以往一样,反射的大多数(但不是所有)使用都是坏主意。

原文由 Tom Hawtin - tackline 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题