1.什么是反射?—描述类的编译阶段
反射:
通过获取类或者接口的字节码文件对象,(正在运行的类)Class类对象,
然后获取类中构造方法所在的对象java.lang.reflect.Constructor,创建当前类实例
然后还可以获取类中的成员方法所在的java.lang.reflect.Field对象,然后去给成员变量赋值
还可以获取类中的成员方法所在的java.lang.reflect.Method对象,可以调用成员方法了!
1.1 如何获取Person类的字节码文件对象呢?
-
Object类的getClass()方法;
-
任意Java类型的class属性
-
在Class类中有一个静态功能(使用居多:参数为String类型–>可以放在配置文件中)
public static Class<?> forName(String className) throws ClassNotFoundException
参数是所需类的全限定名称(包名.类名)/** * @author Cipher * @date 2022/2/10 11:57 * 获取字节码文件的三种方式 * */ public class PersonDemo { public static void main(String[] args) throws ClassNotFoundException { Person p1 = new Person() ; System.out.println(p1); Person p2 = new Person() ; System.out.println(p2); System.out.println(p1 == p2); //false,都需要在堆内存中开辟空间 System.out.println("==========================================="); //方法1:Object类的getClass()方法 Class c1 = p1.getClass(); System.out.println(c1); Class c2 = p2.getClass() ; System.out.println(c2); System.out.println(c1 == c2); System.out.println("============================================"); //方法2:任意Java类型的class属性(最直接的) Class c3 = Person.class ; System.out.println(c3); System.out.println(c1 == c3); System.out.println(c2 == c3); System.out.println("==========================================="); //方法3: //forName()所需类的完全限定名称(包名.类名) Class c4 = Class.forName("com.qf.reflect.Person") ; System.out.println(c4);//class com.qf.reflect.Person System.out.println(c1 == c4); System.out.println("============================================="); //public String getName();获取字节码文件对象中的全限定名称 String name = c4.getName() ; System.out.println(name);//com.qf.reflect.Person } }
1.2 如何通过字节文件对象获取构造器对象Constructor,创建类的实例!
之前的写法:
创建一个Person类对象,无参构造方法公共的 类名 对象名 = new 类名() ;
现在:使用反射的方式来创建Person类的实例 1)获取Person类的字节码文件对象Class 2)获取构造方法Constructor类对象 getConstructors() :获取类中所有的公共的构造方法的类对象 getDeclaredConstructors() :获取当前类中所有的构造方法的类对象(包括私有) 3)通过构造方法Construcotr类对象来创建当前类的实例! 4)输出对象名称
import java.lang.reflect.Constructor; public class ReflectDemo { public static void main(String[] args) throws Exception { //之前的写法 Person p = new Person() ;//Person{name='null', age=0, gender='null'} System.out.println(p); System.out.println("====================="); //1)获取字节码文件对象 Class c = Class.forName("com.qf.reflect.Person") ; //2)获取指定的公共的无参构造方法 Constructor constructor = c.getConstructor() ; //3) 通过构造方法Constructor类对象来创建当前类的实例! //public T newInstance(Object... initargs):创建当前类的实例 ,参数可变参数, 描述给构造方法的实际参数 Object obj = constructor.newInstance(); System.out.println(obj); //obj---->相当上面的p对象 } }
1.3 如何通过反射的方式,获取带参数的构造方法,创建当前类的实例!
import java.lang.reflect.Constructor; public class ReflectDemo2 { public static void main(String[] args) throws Exception { //1)获取Person类的字节码文件对象 Class personClazz = Class.forName("com.qf.reflect.Person") ; //2)获取指定的构造方法的类对象Constructor //获取指定的构造方法,参数是参数类型的字节码文件对象 // private Person(String name,int age) Constructor constructor = personClazz.getDeclaredConstructor(String.class,int.class,String.class) ; //参数为true,取消java语言访问检查 constructor.setAccessible(true); Object obj = constructor.newInstance("张三",23,"女") ; System.out.println(obj); //Person{name='张三', age=23, gender='女'} } }
1.4 如何通过类的字节码文件对象获取成员变量所在的Field类对象,并对成员属性赋值!
import java.lang.reflect.Constructor; import java.lang.reflect.Field; /** * @author Cipher * @date 2022/2/10 14:30 * 如何通过类的字节码文件对象获取成员变量所在的Field类对象,并对成员属性赋值! */ public class ReflectDemo3 { public static void main(String[] args) throws Exception{ //1)获取字节码文件对象 Class clazz = Class.forName("com.qf.reflect.Person") ; //通过字节码文件对象获取公共的空参构造方法类对象 Constructor con = clazz.getConstructor() ; //通过con构造方法类对象创建当前类实例(Person) Object obj = con.newInstance();// //System.out.println(obj); //2)通过字节码文件对象获取指定的公共的字段的类对象Field Field nameField = clazz.getField("name"); //field的特有的方法-->给成员变量赋值 //参数1:当前指定的实例 //参数2:给成员变量实际参数 nameField.set(obj,"高圆圆"); //获取指定的字段类对象(包括私有,受保护的,默认的修饰符) //getDeclaredField(String name) Field ageField = clazz.getDeclaredField("age"); //取消java语言访问检查 ageField.setAccessible(true); //赋值 ageField.set(obj,43) ; System.out.println(obj); } }
2.反射的应用
2.1 有一个ArrayList集合,ArrayList,里面添加一些字符串元素 如何给ArrayList添加Integer类型的元素?
import java.lang.reflect.Method;
import java.util.ArrayList;
/**
* @author Cipher
* @date 2022/2/10 15:51
* 有一个ArrayList集合,ArrayList<String>,里面添加一些字符串元素
* 如何给ArrayList添加Integer类型的元素?
*/
public class ReflectArrayListDemo {
public static void main(String[] args) throws Exception {
//创建一个ArrayList<String>
ArrayList<String> arrayList = new ArrayList<>() ;
arrayList.add("hello") ;
arrayList.add("world") ;
arrayList.add("javaEE") ;
System.out.println(arrayList);
//通过反射获取ArrayList集合的字节码文件对象
//一共三种方式
Class clazz = arrayList.getClass() ;
System.out.println(clazz);
//获取当前的这个成员方法所在Method类对象
Method m = clazz.getMethod("add",Object.class) ;
//调用方法
m.invoke(arrayList,100) ;
m.invoke(arrayList,200) ;
System.out.println(arrayList);
}
}
2.2 已知两个类Student/Worker类,里面都有一个love()方法 在测试类Test2中,测试的时候,当需要学生类的时候,调用love->爱学习,爱生活,啥都爱!"
//学生类
public class Student {
public void love(){
System.out.println("爱学习,爱生活,啥都爱!");
}
}
// 工人类
public class Worker {
public void love(){
System.out.println("爱生活,爱java");
}
}
//classname.properties文件
className=com.qf.reflect_02.Worker
methodName=love
//测试类
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
public class Test2 {
public static void main(String[] args) throws Exception {
//1)读取src下面的配置文件classname.properties ---->获取src类路径下的资源文件所在的输入流
InputStream inputStream = Test2.class.getClassLoader() .getResourceAsStream("classname.properties") ;
//2) 创建一个属性列表集合类对象:Properties
Properties prop = new Properties() ;
//将配置文件中的内容加载到属性列列表集合中
prop.load(inputStream);
//3)获取className键对应的值
String className = prop.getProperty("className") ;
String methodName = prop.getProperty("methodName") ;
//4)获取当前className键对应的类型的字节码文件对象
Class clazz = Class.forName(className);
//直接创建当前类的实例
Object obj = clazz.newInstance();
//5)通过字节码文件对象获取成员方法Method对象
Method method = clazz.getMethod(methodName);
//6)调用方法
method.invoke(obj) ;
}
}