反射
Java反射API是Java语言实现动态性的关键,它允许动态的创建对象、赋值、以及调用对象的方法,同时反射也是实现动态代理的关键,涉及到反射相关的几个类主要有 Class、ClassLoader,Field、Method、Constructor、Proxy等。因为在Java中一切皆对象,当然,编译后的class字节码文件也会被JVM创建出一个唯一的Class类型对象,类就是类,对象就是对象,对象有属性可以调方法,任何对象也不例外,无需把这些对象想复杂化。
反射的几个API使用
- 获取Class对象的几种方式
//一、获取class的几种方式
Class<?> cls1 = Class.forName("com.example.module01.reflect.Human"); //常用于动态的加载某个class,例如从配置文件获取
Class<Human> cls2 = Human.class;//常用于调用某个需要class类型参数的方法时的参数传递
Class<? extends Human> cls3 = new Human().getClass();//常用于在明确类型的情况下获取class
Class<?> cls4 = RefTest01.class.getClassLoader().loadClass("com.example.module01.reflect.Human");//常用于动态
System.out.println(cls1 == cls2 ? cls1 == cls3 ? cls1 == cls4 : "不相等" : "不相等"); //true
- Constructor类API
// 2.1 构造方法相关
Class<Student> clazz = Student.class;
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
System.out.println("声明的构造方法个数:" + declaredConstructors.length);
Constructor<?>[] constructors = clazz.getConstructors();//获取所有的构造方法
for (Constructor<?> constructor : constructors) {
Class<?> declaringClass = constructor.getDeclaringClass();//获取此构造方法的声明类
Class<?>[] parameterTypes = constructor.getParameterTypes();//获取不带泛型的构造方法
Type[] genericParameterTypes = constructor.getGenericParameterTypes();//获取带泛型的构造方法
Object instance = null;
// instance = constructor.newInstance();//调用newInstance
System.out.println("构造方法的声明位置:" + declaringClass + ",构造方法参数个数:" + parameterTypes.length
+ ",带泛型的构造方法参数个数:" + genericParameterTypes.length + ",创建的对象:" + instance);
}
- Method API
//2.2 method方法API
Method[] methods = clazz.getMethods(); //可以获取所有的公开的方法,包括本类的和继承的
for (Method method : methods) {
System.out.println("公开的方法:" + method.getName() + "," + method.getDeclaringClass());
}
Method[] declaredMethods = clazz.getDeclaredMethods();//获取声明在本类中的所有方法,包括静态方法
for (Method declaredMethod : declaredMethods) {
declaredMethod.setAccessible(true);//破解private权限
declaredMethod.invoke(clazz.newInstance());//反射执行方法
System.out.println("本类所有方法:" + declaredMethod);
}
System.out.println("========================Field=======================");
- Field API
//2.3 属性相关
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println("公开的属性:" + field.getName());
}
for (Field declaredField : clazz.getDeclaredFields()) {
Student student = clazz.newInstance();
declaredField.setAccessible(true);//设置可访问
declaredField.set(student,1);//给属性赋值
Object value = declaredField.get(student);
System.out.println("本类的所有属性:" + declaredField.getName() + " val ===> " + value);
}
- 获取父类 接口API相关信息,通过获取superClass给继承的父类属性赋值
//2.4获取父类、接口信息
Class<? super Student> superclass = clazz.getSuperclass();
Class<?>[] interfaces = clazz.getInterfaces();
System.out.println("父类是:" + superclass); //Object类的superClass是null。
System.out.println("本类实现的接口:" + interfaces.length);
//2.5 实现Spring自动注入效果,将父类的protected类型属性也自动注入。
Class<Student> stuClass = Student.class;
//通过反射创建对象
Student instance = stuClass.newInstance();
//获取本类的方法
for (Field field : stuClass.getDeclaredFields()) {
//开启访问权限
field.setAccessible(true);
//动态赋值
field.set(instance,111);
}
//继续获取父类的class对象
Class<? super Student> superClass = stuClass.getSuperclass();
//继续获取父类的成员属性
Field[] declaredFields = superClass.getFields();
for (Field declaredField : declaredFields) {
//给父类属性赋值
declaredField.set(instance,222);
}
//打印输出
System.out.println(instance);
- Class类的几个重要方法
- isAssignableFrom 用于判断两个Class对象是否有继承或者实现关系。
- Reflection.getCallerClass 获取调用当前方法的Class对象
- 在使用反射为静态属性赋值或者调用静态方法时 可以不传target实例对象。
Class类加载过程
Java类加载主要分为这么几个阶段
- 编译阶段
将.java源文件编译成.class字节码文件。
- 运行阶段
- 当JVM第一次加载某一个用到的类时,会使用ClassLoader类加载器加.class文件加载到内存中。
- 连接-验证阶段:对class进行验证 头文件元数据等。
- 连接-准备阶段:收集字节码中的静态属性与静态代码块,并把静态属性赋值默认值。
- 连接-解析阶段:解析阶段主要是把符号引用转换为地址应用。
- 初始化:初始化阶段主要是运行class的静态代码块内容。
所有阶段都运行完成之后,此时JVM会把二进制的class文件加载到方法区,同时创建一个Class对象放入堆区。这个时候仅仅是完成了类的加载过程,之后才是创建对象。