反射学习笔记


在这里插入图片描述

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;
  ...
}

https://www.cnblogs.com/makai/p/11081879.html

在这里插入图片描述

https://www.cnblogs.com/wangflower/p/12238577.html

2)Class:

Class类也是一个实实在在的类,存在于JDK的java.lang包中。在内存中每个类有且只有一个相对应的Class对象Class类的对象作用:是运行时提供或获得某个对象的类型信息

类加载过程:

在这里插入图片描述

  • 加载:类加载过程的一个阶段:通过一个类的完全限定查找此类字节码文件,并利用字节码文件创建一个Class对象;
  • 链接:验证字节码的安全性和完整性,准备阶段正式为静态域分配存储空间,注意此时只是分配静态成员变量的存储空间,不包含实例成员变量,如果必要的话,解析这个类创建的对其他类的所有引用;
  • 初始化:类加载最后阶段,若该类具有超类,则对其进行初始化,执行静态初始化器和静态初始化成员变量。

https://www.cnblogs.com/zyly/p/10727511.html

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,父类泛型

  1. getSuperclass : 返回父类(class com.mark.test.Father)
  2. getGenericSuperclass : 带有泛型的父类,原返回值类型为Type,需要强转类型(com.mark.test.Father<com.mark.test.Son>)
  3. 获取父类的泛型的类型的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);
		}
}

6.获取该类实现的接口 getInterfaces() ,其他方法看api

一定要注意,getXxx,获取所有的public修饰的,包括从父类继承的(Constructor获取不到从父类继承的的),getDeclaredXxx获取当前类中所有权限修饰符的方法,包括private,但是获取不到父类继承的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值