Java中的反射机制详解
反射的简单demo
声明的原始类
class Cat { private String name = "猫猫"; public int age = 10; public Cat(){} public Cat(String name) { this.name = name; } public void hi() { System.out.println("hi~ " + name); } public void hi() { System.out.println(name + " cry"); } }
2. src目录下的配置文件re.properties
classfullpath=com.zhl.Cat method=hi
3. 编写反射代码
public class Reflection01 { public static void main(String[] args) throws Exception { // 加载配置文件中的信息 Properties properties = new Properties(); InputStream inputStream = ReflectionQuestion.class.getClassLoader().getResourceAsStream("re.properties"); properties.load(inputStream); String classfullpath = properties.getProperty("classfullpath"); String methodName = properties.getProperty("method"); // 使用反射创建对象 Class<?> aClass = Class.forName(classfullpath); Object o = aClass.newInstance(); System.out.println(o.getClass()); // 通过反射获取成员方法 Method method = aClass.getMethod(methodName); method.invoke(o); // 通过反射获取成员变量(不能获取私有的) Field ageField = aClass.getField("age"); System.out.println(ageField.get(o)); // 通过反射获取构造器 // 无参 Constructor<?> constructor = aClass.getConstructor(); System.out.println(constructor); // 有参 Constructor<?> constructor2 = aClass.getConstructor(String.class); System.out.println(constructor2); } }
反射机制和传统调用方法的对比
public class Reflection02 { public static void main(String[] args) throws Exception { m1(); m2(); } // 传统方法:直接创建对象 public static void m1() { Cat cat = new Cat(); long begin = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { cat.hi(); } long end = System.currentTimeMillis(); System.out.println("传统方案耗时:" + (end - begin) + "ms"); } // 反射机制调用方法 public static void m2() throws Exception { Class<?> aClass = Class.forName("com.zhl.Cat"); // 无参构造 Constructor<?> constructor = aClass.getConstructor(); // 关闭访问检查的开关 constructor.setAccessible(true); Object o = constructor.newInstance(); Method method = aClass.getMethod("hi"); method.setAccessible(true); long begin = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { method.invoke(o); } long end = System.currentTimeMillis(); System.out.println("使用反射方案耗时:" + (end - begin) + "ms"); } }
反射的优点和缺点:
- 优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活。
- 缺点:使用反射基本是解释执行,对执行速度有影响。
Class类
Class类的对象不是new出来的,是系统创建的,在类加载阶段生成的。
对于某个类的Class类对象,类只加载一次,在内存中只有一份。
每个类的实例都会记得自己是由哪个 Class 实例所生成。
- 已知一个类的全类名 ,且该类在类路径下,可通过Class类的静态方法
forName()
获取- 应用场景:通过配置文件读取类的全路径,加载类
String className;
Class clazz = Class.forName(className);
- 应用场景:通过配置文件读取类的全路径,加载类
- 若已知具体的类,通过 类的class获取,该方式最为安全可靠,程序性能最高
- 应用场景:多用于参数传递,比如通过反射得到对应构造器对象
Class cat = Cat.class;
- 应用场景:多用于参数传递,比如通过反射得到对应构造器对象
- 已知某个类的实例,调用该实例的getClass()方法获取Class对象,实例:
- 应用场景:通过创建好的对象,获取Class对象
Class clazz = 对象.getClass();
- 应用场景:通过创建好的对象,获取Class对象
- 其他方式
获取类加载器,加载指定类 。
ClassLoader loader = 类.class.getClassLoader(); Class<?> clazz = loader.loadClass("com.zhl.Cat");
类加载
基本说明
反射机制是 java实现动态语言的关键,也就是通过反射实现类动态加载。- 静态加载:编译时加载相关的类,如果没有则报错。
依赖性太强 - 动态加载:运行时加载需要的类,如果运行时不用该类,则不报错。
降低了依赖性 - 举例说明 (伪代码)
String key = scan.next();
switch(key) {
case "1" :
Dog dog = new Dog();
dog.hi();
break;
case "2":
Class<?> clazz = Class.forName("Person");
Object o = clazz.newInstance();
Method methodName = clazz.getMethod("hi");
method.invoke(o);
break;
default:
break;
}注意这段代码中,并没有创建
Dog
和Person
类(当前module中没有Dog或Person类),但是编译时,只会报 Dog 类没有创建,而Person类并没有监测出来是否被创建,只有真正被执行到这行代码时才会被加载,判断是否创建过Person类。
- 静态加载:编译时加载相关的类,如果没有则报错。
类加载时机
- 当创建对象时(new)
- 当子类被加载时,父类也加载
- 调用类中的静态成员时
- 通过反射(动态加载)
关于JVM的类加载可参考https://blog.csdn.net/m0_72406127/article/details/137058092?spm=1001.2014.3001.5501
通过反射获取类的结构信息
java.lang.Class
类- getName:获取全类名
- getSimpleName:获取简单类名
- getFields:获取所有public修饰的属性,包含本类以及父类的
- getDeclaredFields:获取本类中所有属性
- getMethods:获取所有public修饰的方法,包含本类以及父类的
- getDeclaredMethods:获取本类中所有方法
- getConstructors:获取所有public修饰的构造器,包含本类
- getDeclaredConstructors:获取本类中所有构造器
- getPackage:以Package形式返回包信息
- getSuperClass:以Class形式返回父类信息
- getInterfaces:以Class[]形式返回接口信息
- getAnnotations:以Annotation[]形式返回注解信息
java.lang.reflect.Field
类- getModifiers: 以int形式返回修饰符
默认修饰符是0,public 是 1,private 是 2,protected 是 4,static 是 8 ,final是 16,多个不同的修饰符可以相加 - getType:以Class形式返回类型
- getName:返回属性名
- getModifiers: 以int形式返回修饰符
java.lang.reflect.Method
类- getModifiers:以int 形式返回修饰符
- getName:返回方法名
- getReturnType:以Class形式获取 返回类型
- getParameterTypes:以Class[]返回参数类型数组
java.lang.reflect.Constructor
类- getModifiers: 以int形式返回修饰符
- getName:返回构造器名(全类名)
- getParameterTypes:以Class[]返回参数类型数组
通过反射创建对象
- 方式一:调用类中的publi 修饰的无参构造器
- 方式二:调用类中的指定构造器
- Class类相关方法
- newlnstance:调用类中的无参构造器,获取对应类的对象
- getConstructor(Class...clazz):根据参数列表,获取对应的public构造器对象
- getDeclaredConstructor(Class...clazz):根据参数列表,获取对应的所有构造器 对象
- Constructor类相关方法
- setAccessible: 设为true,可以访问private修饰的构造器
- newinstance(Object...obj): 调用构造器
在构造器/方法/属性中使用 getDeclaredXxx
方法可以获取本类中的所有构造器/方法/属性,
通过setAccessible()
方法,设置参数为true,关闭访问检查的开关,就可以直接访问和修改被 private 修饰的构造器/方法/属性。