1.什么是反射技术
- Java的反射(reflection)机制是指在程序的运行状态中
- 可以构造任意一个类的对象
- 可以了解任意一个对象所属的类
- 可以了解任意一个类的成员变量和方法
- 可以调用任意一个对象的属性和方法。
- 这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制
- 核心:得到编译以后得class文件对象,提供了一个Class类型,就是编译后的class类对象
HelloWorld.java -> javac -> HelloWorld.class Class clz = HelloWorld.class
- 分类
- 类字节码 Class (本身也是一个类,是Java反射的源头)
- 构造器 Constructor
- 成员变量 Field
- 方法 Method
2.反射-获取类对象方式
public class ReflexTest { public static void main(String[] args) throws ClassNotFoundException { //1.类名.class 获取 Class<User> userClass1 = User.class; System.out.println(userClass1); //2.对象获取 User user = new User(); Class<? extends User> userClass2 = user.getClass(); System.out.println(userClass2); //3.全限定名称获取,Class.forName Class<?> userClass3 = Class.forName("com.lixiang.reflex.User"); System.out.println(userClass3); //4.通过classLoader类加载器 ClassLoader classLoader = ReflexTest.class.getClassLoader(); Class<?> userClass4 = classLoader.loadClass("com.lixiang.reflex.User"); System.out.println(userClass4); } } public class User { private String name; private int age; }
反射API比较多,宏观分类
- get+要获取的东西,例如:获取属性为getField()、获取方法为getMethod()
- 只能获取公有的东西
- 注意:getMethod可以获取到本类及其父类的方法
- get+Declared+要获取的东西,例如:获取属性为getDeclaredField()、获取方法为geDeclaredtMethod()
- 可以获取全部的东西
- 注意:getDeclaredMethod只能获取到本类的方法
3.反射-获取声明构造器
public class ReflexTest { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException { //1.类名.class 获取 Class<User> userClass = User.class; Constructor<?>[] declaredConstructors = userClass.getDeclaredConstructors(); //获取本类的全部构造方法 for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println(declaredConstructor.getName() + " "+ declaredConstructor.getParameterCount()); } System.out.println("--------------------"); //获取String类型的高燥方法 Constructor<User> declaredConstructor = userClass.getDeclaredConstructor(String.class); System.out.println(declaredConstructor.getName() + " "+ declaredConstructor.getParameterCount()); } }
4.反射-对象创建实战
- JDK9后用构造器创建对象,class.getDeclaredConstructor( ).newInstance( )
- 日常开发定义的POJO类里面,开发规范都推荐显示的写出空构造函数
- 一是方便通过反射创建对象 ,二是子类继承父类时,默认调用super( ) 保证父类有空构造函数
方法名 | 说明 |
T newInstance( ) |
根据类的空参的构造器创建对象,类必须提供空参的构造器和public权限 |
newInstance(Object...initargs) |
根据指定的构造方法创建对象 |
- 反射创建对象有多种方式,常用步骤如下
- 根据全类名获取对应的
Class
对象
- 调用指定参数结构的构造器,生成
Constructor
的实例 - 通过
Constructor
的实例创建对应类的对象,并初始化类属性
public class ReflexTest { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { //1.类名.class 获取 Class<User> userClass = User.class; //创建无参User对象 Constructor<User> userConstructor1 = userClass.getDeclaredConstructor(); User user = userConstructor1.newInstance(); System.out.println(user); System.out.println("-----------------------"); //创建一个参数的对象 Constructor<User> userConstructor2 = userClass.getDeclaredConstructor(String.class); User user1 = userConstructor2.newInstance("李祥"); System.out.println(user1); } }
5.反射-方法和属性实战
- 通过class获取方法
方法 | 说明 |
getMethods() |
获取当前运行类和 父类中声明的方法,需要是public访问权限的方法 |
getDeclaredMethods() |
获取当前运行时类中声明的全部方法,不包含父类中声明的方法 |
- 方法method的方法
方法 | 说明 |
getReturnType() |
获取全部的返回值 |
getParameterTypes() |
获取全部的参数 |
getModifiers() |
获取修饰符 |
getExceptionTypes() |
获取异常信息 |
public class ReflexTest { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { //获取类对象 Class<User> clazz = User.class; //获取当前运行类和 父类中声明的方法,需要是public访问权限的方法 Method[] methods = clazz.getMethods(); for(Method method:methods){ System.out.println("修饰符="+method.getModifiers()+",返回值="+method.getReturnType().getName()+",整体="+method); } System.out.println("——————————————————"); //返回当前类中所有的方法,无视修饰符 Method[] declaredMethods = clazz.getDeclaredMethods(); for(Method method:declaredMethods){ System.out.println("修饰符="+method.getModifiers()+",返回值="+method.getReturnType().getName()+",整体="+method); } } }
- 通过class对象获取属性
方法名 | 说明 |
getFields( ) |
获取当前运行类和 父类中声明的属性,需要是public访问权限的属性 |
getDeclaredFields( ) |
获取当前运行时类中声明的全部属性,不包含父类中声明的属性 |
- 属性Field的方法
方法 | 说明 |
getModifiers() |
整数形式返回此Field 的修饰符,整数对应在 java.lang.reflect.Modifier 里面 |
getType() |
返回 Field的属性类型 |
getName() |
返回 Field的名称 |
public class ReflexTest { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { //获取类对象 Class<User> clazz = User.class; //都是private,则获取不了; 属性改为public才行 Field[] fields = clazz.getFields(); for(Field field:fields){ System.out.println("属性名="+field.getName()+",属性类型="+field.getType().getName()+",属性修饰符="+field.getModifiers()); } System.out.println("——————————————————"); // 获取当前运行时类中声明的全部属性,不包含父类中声明的属性 Field[] declaredFields = clazz.getDeclaredFields(); for(Field field:declaredFields){ System.out.println("属性名="+field.getName()+",属性类型="+field.getType().getName()+",属性修饰符="+field.getModifiers()); } } }
6.反射-属性值操作实战
- 对反射进行相关操作,但如果构造器、方法、属性 没权限怎么操作?
- 可以通过
setAccessible(true)
,修改访问权限,Method和Field、Constructor对象都有setAccessible()方法
public class ReflexTest { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { //获取类对象 Class<User> clazz = User.class; //获取public空构造函数,并创建对象(把构造函数private私有化) Constructor<User> declaredConstructor = clazz.getDeclaredConstructor(); //修改访问权限,true表示暴力反射,攻破权限 declaredConstructor.setAccessible(true); User user = declaredConstructor.newInstance(); user.setAge(11); user.setName("李祥"); System.out.println(user); } }
- Field相关方法
方法 | 说明 |
get(Object obj) |
获取取指定对象obj 上此Field 的属性内容 |
set(Object obj,Object value) |
设置指定对象obj 上此Field 的属性内容 |
public class ReflexTest { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException { Class clazz = User.class; //创建运行时类的对象 //获取类对象 //获取public空构造函数,并创建对象 Constructor<User> declaredConstructor = clazz.getDeclaredConstructor(); //修改访问权限,true表示保留反射 declaredConstructor.setAccessible(true); //创建对象 User user = declaredConstructor.newInstance(); System.out.println(user.toString()); //获取运行时类中指定变量名的属性 Field name = clazz.getDeclaredField("name"); //保证当前属性是可访问的 name.setAccessible(true); //设置指定对象的的属性值 name.set(user,"李祥"); //打印对象的name属性值 System.out.println(name.get(user)); System.out.println(user.toString()); } }
7.反射-invoke运行类方法
- 运行类的指定方法步骤
- 获取class对象,创建对象
- 获取方法,invoke调用
什么是invoke调用 Object invoke(Object obj, Object … args)
- invoke的中文意思是【调用、召唤】
- 用来调用某个类中的方法的,但是它不是通过当前类直接去调用而是通过反射的机制去调用
- 参数说明:obj是调用类的实例对象, args:调用方的方法参数,是可变长度的
- Object 对应原方法的返回值,若原方法无返回值,此时返回null
- 如果原方法为静态方法,此时形参 obj可为null
- 如果原方法形参列表为空,则args为null
- 如果原方法声明为private,则需要在调用此invoke()方法前,调用对象的
- setAccessible(true)方法
- 编码实战
- User类中加入这三个方法
public class ReflexTest { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException { //获取类对象 Class<User> clazz = User.class; //获取public空构造函数,并创建对象 Constructor<User> declaredConstructor = clazz.getDeclaredConstructor(); //确保有访问权限,true表示暴力反射 declaredConstructor.setAccessible(true); User user = declaredConstructor.newInstance(); user.setName("李祥"); System.out.println("—————————调用普通方法———————————"); //获取指定的某个方法, 参数1,指明获取的方法的名称 参数2,指明获取的方法的形参列表 Method say = clazz.getDeclaredMethod("say", String.class); //保证当前方法是可访问的 say.setAccessible(true); //invoke调用,参数1 方法的调用者 参数2 给方法形参赋值的实参, // 返回值 是 对应类中调用的方法的返回值。 Object returnValue = say.invoke(user, "李祥"); System.out.println(returnValue); System.out.println("—————————调用静态方法———————————"); //获取指定的某个方法, 参数1,指明获取的方法的名称 参数2,指明获取的方法的形参列表 Method sleepMethod = clazz.getDeclaredMethod("sleep",String.class); //保证可以访问 sleepMethod.setAccessible(true); //调用静态方法,不需要获取类对象。 Object returnValue2 = sleepMethod.invoke(null,"李祥"); //如果调用的运行时类中的方法没有返回值,则此invoke()返回null System.out.println(returnValue2); } }