文章目录
前言
刚开始学习反射和动态代理会很迷,没关系,等有一定知识积累就可以看下面博客:
高级知识:反射机制详解:什么叫做运行时动态的加载操作类
看完这篇博客你就知道反射究竟在干什么了,就能理解里面的逻辑了。
一、反射
1、 什么是反射?
- 应用场景:IDEA里面的自动语法提示一个类有哪些成员变量、成员方法;一个方法里面的参数类型提示这些功能都是利用反射的功能实现的。
看到这里你应该就明白反射的应用场景了,每次就是在书写封装各种框架会用到。 - 什么是反射?一句话,就是能够动态访问类里面成员变量、方法的所有信息。
【注】:这里的获取我们不是从java文件中获取的,而是从其对应的class字节码文件中获取的。所以先要学习怎么获取class字节码文件的对象。
在java中也可以说万物都是对象:
- 字节码对象:封装在Class类
- 构造方法对象:封装在Constructor类
- 成员变量(字段):封装在Field类
- 成员方法:封装在Method类
2、 反射代码学习
先准备一个标准的javabean类
package cn.hjblogs.reflect;
public class Student {
private String name;
private int age;
public String gender;
public Student() {
}
public Student(String name) {
this.name = name;
}
protected Student(int age) {
this.age = age;
}
private Student(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
public void sleep() {
System.out.println("sleep...");
}
private String eat(String food) throws IOException,NullPointerException {
System.out.println("eat " + food);
return "奥利给";
}
}
(1)获取class对象的三种方式
-
Class.forName(“全类名”): 将字节码文件加载进内存,返回class对象
多用于配置文件,将类名定义在配置文件中。读取文件,加载类 -
类名.class: 通过类名的属性class获取
多用于参数的传递 -
对象.getclass(): getclass()方法在object类中定义着
多用于对象已经创建的获取字节码的方式
参考视频,关于这三个方法对应的三个阶段,这个视频讲的比较清晰。 -
结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的class对象都是同一个。
package cn.hjblogs.demo2;
public class Test {
/*
* 获取class对象的三种方式:
* 1. Class.forName("全类名"): 将字节码文件加载进内存, 返回class对象
* 2. 类名.class: 通过类名的属性class获取
* 3. 对象.getClass(): getClass()方法在Object类中定义着
*/
public static void main(String[] args) throws ClassNotFoundException {
// 1. 第一种方式:Class.forName("全类名")
// 全类名:包名 + 类名
// 最为常用,一般都是用这种方式
Class<?> clazz1 = Class.forName("cn.hjblogs.demo2.Student");
System.out.println(clazz1); // class cn.hjblogs.demo2.Student
// 2. 第二种方式:类名.class
// 更多的是当做参数传递
Class<Student> clazz2 = Student.class;
System.out.println(clazz2); // class cn.hjblogs.demo2.Student
// 3. 第三种方式:对象.getClass()
// 需要先创建对象,使用局限性大
Student student = new Student();
Class<? extends Student> clazz3 = student.getClass();
System.out.println(clazz3); // class cn.hjblogs.demo2.Student
System.out.println("----------------------");
System.out.println(clazz1 == clazz2); // true
System.out.println(clazz1 == clazz3); // true
// 上面的泛型都可以不加,这里都是Ctrl+Alt+V自动补全的
// Class clazz1 = Class.forName("cn.hjblogs.demo2.Student");
// Class clazz2 = Student.class;
// Class clazz3 = student.getClass(); 这样都是可以的
}
}
(2)反射获取构造方法并从构造方法Constructor对象中获取构造方法详细信息
Class类中用于获取构造方法的方法
方法 | 说明 |
---|---|
Constructor<?>[] getConstructors() | 返回所有public修饰的构造方法对象的数组 |
Constructor<?>getDeclaredConstructors() | 返回所有构造方法对象的数组 |
Constructor<T>getConstructor(Class<?>… parameterTypes) | 返回某个公共构造方法对象(具体返回的是哪一个就要看你括号里面传的参数和哪一个构造方法是一一对应的,不过这里传的是对应类型的字节码文件) |
Constructor<T> getDeclaredConstructor(Class<?>… parameterTypes) | 返回某个构造方法对象(具体返回的是哪一个就要看你括号里面传的参数和哪一个构造方法是一一对应的,不过这里传的是对应类型的字节码文件) |
Constructor类中获取构造方法详细各种信息的方法,详细的有很多方法见官方文档
方法 | 说明 |
---|---|
int getModifiers() | 获取构造方法对象中的权限修饰符(这里返回的是int,Java中权限修饰符有对应常量对应) |
Class<?>[] getParameterTypes() | 返回该构造方法的所有参数类型 |
T newInstance(Object…initargs) | 根据指定的构造方法创建对象(需要注意的是注意构造方法的修饰符,如果是私有修饰可能会抛出错误不让创建) |
setAccessible(boolean flag) | 设置为true,表示取消访问检查(暴力反射时用) |
反射中权限修饰符对应的常量数字表
package cn.hjblogs.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Test {
/**
* Class类中用于获取构造方法的方法
* Constructor<?>[] getconstructors() : 获取所有public修饰的构造方法
* Constructor<?>[] getDeclaredconstructors() : 获取所有的构造方法
* Constructor<T> getConstructor(class<?>... parameterTypes) : 获取指定参数列表的public修饰的构造方法
* Constructor<T> getDeclaredConstructor(Class<?>...parameterTypes) : 获取指定参数列表的构造方法
*/
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 1 获取Student的字节码Class对象
Class<?> clazz = Class.forName("cn.hjblogs.reflect.Student");
// 2 获取构造方法
// 获取所有public修饰的构造方法
Constructor<?>[] cons1 = clazz.getConstructors();
for (Constructor<?> con : cons1) {
System.out.println(con);
}
/*
输出:
public cn.hjblogs.reflect.Student()
public cn.hjblogs.reflect.Student(java.lang.String)
*/
// 获取所有的构造方法
Constructor<?>[] cons2 = clazz.getDeclar