反射
基本概念
什么是反射
在程序运行过程中分析类的一种能力;
程序运行先在.java源文件中编写源代码,然后通过编译将其编译成.class的字节码文件,此时就可以通过对象 . 的形式来调用具体的方法。把这个动作反过来就可以理解成反射。比如:现在调用某一个对象的某一个方法,此时要先获取到后缀名为.class的字节码文件,然后通过字节码文件来反问类中的构造方法、成员方法、成员变量
反射能做什么
①分析类 通过类加载器实现
类加载器ClassLoader :负责将类的字节码文件加载到内存中,并生成对应的Class对象
Class对象 :指的是java.lang.Class类的对象,也叫字节码文件对象,每个Class对象对应一个字节码文件
类的加载时机 :①创建类的实例 eg:Student stu=new Student(); 类加载器会将学生类的字节码文件加载到内存 中,如果再次编写这行代码来创建学生类的对象,那么类加载器不会再加载该类的字节码文件,因为一个类的字节码文件只会加载一次。
②访问类的静态成员:eg:调用Calendar.getInstance();方法,类加载器会先将Calendar类的字节码文件加载到内存中。
③初始化类的子类 eg:class User extends Person{} User user=new User();
先加载Person类的字节码文件,然后加载User类的字节码文件
④通过反射方式创建类的Class对象:Class class="Class".forName("类的正名");正名指的是包名+类名 eg:cn.itcast.demo1.Student
获取Class对象的三种方式 :①Object类中的getClass()方法,即所有类中都有这个方法
Class class=对象名.getClass();
②类的静态属性 Class class=类名.class;
③通过Class类的静态方法: Class class="Class".forName("类的正名");
加载并初始化一个类--->查看类的所有属性和方法
一个源文件.java对应一个字节码文件.class
package com.wang.reflect;
public class classDemo01 {
public static void main(String[] args) throws ClassNotFoundException {
//需求:获取Class对象
//1、getClass()方法创建字节码文件
Student stu=new Student();
Class class1=stu.getClass();
//2、类名.class的方式
Class class2=Student.class;
//3、Class class="Class".forName("类的正名");
Class class3=Class.forName("com.wang.reflect.Student");
//这三种方式获取到的字节码文件是同一个
System.out.println(class1.equals(class2));
System.out.println(class3.equals(class2));
}
}
class Student{
}
②查看并使用对象 查看一个对象的所有属性和方法,使用对象的任意属性和方法
A.反射方式获取构造方法并使用
Constructor<T> 对象 :构造器对象,属于java.base模块,java.lang.reflect包下的类
通过Class对象获取构造器对象:a、getConstructor(Class<?>...parameterTypes):返回一个Constructor对象,只
能获取公共的构造函数。Class<?>...:可变参数,代表Class类型的数组
?:通配符,代表不确定的任意类型
b、getDeclaredConstructor(Class<?>...parameterTypes):返回一个Constructor
对象,可以获取私有构造函数
c、getConstructors() 获取该类中所有的(不含私有)构造函数的数组
Constructor的常用方法:a、String getName(); 返回构造函数名
b、T newInstance(Object... initargs):使用此构造函数和指定参数创建并初始化对象
package com.wang.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class constructorDemo01 {
public static void main(String[] args) throws Exception {
//通过反射方式创建Student1类型的对象
//1、获取Student1类的字节码文件
Class clazz=Student1.class;
// 2、根据字节码文件,获取指定的构造器对象
/*获取公共的无参构造
Constructor con1=clazz.getConstructor();
System.out.println(con1);
*/
//获取公共的有参构造
Constructor con2=clazz.getConstructor(String.class);
System.out.println(con2);
/*获取所有的非私有构造方法
Constructor[] cons=clazz.getConstructors();
for (Constructor con:cons) {
System.out.println(con);
}
*/
//获取私有的构造方法
Constructor con3=clazz.getDeclaredConstructor(int.class);
System.out.println(con3);
//获取构造器的名字,看看它是那个类的构造器
String name=con3.getName();
System.out.println(name);
//3、根据构造器对象和参数,创建对象
Student1 stu=(Student1) con2.newInstance("王迎婧");
System.out.println(stu);
//打印结果
}
}
class Student1{
public Student1(){}
public Student1(String name){
System.out.println("您输入的name的值是:"+name);
}
private Student1(int age){
System.out.println("您输入的age的值是:"+age);
}
}
B、反射方式获取成员方法并使用
Method对象 :方法对象,属于java.base模块,java.lang.reflect包
通过Class对象获取方法:a、getMethod(String name,Class<?>...parameterTypes):返回一个Method对象,仅
公共成员方法。name:要获取的方法名 parameterTypes:方法的参数列表
b、getDeclaredMethod(String,class<?>...):返回一个Method对象,可获取私有方法
c、getMethods():返回此类所有(不含私有)方法的数组
Method常用方法:a、String getName():返回方法名
b、Object invoke(Object obj,Object... args) :在指定对象上调用此方法,参数为args
obj是对象名
package com.wang.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Map;
public class methodDemo01 {
public static void main(String[] args) throws Exception{
//需求:反射方式获取Student2类中的成员方法并调用
//1、获取Student2类的字节码文件
Class clazz=Student2.class;
//2、获取该类的构造器对象,然后创建Student2类的对象
Constructor con =clazz.getConstructor();
Student2 stu=(Student2)con.newInstance();
System.out.println(stu);
//3、获取该类的成员方法对象,然后调用此方法
Method method=clazz.getMethod("show");
System.out.println(method.getName());//打印方法名
//调用方法
method.invoke(stu);//方法返回时void,不需要变量做接收
System.out.println("============================================");
Method method1=clazz.getMethod("show2", int.class);
System.out.println(method1.getName());
method1.invoke(stu,3);
System.out.println("===============================================");
Method method2=clazz.getDeclaredMethod("show3", int.class, int.class);
System.out.println(method2.getName());
//因为是私有方法,所以要用暴力反射
method2.setAccessible(true);
int i= (int) method2.invoke(stu,2,3);//不能用int接收Object类型的数据
System.out.println("输入的两个数之和为:"+i);
//获取类中所有的非私有成员方法
Method[] methods=clazz.getMethods();
for (Method m:methods) {
System.out.println(m);
}
}
}
class Student2{
public Student2(){}
public void show(){
System.out.println("我是公有的空参方法");
}
public void show2(int a){
System.out.println("我是公共的带参方法,您传入的a的值为:"+a);
}
private int show3(int a,int b){
System.out.println("我是带参的私有方法");
return a+b;
}
}
需求:通过反射获取类的setter方法,使用该方法为属性赋值
分析:setter方法的方法名由set和属性名(首字母大写)组成:setName
setter方法有且只有一个参数,参数类型为属性的类型:setName(String name)
setter方法为public修饰的方法,反射获取该方法使用getMethod(class<?>...);
根据上述分析分别为属性name、age赋值并使用
package com.wang.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
public class methodDemo02 {
public static void main(String[] args) throws Exception{
//需求:通过反射获取类的setter方法,使用该方法为属性赋值
Class clazz=Student3.class;
Constructor con=clazz.getConstructor();
Student3 stu=(Student3)con.newInstance();
//System.out.println(stu);
Method method=clazz.getMethod("setName", String.class);
Method method1=clazz.getMethod("setAge", int.class);
method.invoke(stu,"王迎婧");
method1.invoke(stu,23);
System.out.println(stu);
}
}
class Student3{
private String name;
private int age;
public Student3(){}
public Student3(String name,int age){
this.name=name;
this.age=age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
C、反射方式获取成员变量并使用
Field对象 : 域(属性、成员变量)对象,属于java.base模块,java.lang.reflect包
通过Class对象获取属性 getField(String name) 返回一个Field对象,仅公共属性。
getDeclaredField(String name) 获取私有属性
getDeclaredFields() 返回此类所有(含私有)属性的数组
Field的常用方法:void set(Object obj,Object value) 设置obj对象的指定属性为value
void setAccessible(boolean flag) 将此属性的可访问性设置为指定布尔值 即:是否开启暴力 反射
package com.wang.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class fieldDemo01 {
public static void main(String[] args) throws Exception {
//通过反射获取成员变量并使用
//1、先获取Student类的字节码文件
Class clazz=Student4.class;
//2、通过字节码对象获取构造器对象,然后创建学生类对象
Constructor con=clazz.getConstructor();
Student4 stu=(Student4)con.newInstance();
Student4 stu1=(Student4)clazz.getConstructor().newInstance();//第二种写法 链式编程
System.out.println(stu);
System.out.println(stu1);
//3、设置学生对象的各个属性值
Field field1=clazz.getField("name");
field1.set(stu,"王迎婧");
field1.set(stu1,"王迎婧");
Field field=clazz.getDeclaredField("age");
field.setAccessible(true);//开启暴力反射
field.set(stu,23);
field.set(stu1,22);
System.out.println(stu);
System.out.println(stu1);
}
}
class Student4{
public Student4(){}
public String name;
private int age;
反射的使用场景
①构建通用的工具
②搭建具有高度灵活性和拓展性的系统框架