反射
反射:框架设计的灵魂,需重点掌握
-
框架:半成品软件。可以在框架的基础上进行软件开发,简化编码
-
反射:将类的各个组成部分封装为其它对象,这就是反射机制
- 优点
- 可以在程序的运行过程中,操作这些对象
- 可以解耦,提高程序的可扩展性
- 优点
-
获取Class对象的方式(字节码文件对象)
- 源代码阶段:Class.forName(“包名\类名”);将字节码文件加载进内存,返回Class对象
- Class类对象阶段:通过 类名.class,来获取Class对象
- 运行时阶段:对象.getClass();返回一个Class对象,getClass()方法在Object类中定义
-
使用场景
- Class.forName(全类名),多用于配置文件,将类名定义在配置文件中,读取文件,加载类。
- 类名.class,多用于参数的传递
- 对象.getClass(),多用于对象的获取字节码的方式
-
Class对象的功能
- 获取包含成员变量
- getFields():返回Class类所有public修饰的成员变量数组
- getField(String name):返回指定名称的public修饰的成员变量
- getDeclaredFields():返回Class类中所有的成员变量数组数组,包含私有和默认成员变量
- getDeclaredField(String name):返回指定名称的成员变量,包含私有和默认成员变量
package com.reflect; import com.domain.Person; import java.lang.reflect.Field; /** * @author: 小码农 * @create: 2021-08-10 15:51 **/ public class ReflectDemo2 { public static void main(String[] args) throws Exception { //获取Person的Class对象 Class<Person> pc1 = Person.class; //获取成员变量 //getFields()返回一个成员变量数组 Field[] fields = pc1.getFields(); for(Field field:fields){ System.out.println(field); } //getField()返回指定名称的public修饰的成员变量 Field a = pc1.getField("a"); //获取成员变量a的值 Person p = new Person(); Object value = a.get(p); System.out.println(value); a.set(p,"12345"); System.out.println(p); System.out.println("---------------"); Field[] declaredFields = pc1.getDeclaredFields(); for (Field declaredField:declaredFields){ System.out.println(declaredField); } Field d = pc1.getDeclaredField("d"); //使用private修饰的内容前,需要设置忽略全是修饰符的安全检查 d.setAccessible(true);//暴力反射 Object obj = d.get(p); System.out.println(obj); } }
- 获取包含构造方法
- getConstructor():返回指定传入参数类型的公共构造函数方法
- getConstructors():返回Class类的所有公共构造函数数组
- getDeclaredConstructor():返回指定传入参数类型的构造函数,包含私有和默认构造函数
- getDeclaredConstructors():返回Class类的所有构造函数数组,包含私有构造函数
package com.reflect; import com.domain.Person; import java.lang.reflect.Constructor; import java.lang.reflect.Field; /** * @author: 小码农 * @create: 2021-08-10 15:51 **/ public class ReflectDemo3 { public static void main(String[] args) throws Exception { //获取Person的Class对象 Class<Person> pc1 = Person.class; //getConstructor(String name) Constructor<Person> con1 = pc1.getConstructor(String.class,int.class); System.out.println(con1); //使用newInstance()方法创建对象 Person person = con1.newInstance("xm",11); System.out.println(person); Constructor<Person> con2 = pc1.getConstructor(); Person person2 = pc1.newInstance(); System.out.println(person2); } }
- 获取包含成员方法
- getMethod(String name,参数类型):返回指定名称、传参类型的公共成员方法
- getMethods():返回Class类的所有公共成员方法数组
- getDeclaredMethod(String name,参数类型):返回指定名称、传参类型的成员方法,包含默认私有
- getDeclaredMethods(String name,参数类型):返回Class类的所有成员方法数组,包含默认和私有
package com.reflect; import com.domain.Person; import java.lang.reflect.Constructor; import java.lang.reflect.Method; /** * @author: 小码农 * @create: 2021-08-10 15:51 **/ public class ReflectDemo4 { public static void main(String[] args) throws Exception { //获取Person的Class对象 Class<Person> pc1 = Person.class; //获取所有public修饰的方法getMethods() Method[] methods = pc1.getMethods(); for (Method method:methods){ System.out.println(method); } Method toString = pc1.getMethod("toString"); System.out.println(toString); //获取私有方法对象 Method eat = pc1.getDeclaredMethod("eat"); System.out.println(eat); //创建对象 Person p = new Person(); eat.setAccessible(true);//使用私有方法,需暴力反射 eat.invoke(p); //获取私有含参方法 Method eat1 = pc1.getDeclaredMethod("eat", String.class); eat1.setAccessible(true);//暴力反射 eat1.invoke(p,"小明"); //获取包含private修饰的方法 Method[] declaredMethods = pc1.getDeclaredMethods(); for (Method m:declaredMethods){ System.out.println(m); System.out.println(m.getName()); } } }
- 获取类名
- String getName():获取类名
public class ReflectDemo5 { public static void main(String[] args) throws Exception { //获取Person的Class对象 Class<Person> pc1 = Person.class; //Class类对象.getName()返回字符串形式的全类名 String name = pc1.getName(); System.out.println(name); } }
- 获取包含成员变量
暴力反射:使用private修饰的内容前,需要设置忽略全是修饰符的安全检查
-
对象.setAccessible(true);
-
Field:成员变量
- 操作:
- 设置值:set(Object obj,Object value)
- 获取值:get(Object obj)
- 操作:
-
Constructor:构造方法
- 操作:
- 创建对象:具体对象.newInstance(参数1,参数2…);
- 创建空参对象可以直接使用Class类的newInstance()方法,不用在找空参构造方法,然后再构造空参对象,Class类的newInstance()方法一步到位
- 同样如果使用私有构造方法创建对象,也需要提前进行暴力反射
- 操作:
-
Method:成员方法
- 操作:
- 使用方法:invoke(对象,方法需要传入的参数);当成员方法是无参方法时,传入参数为空
- 获取方法名称:方法对象.getName();返回字符串类型的方法对象名
- 对于私有成员方法,使用的前提是对先该方法进行暴力反射 方法对象.setAccessiable(true);
- 需要注意的是,使用使用Methods()方法返回public修饰的方法数组时,一些Class类继承的Object对象的方法也会存入返回的成员方法数组中
- 操作:
-
Class:获取类名
- 操作
- Class对象.getName():返回字符串形式的全类名
- 操作
结论
- 同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个,内存地址都指向同一个地址
Java代码在计算机中经历的三个阶段:
- 源代码阶段:一个包含成员变量、构造方法、成员方法的java文件通过编译命令javac形成一个class字节码文件,字节码文件中将成员变量、构造方法、成员方法分区
- Class类对象阶段:字节码文件通过类加载器ClassLoader将成员变量、构造方法、成员方法封装为包含成员变量的数组对象Field[],包含构造方法的数组对象Constructor[],包含成员方法的数组对象Method[]
- Runtime运行时阶段:在这个阶段,我们可以使用new 对象();从构造器对象数组中找到构造器创建新的对象,从成员变量数组中获取成员变量,从成员方法数组中使用成员方法。
案例:
- 需求:写一个"框架",在不改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
- 实现
- 配置文件
- 反射技术
- 步骤:
- 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
- 在程序中加载读取配置文件
- 使用反射技术来加载类文件进内存
- 创建对象
- 执行方法
package com.reflect;
import com.domain.Person;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* @author: 小码农
* @create: 2021-08-10 15:51
**/
public class ReflectTest {
public static void main(String[] args) throws Exception {
//要求实现:可以创建任意类的对象,可以执行任意方法
//1 加载配置文件
//1.1 创建Properties对象
Properties pro = new Properties();
//1.2 加载配置文件,转换为一个集合
//1.2.1 获取class目录下的配置文件方式
ClassLoader classLoader = ReflectTest.class.getClassLoader();//获取字节码文件的类加载器
InputStream is = classLoader.getResourceAsStream("Pro.Properties");
pro.load(is);
//2 获取配置文件中定义的数据
String className = pro.getProperty("className");//全类名
String methodName = pro.getProperty("methodName");//方法名
//3 创建Class对象
Class<?> cls = Class.forName(className);
//3.1 创建具体类对象
Object obj = cls.newInstance();
//3.2 创建方法对象
Method mn = cls.getDeclaredMethod(methodName);
mn.setAccessible(true); //暴力反射
//4 调用方法
mn.invoke(obj);
}
}
配置文件 Pro.Properties
className=com.domain.Student
methodName=sleep
Student类
package com.domain;
/**
* @author: 小码农
* @create: 2021-08-10 17:35
**/
public class Student {
public void sleep(){
System.out.println("sleep...");
}
}
Person类
package com.domain;
/**
* @author: 小码农
* @create: 2021-08-10 15:52
**/
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
private void eat(){
System.out.println("eat...");
}
private void eat(String s){
System.out.println(s+"eat...");
}
}