反射的用法
反射是Java中的一种高级技术,核心功能:可以让开发者动态的获取到一个类的全部组成部分并且灵活使用。
学习反射的目标:
(1)对于任何一个类,都可以获取它的全部的组成部分。
(2)对于类中的任何一个方法,任何一个成员变量都可以基于反射动态的进行调用和取值赋值。
反射第一步-获取类的Class对象
要获取类的各个组成部分需要先获取类的Class对象,获取类的Class对象有三种方式。
(1)Class 变量名 = 类名.class
Class<Person> personClassOne = Person.class;
System.out.println("personClassOne : " + personClassOne);
(2)基于Class类的静态方法public static Class forName(String package) : 传递全类名获取指定类的Class对象
//全类名:包名 + 类名 【包名之间的级别用.进行分隔】
//java.lang.ClassNotFoundException : 全类名写错了
Class<?> personClassTwo = Class.forName("com.itheima.entity.Person");
System.out.println("personClassTwo : " + personClassTwo);
(3)基于要获取Class对象的类的对象调用继承自Object类的getClass方法来获取
Person p = new Person();
Class<? extends Person> personClassThree = p.getClass();
System.out.println("personClassThree : " + personClassThree);
具体获取Class对象的时候不限制通过哪个方法来获取,能使用哪个方法就使用哪个方法。
反射第二步-获取类的构造方法与创建对象
获取类中的构造方法对象有四种方式
(1)Constructor[] getConstructors() : 获取所有的public修饰的构造方法对象组成的数组
Constructor<?>[] constructors = personClass.getConstructors();
(2)Constructor[] getDeclaredConstructors() : 获取所有的构造方法对象组成的数组
Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();
(3)Constructor getConstructor(Class... parameterTypes) : 基于传递的Class对象组成的参数列表匹配对应的构造方法对象并返回(public)
Constructor<Person> noArgsConstructor = personClass.getConstructor();
Constructor<Person> allArgsConstructor = personClass.getConstructor(String.class, Integer.class, String.class);
★(4)Constructor getDeclaredContructor(Class... parameterTypes) : 基于传递的Class对象组成的参数列表匹配对应的构造方法对象并返回
Constructor<Person> constructor = personClass.getDeclaredConstructor(String.class, Integer.class);
当获取到类的构造方法对象之后,可以基于构造方法创建对象。
但是如果本次使用的对象表示的类的组成部分是私有的(默认是不能直接使用),暴力反射(取消权限检查)
建议无论获取到什么对象,先进行暴力反射,保证一定可以进行使用,否则权限不满足会出现异常。
constructor.setAccessible(true); //暴力反射
public T newInstance(Object... initArgs) : 根据传递的实际参数调用调用者所表示的构造创建对象
Person person1 = noArgsConstructor.newInstance(); //无参构造newInstance的时候不传递参数
System.out.println(person1);
Person person2 = allArgsConstructor.newInstance("张二狗", 23, "男"); //有参构造newInstance的时候必须按照要求传递实参
System.out.println(person2);
反射第三步-获取类的成员变量与取值赋值
获取类中成员变量的方法有四种,但是由于类中的成员变量大部分情况都是私有的,所以这里总结两种。
(1)public Field[] getDeclaredFields() : 获取类中所有的成员变量Field对象组成的数组
Field[] fields = personClass.getDeclaredFields();
(2)public Field getDeclaredField(String name) : 获取指定名称的成员变量Field对象
Field nameField = personClass.getDeclaredField("name");
获取到类中的某个成员变量的Field对象之后,可以基于该Field对象获取该类某个对象的字段值或者给该类的某个字段赋值。
但是如果本次使用的对象表示的类的组成部分是私有的(默认是不能直接使用),暴力反射(取消权限检查)
赋值 public void set(Object obj,Object value) : 给指定对象的对应字段赋值
Field nameField = personClass.getDeclaredField("name"); //Person类的name字段
//赋值 public void set(Object obj,Object value) : 给指定对象的对应字段赋值
nameField.setAccessible(true);
nameField.set(person, "张二狗");
取值 public void get(Object obj) : 获取指定对象的指定字段值
Object name = nameField.get(person);
反射第四步-获取类的成员方法与调用方法
获取类的成员方法的方法有四种,这里总结常用的两种。
(1)public Method[] getDeclaredMethods() : 获取类中所有的成员方法对象组成的数组
Method[] methods = studentClass.getDeclaredMethods();
(2)public Method getDeclaredMethod(String name,Class... parameterType) : 基于传递方法名称与方法形参列表的Class对象获取对应的Method方法
Method sleepMethod = studentClass.getDeclaredMethod("sleep");
Method studyMethod = studentClass.getDeclaredMethod("study", String.class);
获取到类中的某一个成员方法Method对象之后,可以调用该Method对象表示的成员方法。
但是如果本次使用的对象表示的类的组成部分是私有的(默认是不能直接使用),暴力反射(取消权限检查)
public Object invoke(Object obj,Object... args) : 基于传递调用方法的对象和方法执行的实际参数来执行调用者表示的成员方法
//调用成员方法需要先准备一个对象类的对象 sleepMethod表示的是Student类的sleep方法,也只能被Student类对象调用
//调用成员方法需要思考调用该方法时传递的实际参数
sleepMethod.setAccessible(true);
sleepMethod.invoke(stu);
studyMethod.setAccessible(true);
studyMethod.invoke(stu, "JavaEE基础进阶");
反射的注意事项-泛型擦除
泛型就是可以在类中预先使用一个未知数据类型占位的技术。
泛型擦除,泛型的声明之后【限制类型的时间节点只在编码期】,实际代码运行之后泛型会被擦除掉。泛型就默认变成了Object。