1.java反射基础
2.java反射进阶
上一节介绍了反射基础,本节介绍更深入的反射概念。
1.获取不到Class
当Class.forName()中路径获取不到对应的Class时,会抛出异常。
2. 获取不到Field
- 确实不存在这个Field
- 修饰符导致的权限问题
以上两种情况会抛出NoSuchFieldException异常
反射获取属性的修饰符
方法 | 本Class | SuperClass |
---|---|---|
getField | public | public |
getDeclaredField | public procted private | no |
getFields | public | public |
getDeclaredFields | public procted private | no |
1. getField只能获取对象中public修饰符的属性,并且能获取父类Class的public属性。
2. getDeclaredField能获取对象中各种修饰符的属性,但无法获取父类的任何属性。
如何才能获取父类的属性呢?class对象会提供getSuperClass的方法来获取父类的对象,然后再通过父类调用
getDeclaredField来获取其属性。
Class c1=Class.forName("com.zhang.Reflect");
Class superClass=c1.getSuperClass();
Field field=superClass.getDeclaredField("name");
3. 获取不到Method
和Field的情况类似,当方法名或参数数目类型没对上时,就会出NoSuchMethodException异常。获取方法体如下表
获取方法体
方法 | 本Class | SuperClass |
---|---|---|
getMethod | public | public |
getDeclaredMethod | public procted private | no |
getMethods | public | public |
getDeclaredMethods | public procted private | no |
4. 获取不到Constructor
方法 | 本Class | SuperClass |
---|---|---|
getConstructor | public | public |
getDeclaredConstructor | public procted private | no |
反射创建一个对象,可以使用Class.newInstance()和Constructor.newInstance()两种方法,不同之处在在于Class.newInstance()的使用受到限制,对应的Class中必须存在一个无参数的构造方法,并且必须要有访问权限,而constructor.newInstance()适用任何类型的构造方法,无论是否有参数都可以调用,只需要使用setAccessible()控制访问验证即可。所以一般建议使用constructor.newInstance().
5.反射静态方法
调用静态方法直接用Class.method()的形式就可以调用。
public class TestMethod{
static void test(){
System.out.println("test");
}
}
public class Test{
public static void main(String[] args) {
try {
Class clz=Class.forName("TestMethod");
Method method=clz.getDeclaredMethod("test");
method.invoke(null);
}catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
关键在于Method.invoke的第一个参数,static方法因为因为属于类本身,所以不需要填写对象,填写nuLL即可。
6.反射泛型参数方法
在说这个之前,我门需要看下invoke方法的代码
@CallerSensitive
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}
第一个Object参数对应的Class对象实例,后面的参数是可变参数,可以接受多个参数。
public class TestMethod<T> {
public void test(T t){
System.out.println(t);
}
}
public class Test {
public static void main(String[] args) {
try {
Class clzT=TestMethod.class;
Method method=clzT.getDeclaredMethod("test", Object.class);
method.setAccessible(true);
method.invoke(new TestMethod<Integer>(), 1);
}catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
其中有一个泛型基础——类型擦除。
当一个方法中有泛型参数时,编译器会自动类型向上转型,T向上转型是Object,所以实际上在TestMethod类中是test(Object t)。getDeclaredMethod需要用Object.class作为参数。
网友评论