我试图让一个不可见的类的实例,AKA包专用类,使用反射。 我在想,如果有切换修饰符把它公开,然后使用访问它的方式Class.forName
。 当我尝试,现在它停止我说我不能这样做。 不幸的是没有setAccesible
的方法Class
类。
Answer 1:
嵌套类 -其他类中定义的类(包括静态和非静态类)
内部类 -非静态嵌套类(内部类的实例需要外部类的实例存在)
非嵌套(顶层)的类
根据您的问题,我们知道你要访问的构造是不公开的。 所以,你的类可能看起来像这样( 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
场,更多的信息: 这是什么意思,如果一个变量的名称为“此$ 0”中的IntelliJ IDEA而调试Java? )
因此,在创建的实例Inner
与类Constructor#newInstance()
你需要传递的第一个参数参考的实例Outer
类(模拟outer.new Inner()
行为)。
下面是一个例子。
在包1
package package1;
public class Outer {
class Inner{
Inner(){
System.out.println("non-public constructor of inner class");
}
}
}
在包装2
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
作为第一个参数。 而且我们也不需要通过外部类的实例作为第一个参数。 换句话说代码将是相同非嵌套(顶层)类(也许除了事实,你将需要添加$Nested
语法Class.forName()
Answer 2:
Class.forName
应该工作。 如果类是在一个包层次列表中的"package.access"
安全属性,那么你将需要与适当的权限进行操作(通常是所有权限;或者没有安全管理器)。
如果你要使用Class.newInstance
,不要。 Class.newInstance
不好处理异常。 相反,获得了Constructor
和调用newInstance
上。 这是很难看到你有什么用没有异常跟踪问题。
与以往一样,大多数但不是反射的所有用途是坏主意。
Answer 3:
我们最近发布了一个库,帮助了很多通过反射来访问私有字段,方法和内部类: BoundBox
对于像一个类
public class Outer {
private static class Inner {
private int foo() {return 2;}
}
}
它提供了一个语法,如:
Outer outer = new Outer();
Object inner = BoundBoxOfOuter.boundBox_new_Inner();
new BoundBoxOfOuter.BoundBoxOfInner(inner).foo();
你所要做的创建BoundBox类的唯一的事情就是写@BoundBox(boundClass=Outer.class)
和BoundBoxOfOuter
类将立即产生。
Answer 4:
我不得不要求从旧版本的对象复制字段的值,如果这个值是最新版本空。 我们有这两个选项。
核心Java:
for (Field f : object.getClass().getSuperclass().getDeclaredFields()) {
f.setAccessible(true);
System.out.println(f.getName());
if (f.get(object) == null){
f.set(object, f.get(oldObject));
}
}
使用Spring [org.springframework.beans.BeanWrapper]:
BeanWrapper bw = new BeanWrapperImpl(object);
PropertyDescriptor[] data = bw.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : data) {
System.out.println(propertyDescriptor.getName());
Object propertyValue = bw.getPropertyValue(propertyDescriptor.getName());
if(propertyValue == null )
bw.setPropertyValue( new PropertyValue(propertyDescriptor.getName(),"newValue"));
}
Answer 5:
您可以使用歧管的 @Jailbreak直接,类型安全的Java反射:
Foo foo = new @Jailbreak Foo();
public class Foo {
Foo() {...}
private void yodog() {...}
}
这里@Jailbreak
使编译器来解决构造类型安全,就好像公众,而歧管的发动机罩下为您生成有效的反射代码。
此外,您可以使用@Jailbreak
访问和构建非可视类:
com.abc. @Jailbreak Bar bar = new com.abc. @Jailbreak Bar();
package com.abc;
// package-private class
class Bar {
Bar() {...}
}
对于隐藏的类访问,Java的语法标注要求将其从包装标注的分开的类。
更一般地,你可以使用@Jailbreak
任何类型的反思:
@Jailbreak Foo foo = new Foo();
foo.yodog();
@Jailbreak
解锁在编译器中直接访问所有成员foo的局部变量Foo
的层次结构。
同样,您可以使用一次性使用的越狱()扩展方法:
foo.jailbreak().yodog();
通过jailbreak()
方法,你可以访问任何成员Foo
的层次结构。
了解更多有关歧管 。