文章目录
- 1.java的反射机制:
- 2、反射机制的实现实质:Class
- 2.2 因为每个类都是一个Class对象,想操作类,就要先获取类,所以要获取与其映射的Class对象
- 1.获取类的Class对象
- 》》》获取当前Class的 泛型
- 》》》获取父类Class,父类带泛型的Class,父类泛型
- 2.暴力反射
- 3.根据Class对象创建实例(获取构造方法)
- 4.根据Class对象获取属性(FIeld类修改或获取实体对象的属性)
- 5.根据Class对象获取方法(Method类调用实体对象方法)
- 6.获取该类实现的接口 getInterfaces() ,其他方法看api
- 一定要注意,getXxx,获取所有的public修饰的,包括从父类继承的(Constructor获取不到从父类继承的的),getDeclaredXxx获取当前类中所有权限修饰符的方法,包括private,但是获取不到父类继承的
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/462e6fdcca17d530a63d0997776896e1.png)
1.java的反射机制:
JAVA反射机制是在运行状态中,
对于任意一个实体类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意方法和属性;
这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制**
2、反射机制的实现实质:Class
api中描述Class类:
Class类的实例表示正在运行的 Java 应用程序中的类和接口。
(理解:所有的类和接口都是是Class类的实例)在加载.class
文件时会根据.class
文件在堆中创建一个Class类的对象 与 该类或接口 相映射
枚举是一种类,注解是一种接口。(理解:枚举和注解也是一个Class对象)
每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。
2.1 ClassLoader和Class
1)ClassLoader
顾名思义,它是用来加载 Class 的。它负责将 Class 的字节码形式转换成内存形式的 Class 对象。字节码可以来自于磁盘文件 *.class,也可以是 jar 包里的 *.class,也可以来自远程服务器提供的字节流,字节码的本质就是一个字节数组 []byte,它有特定的复杂的内部格式。
有很多字节码加密技术就是依靠定制 ClassLoader 来实现的。先使用工具对字节码文件进行加密,运行时使用定制的 ClassLoader 先解密文件内容再加载这些解密后的字节码。
每个 Class 对象的内部都有一个 classLoader 字段来标识自己是由哪个 ClassLoader 加载的。
class Class<T> {
...
private final ClassLoader classLoader;
...
}
2)Class:
Class类也是一个实实在在的类,存在于JDK的java.lang包中。在内存中每个类有且只有一个相对应的Class对象Class类的对象作用:是运行时提供或获得某个对象的类型信息
类加载过程:
- 加载:类加载过程的一个阶段:通过一个类的完全限定查找此类字节码文件,并利用字节码文件创建一个Class对象;
- 链接:验证字节码的安全性和完整性,准备阶段正式为静态域分配存储空间,注意此时只是分配静态成员变量的存储空间,不包含实例成员变量,如果必要的话,解析这个类创建的对其他类的所有引用;
- 初始化:类加载最后阶段,若该类具有超类,则对其进行初始化,执行静态初始化器和静态初始化成员变量。
2.2 因为每个类都是一个Class对象,想操作类,就要先获取类,所以要获取与其映射的Class对象
1.获取类的Class对象
1、根据对象,对象名.getClass();
2、根据类名,类名.class
3、根据Class工具类的静态方法,Class.forName("全类名")
注意:类的完全限定名/类的全名 指src下开始的包的路径。forName()方法需要抛异常,找不到类异常,ClassNotFountException
Class的泛型加上后,newInstance()时不用手动强转。
举例:
//1
Student s = new Student();
Class<? extends Student> clazz = s.getClass();
//2
Class<?> clazz = Student.class;
//3
Class<?> clazz = Class.forName("com.reflect.test.Student");
》》》获取当前Class的 泛型
根据当前Class对象的getTypeParameters获取当前泛型
public static void main(String[] args) {
Class<Son> clazz = Son.class;
TypeVariable<Class<Son>>[] typeParameters = clazz.getTypeParameters();
Arrays.stream(typeParameters).forEach(System.out::println);
}
Son类的泛型Integer
结果:Integer
》》》获取父类Class,父类带泛型的Class,父类泛型
- getSuperclass : 返回父类(class com.mark.test.Father)
- getGenericSuperclass : 带有泛型的父类,原返回值类型为Type,需要强转类型(com.mark.test.Father<com.mark.test.Son>)
- 获取父类的泛型的类型的Class:将带泛型的父类强转成参数化类型(ParameterizedType pt = (ParameterizedType) clazz.getGenericSuperclass();)
然后通过parameterizedType获取实际参数类型(Type type = pt.getActualTypeArguments()[0];)
2.暴力反射
1.通过Class对象中的getDeclaredXxx()方法获取类中所有的成员,不包括从父类继承的(属性,方法,内部类)
2.修改对该成员的访问权限
成员变量,构造方法,成员方法都是AccessibleObject类型的子类,具有判断是否可以访问 和 设置是否可以访问的方法。
isAccessible():判断当前对象是否可以被访问
setAccessible(boolean flag):true,设定当前对象可以被访问。false相反。
setAccessible(true)后,私有成员也可以被访问和修改
3.根据Class对象创建实例(获取构造方法)
根据指定类的Class对象创建一个该类的新的对象
方法1:根据Class对象直接newInstance();调用该类无参构造方法创建一个新的对象。
注意:clazz的newInstance()返回值是Object,要求必须有一个无参构造方法
Class<?> clazz = Class.forName("com.reflect.test.Student");
Student stu = (Student)clazz.newInstance();
方法2:获取构造方法,用指定构造方法创建对象。
注意:
getConstructors();//获取student的所有public修饰的构造方法
getDeclaredConstructors();//获取全部构造方法,包括private
getConstructor(String.class , int.class);//根据指定的参数获取构造方法,参数必须也是参数类型对应的Class对象
getDeclaredConstructor(String.class,int.class);//根据参数获得构造方法(所有修饰符都可以)
Class clzz = Student.class;
//获取构造方法
Constructor[] cons = clzz.getConstructors(); 获取student的所有public修饰的构造方法
Constructor[] cons1 = clzz.getDeclaredConstructors(); 获取全部构造方法,包括private
Constructor<?> con1 = s1.getConstructor(String.class,int.class); 根据指定的参数获取构造方法(只有public修饰的)
Constructor<?> con2 = s1.getDeclaredConstructor(String.class, int.class , int.class); 根据参数获得任何修饰符修饰的构造方法(包括private)
//根据构造方法创建对象
1.如果是public修饰的构造方法(有权限调用,直接调用)
Student stu1 = (Student) con1.newInstance("张三",14);//有参数直接加参数就可以
2.如果是private修饰的构造方法(没权限调用,暴力反射)
con2.setAccessible(true);//设置为允许访问
Student stu2 = (Student) con2.newInstance("张三",14,15);
4.根据Class对象获取属性(FIeld类修改或获取实体对象的属性)
-
根据Class对象获取属性
getField(String name),getDeclaredField(String name) 根据属性名获取属性
getFields(),getDeclaredFields() 获取所有的属性
注意:
getXxx:只能获取public修饰的所有属性,包括父类的属性;
getDeclaredXxx:只能获取本类的所有权限符修饰的属性,包括private,不包括父类的。
用declared不declared看需求,如果要用从父类继承的就不加declared -
使用Field类,获取或设置实体对象的属性
setAccessible(boolean flag);true:开启访问权限
get(Object o)
:返回实体对象上此 Field对象 表示的字段的值。
set(Object obj, Object value)
;将实体对象的Field对象表示的字段 设置为指定的新值
getName getType声明类型,数据类型 getModifiers权限修饰符
用法:
//1获取实体类的Class对象
Class<Student> clazz = Student.class;
//2创建实体对象
Student student = clazz.newInstance();
//3 获取指定属性的Field对象
Field nameField = clazz.getDeclaredField("name");用declared不declared看需求,如果要用从父类继承的就不加declared
//4 设置权限,如果不用暴力反射就略过这一步
nameField.setAccessible(true);
//5 用指定属性的Field对象设置实体对象中该属性的值
nameField.set(student, "小螺号滴滴滴吹");
//6 获取实体对象的指定属性的值
String studentName = (String) nameField.get(student);
System.out.println(studentName);
//7 验证该对象的属性值
System.out.println(student);
Student实体类
class Student{
private String name;
@Override
public String toString() {
return "Student [name=" + name ]";
}
}
5.根据Class对象获取方法(Method类调用实体对象方法)
-
根据Class对象获取方法,或调用该对象的方法
getMethod(String name,Class<?>... paramTypes)
/getDeclaredMethod(String name,Class<?>... paramTypes)
根据方法名,和参数数据类型的Class对象获取方法(注意:如果没参数,不写参数或者写null)
getMethods()
/getDeclaredMethods()
;获取所有的方法。返回Method数组 -
使用Method类 调用该对象的方法
setAccessible()
invoke(Object obj, Object… args) obj对象,args参数
注意:没有参数写null或不写。静态方法的调用 对象参数写null,有返回值的方法invoke返回相同的数据,没有返回值返回null
getModifiers() ,getName() ,getParameterTypes() ,getReturnType()
这些方法都是顾名思义的
用法:
public static void main(String[] args) throws Exception {
Class<MethodClass> clazz = MethodClass.class;
MethodClass mc = clazz.newInstance();
Method f1 = clazz.getMethod("fun1", int.class);
System.out.println("有返回值,返回:"+f1.invoke(mc, 1));
Method f2 = clazz.getDeclaredMethod("fun2", int.class); //后边有参数,加参数的Class对象。默认的不用设置Accessible
System.out.println("无返回值,返回:"+f2.invoke(mc, 2));//没有返回值,返回null
Method f3 = clazz.getDeclaredMethod("fun3");//没有参数可以直接用方法名,后边不写参数,也可以写null
f3.setAccessible(true);
String f3String = (String) f3.invoke(mc);
System.out.println("有返回值,返回:"+f3String);
//如果没有参数,获取方法的时候最好不加
Method f4 = clazz.getDeclaredMethod("fun4",null);//Type null of the last argument to method getDeclaredMethod(String, Class<?>...) doesn't exactly match the vararg parameter type. Cast to Class<?>[] to confirm the non-varargs invocation, or pass individual arguments of type Class<?> for a varargs invocation.
System.out.println("无返回值,返回:"+f4.invoke(mc));//静态方法可以加对象,可以用null
Method f5 = clazz.getDeclaredMethod("fun5", int.class);
System.out.println("有返回值,返回:"+f5.invoke(null,1));//静态方法第一个参数必须加,加对象,或者加null
//getMethod和getDeclaredMethod
System.out.println("-------------------------getMethod和getDeclaredMethod----------------------------------");
Class<A> clazz1 = A.class;
// Method[] methods = clazz1.getDeclaredMethods();//所有权限修饰符的本类的方法,不包括从父类继承的
Method[] methods = clazz1.getMethods();// 只有public修饰的,包括从父类继承的,父父类的.
for (Method method : methods) {
System.out.println(method);
}
}