反射基础
反射机制:允许在运行时发现和使用类的信息,将 Java 类中各种成分映射成 Java 对象。
Class 类
手动编写的类被编译后会产生一个 Class 对象,每个 class 标识的类在内存中有且只有一个与之对应的 Class 对象来描述类型信息,Class 类只存私有构造函数,因此对应的 Class 对象只能由 JVM 创建和加载。Class 类的对象作用是运行时提供或获得某个对象的类型信息
源码理解
原文链接:
public class HelloReflect {
public static void main(String[] args) {
try {
// 1. 使用外部配置的实现,进行动态加载类
TempFunctionTest test = (TempFunctionTest)Class.forName("basic.TempFunctionTest").newInstance();
test.sayHello("call directly");
// 2. 根据配置的函数名,进行方法调用(不需要通用的接口抽象)
Object t2 = new TempFunctionTest();
Method method = t2.getClass().getDeclaredMethod("sayHello", String.class);
method.invoke(test, "method invoke");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e ) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public void sayHello(String word) {
System.out.println("hello," + word);
}
}
执行流程具体如下:
小结
- 反射类及反射方法的获取,都是通过从列表中搜寻查找匹配的方法,所以查找性能会随类的大小方法多少而变化。
- 每个类都会有一个与之对应的 Class 实例,从而每个类都可以获取 method 反射方法,并作用到其他实例身上。
- 反射也是考虑了线程安全的,放心使用。
- 反射使用软引用 relectionData 缓存 class 信息,避免每次重新从 jvm 获取带来的开销。
- 反射调用多次生成新代理 Accessor,而通过字节码生存的则考虑了卸载功能,所以会使用独立的类加载器。
- 当找到需要的方法,都会 copy 一份出来,而不是使用原来的实例,从而保证数据隔离。
- 调度反射方法,最终是由 jvm 执行
invoke0()
执行。
反射调用静态方法
下面实例使用反射调用静态方法的两种类型:public 和 private
创建一个类GreetingAndBye
public class GreetingAndBye {
public static String greeting(String name) {
return String.format("Hey %s, nice to meet you!", name);
}
private static String goodBye(String name) {
return String.format("Bye %s, see you next time.", name);
}
}
这个类包含两个静态方法,一个是private
,一个是public
调用public static
方法
@Test
public void invokePublicMethod() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<GreetingAndBye> clazz = GreetingAndBye.class;
Method method = clazz.getMethod("greeting", String.class);
Object result = method.invoke(null, "Eric");
System.out.println(result);
}
当调用静态方法时,invoke
的第一个参数传null
调用private static
方法
@Test
public void invokePrivateMethod() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<GreetingAndBye> clazz = GreetingAndBye.class;
Method method = clazz.getDeclaredMethod("goodBye", String.class);
method.setAccessible(true);
Object result = method.invoke(null, "Eric");
System.out.println(result);
}
当调用的方法为private
时,需要使用getDeclaredMethod
代替getMethod
。并且需要调用method.setAccessible(true)
去调用private
方法。
反射检测抽象类、接口、实现类
public abstract class AbstractExample {
public abstract LocalDate getLocalDate();
public abstract LocalTime getLocalTime();
}
public interface InterfaceExample {
}
检测抽象类
public void givenAbstractClass_whenCheckModifierIsAbstract_thenTrue() throws Exception {
Class<AbstractExample> clazz = AbstractExample.class;
System.out.println(Modifier.isAbstract(clazz.getModifiers())); // true
}
public void givenAbstractClass_whenCheckIsAbstractClass_thenTrue() {
Class<AbstractExample> clazz = AbstractExample.class;
int mod = clazz.getModifiers();
System.out.println(Modifier.isInterface(mod)); // false
System.out.println(Modifier.isAbstract(mod)); // true
}
检测接口
public void givenInterface_whenCheckModifierIsAbstract_thenTrue() {
Class<InterfaceExample> clazz = InterfaceExample.class;
int mod = clazz.getModifiers();
System.out.println(Modifier.isInterface(mod)); // true
System.out.println(Modifier.isAbstract(clazz.getModifiers())); // true 注意该方法检测接口也是抽象类
}
检测实现类
public void givenConcreteClass_whenCheckIsAbstractClass_thenFalse() {
Class<Date> clazz = Date.class;
int mod = clazz.getModifiers();
System.out.println(Modifier.isInterface(mod)); // false
System.out.println(Modifier.isAbstract(mod)); // false
}
判断静态方法
/**
* 判断是不是静态方法
*/
@Test
public void whenCheckStaticMethod_ThenSuccess() throws Exception {
Method method = StaticUtility.class.getMethod("getAuthorName", null);
System.out.println(Modifier.isStatic(method.getModifiers())); // true
}
/**
* 获取类中的静态方法
*/
@Test
public void whenCheckAllStaticMethods_thenSuccess() {
List<Method> methodList = Arrays.asList(StaticUtility.class.getMethods())
.stream()
.filter(method -> Modifier.isStatic(method.getModifiers()))
.collect(Collectors.toList());
System.out.println(methodList);
}
方法详解
1. getMethod()
可以使用寻找类中所有的public
的方法,包括从基类继承的、从接口实现的所有public方法。
2. getDeclaredMethod()
获取类中定义的任何方法,包括public, protected, default, private
,但是不包括继承的方法。
3. setAccessible()
设置访问控制检查。当调用private
或protected
方法时,需要method.setAccessible(true);
,否者会抛出IllegalAccessException
异常。