反射笔记
一、反射初接触
-
定义:将类中的各个组成部分封装为对象
比如:
- 构造器对象Constructor
- 成员变量对象Field
- 成员方法对象Method
-
好处
- 可以在程序运行过程中操作这些对象
- 解耦合,提高程序的可扩展性
-
获取类的Class对象的三种方式
- 【SOURCE 源代码阶段】
Class.forName("packageName.className");
将字节码文件加载如内存,返回Class对象【路径是全路径】 - 【CLASS 类加载器已经执行】
className.class;
:已经编译,通过类名的属性class获取Class对象 - 【RUNTIME 运行期实例阶段】
Object.getClass();
已经创建了对象,使用对象的方法getClass() 获取Class对象【这是Object类定义的方法】
- 【SOURCE 源代码阶段】
-
实现
-
//Demo01Reflect.java //1. 获取class对象第一种方式: Class.forName() 【已经编译】 //注意!!参数为字符串类型,传递的是类的全路径 Class personClass = Class.forName("com.rapjoee.day01.domain.Person"); System.out.println("第一种方式:" + personClass); //2. 第二种方式:类名.class Class personClass1 = Person.class; System.out.println("第二种方式:" + personClass1); //3.第三种方式: 已经创建了对象,则使用对象的getClass()方法 Person person0 = new Person(); Class personClass2 = person0.getClass(); System.out.println("第三种方式:" + personClass2); //判断下几个class对象是否是同一个 【是同一个】 //true System.out.println(personClass == personClass1); System.out.println(personClass2 == personClass1); 复制代码
-
-
备注
- 同一个字节码文件(.class)在一次出现运行过程中,只会加载一次,不论哪种方式获得的Class对象是同一个【不同的字节码文件对应的Class对象是不同的】
- 适用性
- 第一种方式多应用于配置文件。可以把类名定义在配置文件中,读取文件。传递的是字符串
- 第二种方式多用于参数的传递
- 第三种方式多用于已经获取对象后,对象的字节码文件获取
二、Class对象的功能之一 -- 获取类的成员变量们
-
通过类的Class对象可以获取其成员变量的Field对象
-
方法
Field getField(String name)
:获取指定名称的public修饰的成员变量**【此类方法不能获取public关键字之外修饰的成员变量】**Field[] getFields()
:获取所有public修饰的成员变量 获取后的操作: 1. 设置值:Object get(Object obj)
返回该所表示的字段的值 Field ,指定的对象上。 2. 获取值:void set(Object obj, Object value)
将指定对象参数上的此 Field对象表示的字段设置为指定的新值。 3.setAccessible(boolean x)
参数为true则忽略访问修饰符的安全检查【暴力反射】Field getDeclaredField(String name)
:获取指定名称的成员变量,不考虑修饰符Field[] getDeclaredFields()
:获取所有的成员变量,不考虑修饰符
-
实现
//获取Person类的Class对象 Class personClass = Person.class; //获取成员变量的方法 //Field getField(String name):获取指定名称的public修饰的成员变量 Field a = personClass.getField("a"); System.out.println("getField():" + a); //对获取得到的成员变量进行操作 Person person0 = new Person(); a.set(person0, "Smith"); Object o = a.get(person0); System.out.println("get():" + o + " person0:" + person0); //Field[] getFields() Field[] fields = personClass.getFields(); System.out.println("getFields():" + Arrays.toString(fields)); //对获取的成员变量值进行操作 System.out.println("=========================================="); //Field getDeclaredField(String name) Field[] declaredFields = personClass.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println(declaredField); } //Field[] getDeclaredFields() Field name = personClass.getDeclaredField("name"); //忽略访问修饰符的安全检查,不然下面的代码会抛出异常【private修饰中】 //暴力反射 name.setAccessible(true); name.set(person0, "吐槽星人"); System.out.println("name.get(person1):" + name.get(person0)); System.out.println("name:" + name); //Person类的成员变量 private String name; private int age; public String a; public String b; 复制代码
三、Class对象的功能之二 -- 获取类的构造器们
-
通过类的Class对象获取类的构造器对象Constructor
-
方法
-
Constructor<T> getConstructor(class<?>... parameterTypes)
-
获取指定参数类型的被public关键字修饰的构造器
-
比如获取
public Person(String name, int age){}
构造器的写法为-
Constructor cons = personClass. getConstructor(String.class, int.class); 复制代码
-
-
-
Constructor<?>[] getConstructors()
:获取被public修饰的构造器们 -
Constructor<T> getDeclaredConstructor(class<?>... parameterTypes)
:获取所有的指定参数类型的构造器,不考虑访问控制修饰符 -
Constructor<?>[] getDeclaredConstructors()
:获取所有的构造器,忽略修饰符 -
获取构造器之后可以进行的操作: 创建对象
-
T newInstance(Object... initargs)
使用此 Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例。
-
这里我们知道,如果使用空参构造创建对象,可以使用newInstance()方法进行构造达到一样的效果
Object o = constructor.newInstance("Smith", 123); 复制代码
-
-
-
实现
//获取一个Class对象 Class personClass = Person.class; // Constructor<T> getConstructor(类<?>... parameterTypes) //构造器的区别就是传递的参数不同,这里我们对该方法传递String 和 int Constructor constructor = personClass.getConstructor(String.class, int.class); System.out.println("constructor --> " + constructor); //获取构造器后,对其操作 //创建对象 //这里如果获取的 constructor 在第34行没有参数,则此处也没有参数,就是一个空参的构造器 Object o = constructor.newInstance("Smith", 123); System.out.println("o --> " + o); //Person类的构造器 private Person(String a) { this.a = a; } public Person(String name, int age) { this.name = name; this.age = age; } 复制代码
四、Class对象的功能之三 -- 获取类的类/包与方法们
-
通过类的Class对象获取类的构造器对象Method
-
方法
Method getMethod(String name, class<?>... parameterTypes)
:获取指定名称与指定参数类型的方法Method[] getMethods()
Method getDeclaredMethod(String name, class<?>... parameterTypes)
Method[] getDeclaredMethods()
:获取所有的方法【包括自定义类中的与Object类定义的】- 获取方法对象后可以执行的操作
Object invoke(Object obj, Object... args)
:Method类的invoke方法可以用来执行方法。其参数:- Object obj:真实的对象
- Object... args:参数列表
String getName()
:获取方法的名称
String getName()
获取的是类的全路径【com.rapjoee.day01.domain.Person 】packageName getPackage()
获取这个类的全包名【 package com.rapjoee.day01.domain 】
-
实现
Class personClass = Person.class; // Method getMethod(String name, class<?>... parameterTypes) //获取指定名称的方法,传递1.方法名 2. 参数列表可变参数【一个方法的要素:方法名、参数】 //获取一个无参的方法Method对象 Method speak = personClass.getMethod("speak"); //对获取的Method对象进行操作【使用】 //先准备一个对象 Object person = personClass.newInstance(); speak.invoke(person); System.out.println("======================================="); //获取到一个带参的Method对象 //方法参数类型是String ,所以获取时传递参数类型为String.class Method eat = personClass.getMethod("eat", String.class); //调用Method类的invoke方法执行方法eat,传递eat方法的参数 eat.invoke(person, "Apple"); //Person类的两个测试方法 public void eat(String food) { System.out.println("The eat method is executed... --> " + food); } public void speak() { System.out.println("The speak method is executed..."); } 复制代码