简单的记录使用反射获取构造方法碰到的问题。
代码列表
return instantiateClass(clazz.getDeclaredConstructor());
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) { ReflectionUtils.makeAccessible(ctor); return ctor.newInstance(args); } public static void makeAccessible(Constructor<?> ctor) { if ((!Modifier.isPublic(ctor.getModifiers()) || !Modifier.isPublic(ctor.getDeclaringClass().getModifiers())) && !ctor.isAccessible()) { ctor.setAccessible(true); } }
1、所以非public的方法都需要带有Declared这个单词的反射方法,才能得到
2、非public属性要想使用还是老老实实设置setAccessible(true)
3、获取私有内部类,需要给定一个外部类对象
代码-例子
public class ConstructorTest {
private ConstructorTest() {
}
}
public class ConstructorMain {
public static void main(String[] args) throws Exception{
Constructor constroctor = ConstructorTest.class.getDeclaredConstructor();
constroctor.setAccessible(true);
ConstructorTest entity = (ConstructorTest) constroctor.newInstance();
System.out.println(entity);
}
}
分析一下这个getConstructor,里面有一行如下代码
return getConstructor0(parameterTypes, Member.PUBLIC);
getConstructor0代码中第二个参数与Member.PUBLIC比较为true
Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
privateGetDeclaredConstructors方法中如下代码返回public的构造方法
private Constructor<T>[] privateGetDeclaredConstructors(boolean publicOnly) { res = publicOnly ? rd.publicConstructors : rd.declaredConstructors; }
返回为null,在getConstructor0中抛出异常
备注:所以非public的方法都需要带有Declared这个单词,才能得到
贪图省力,main方法直接写在ConstructorTest这个类中,发现不用setAccessible,照样可以运行。在main中,还可以直接访问私有方法呢。
public class ConstructorTest {
private ConstructorTest() {
}
private void print() {System.out.println("yes");};
public static void main(String[] args) {
ConstructorTest ct = new ConstructorTest();
ct.print();
}
}
分析代码
newInstance里面的代码片段
public T newInstance(Object ... initargs) {
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}
}
这里main方法直接写在ConstructorTest这个类中,那么caller == clazz
void checkAccess(Class<?> caller, Class<?> clazz, Object obj, int modifiers) { if (caller == clazz) { // quick check return; // ACCESS IS OK } }
检查权限通过,因此不写setAccessible也是可行的
如果设置setAccessible(true)
会设置这样的属性
obj.override = flag;
从上面可以知道if (!override) 直接跳过checkAccess权限检查
备注:所以,非public属性要想使用还是老老实实设置setAccessible(true)
在获取私有内部类方法的时候比较纠结,外部无法访问私有内部类,做了如下设计
1、ConstructorMain,提供一个方法tryPrivateConstructor,供ConstructorTest调用,传递Constructor
2、在ConstructorTest中提供一个printInnerClazz的public方法调用tryPrivateConstructor并传入私有变量的Constructor
3、在ConstructorMain的main中执行printInnerClazz
这里因为内部类私有,无法转化成具体内部类,可以让内部类实现一个接口,按照多态,强转成接口,使用接口调用具体方法,打印
代码-ConstructorTest
public class ConstructorTest {
private class InnerClazz {
}
public void printInnerClazz() throws Exception{
ConstructorMain.tryPrivateConstructor(InnerClazz.class.getDeclaredConstructor(ConstructorTest.class),this);
}
}
代码-ConstructorMain
public class ConstructorMain {
public static void tryPrivateConstructor(Constructor constructor,ConstructorTest thizz) throws Exception {
constructor.setAccessible(true);
System.out.println(constructor.newInstance(thizz));
}
public static void main(String[] args) throws Exception{
ConstructorTest ct = new ConstructorTest();
ct.printInnerClazz();
}
}
上面验证了constructor.setAccessible(true),以及getDeclaredConstructor。非静态内部类还需要一个外部类的对象,作为参数,所以它的构造函数默认就只有一个包含外部类对象的参数。
如果觉得使用getDeclaredConstructor容易错,那么就使用getDeclaredConstructors()[0],返回所有的构造方法,取第一个。
在getDeclaredConstructor中里面参数与实际的构造函数参数不一致,如果抛出异常
代码-getDeclaredConstructor
if(arrayContentsEq(parameterTypes,constructor.getParameterTypes()))
arrayContextsEq比较他们的个数
备注:获取私有内部类,需要给定一个外部类对象