一? 反射的概述
- 什么是反射?在程序运行中分析类的一种能力?
?反射能做什么?(1)分析类:加载并初始化一个类,查看类的所有属性和方法 (2)查看并使用对象:查看一?个对象的所有属性和方法,使用对象的任意属性和方法?
- 反射的应用场景:(1)构造通用的工具类时 (2)搭建具有高度灵活性和扩展性的系统框架
二? 类加载器(ClassLoader)
- 作用:负责将类的字节码文件(.class文件)加载到内存中,并生成对应的Class文件
- Class对象:一个后缀名为?.java的源文件对应一个后缀名为 .class的字节码文件,一个字节码文件又对应一个Class对象
- 类加载时机:
(1)创建类的实例:
Student stu = new Student(); //使用类加载器,将Student类的字节码文件加载到内存中
注:如果再次使用该方法创建Student类对象,类加载器将不再加载该类的字节码文件,因为一个类的字节码文件只会被加载一次
(2)访问类的静态成员时:
Calendar.getInstance(); //使用类加载器,将Calendar类的字节码文件加载到内存中
(3)初始化类的子类:
class User extends Person{}
User user = new User(); //使用类加载器,先加载父类Person类的文件,再加载子类User类文件
(4)反射方式创建类的Class对象:
Class clazz = Class.forname("类的正名");
类的正名:包名 + 类名:如 javatest.Test
总结:第一次使用类中的成员时,类加载器就会将该类的字节码文件加载到内存中
?三? 获取Class对象的方式
1 使用Object类中的getClass()方法:Class clazz = 对象名.getClass();
2 类的静态属性:Class clazz = 类名.class;
3 Class类的静态方法:Class clazz = Class.forName("类的正名");
四? 通过反射方式获取构造方法并使用
- Constructor<T>:构造器对象 :即其对应类的构造方法
- 通过Class对象获取构造器对象
1 getConstructor?(Class<?>... parameterTypes):根据参数列表,获取对应的构造器对象(即对应类的构造方法)仅限公共的构造方法。
其中Class<?>...:可变的参数,代表Class类型的数组, ?:通配符,代表不确定的任意类型,根据构造方法进行对应,即Class<?>... parameterTypes 代表参数类别
2 getDeclaredConstructor?(Class<?>... parameterTypes):根据参数列表,获取对应的构造器对象,可获得私有的构造方法
3 getConstructors?():直接获取此类所有的构造方法(不报含私有方法)
4 getDeclaredConstructors()?:直接获取此类所有的构造方法
例:Class<?>... parameterTypes
若参数为 String name , 则为clazz.getConstructor(String.class);
若参数为 int age , 则为clazz.getConstructor(int.class); - Constructor的常用方法
1 getName?(): 获取构造函数名:以字符串形式返回此构造函数的名称
2 newInstance?(Object... initargs): 根据此构造函数和指定参数创建对象,返回为Object类型对象,需要下转型
3 在调用私有构造方法创建对象是需要结合:
Constructor对象名.setAccessible(boolean flag); + Constructor对象名.newInstance?(Object... initargs);
当flag为true是,开始暴力反射,即可以调用私有构造方法 - 代码演示:
Person类
package javatest;
//Person类
public class Person {
//公共的无参构造
public Person() {}
//公共的带参构造
public Person(String name){
System.out.println(name);
}
//私有的的带参构造
private Person(int age){
System.out.println(age);
}
}
测试类:
import java.lang.reflect.Constructor;
public class Test {
public static void main(String[] args) throws Exception {
//需求通过反射的方式创建Person类型的对象并使用其构造方法
//若不需要反射:可直接 Person p = new Person();
//1.获取Person类的字节码文件对象
Class clazz = Class.forName("javatest.Person"); //文件可能不存在,抛出异常
//或Class clazz = Person.class; //通过;类名也可得到CLass对象
//2.根据第一步,获取到的字节码文件对象获取指定的构造器对象
//2.1获取公共的无参构造
Constructor con1 = clazz.getConstructor(); //可能不存在该类型参数列表的构造方法,所以要抛出异常
System.out.print("获取公共的无参构造: ");
System.out.println(con1);
//2.2获取公共的有参构造
Constructor con2 = clazz.getConstructor(String.class); //参数为String类型,需要String.class作为参数
System.out.print("获取公共的有参构造: ");
System.out.println(con2);
//2.3获取私有的有参构造
Constructor con3 = clazz.getDeclaredConstructor(int.class); //参数为int类型,需要int .class作为参数
System.out.print("获取私有的有参构造: ");
System.out.println(con3);
//2.4直接获取所有的公共构造
System.out.println("-------------------------------");
System.out.print("直接获取所有的公共构造: ");
Constructor[] cons = clazz.getConstructors();//获取所有公共构造
//遍历cons数组
for (Constructor con : cons) {
System.out.println(con);
}
//3.根据构造器对象和参数,创建对应的Person类对象
System.out.println("-------------------------------");
//首先根据获取构造器名字
System.out.print("获取构造器名字: ");
System.out.println(con2.getName());
//根据con2构造器创建对象
System.out.print("通过公共带参构造方法,创建对象,并输出参数:");
Person p = (Person) con2.newInstance("张三");//默认为Object类型,通过下转型(强制转换)得到Person对象
//通过有无地址值,确定是否存在
System.out.print("p的地址为: ");
System.out.println(p);
}
} - 结果展示:
- 总结:通过”全类名“先获得Class对象,根据Class对象获得Constructor对象(构造器对象)通过Constructor对象可以创建Person类对象
五??通过反射方式获取成员方法并使用
- Method:方法对象:及其对应类的成员方法
- 通过Class对象获取方法
1 getMethod(String name, Class<?>...parameterTypes):返回一个Method对象,仅公共成员方法
其中:name方法名, Class<?>...parameterTypes方法的参数列表
2 getDeclaredMethod(String, Class<?>...):返回一个Method对象,可获取私有方法
3 getMethod():返回此类中的所有(不含私有)方法数组
4 getDeclaredMethods():返回此类中的所有方法数组
注:getMethod()得到父类的方法,即一定会得到Object类中的方法 - ?Method的常用方法
1 getName():放回方法名
2 invoke( Object obj, Object...args):在指定对象上调用此方法,参数为args
其中:obj代表对象名 , 返回值为Object类型
3 在调用私有方法是需要结合:
Method对象名.setAccessible(boolean flag); + Method对象名.invoke( Object obj, Object...args);
当flag为true是,开始暴力反射,即可以调用私有方法 - 代码演示
Person类:
package javatest;
//Person类
public class Person {
//公共的无参方法
public void show1(){
System.out.println("我是公共的无参方法");
}
//公共的有参方法
public void show2(int a){
System.out.println("我是公共的有参方法,参数为" + a);
}
//私有的有参方法
private int show3(int a, int b){
System.out.println("我是私有的有参方法,参数和为" + (a + b));
return (a + b);
}
} import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) throws Exception {
//需求通过反射的方式获取Person类的成员方法并调用
//若不需要反射:可直接 Person p = new Person();
//1.获取Person类的字节码文件对象
Class clazz = Class.forName("javatest.Person"); //文件可能不存在,抛出异常
//或Class clazz = Person.class; //通过;类名也可得到CLass对象
//2.获取该类的构造器对象,然后创建Person对象
Constructor con = clazz.getConstructor(); //获得系统提供的无参构造方法
Person person = (Person) con.newInstance(); //向下转型,获得Person对象
//3.获取该类的成员方法对象,然后调用此方法
//3.1调用公共的无参方法
Method method1 = clazz.getMethod("show1");
//打印方法
System.out.println(method1);
//打印方法名
System.out.println(method1.getName());
//调用此方法
method1.invoke(person); //person作为对象进行调用
System.out.println("-----------------------------");
//3.2调用公共的有参方法
Method method2 = clazz.getMethod("show2", int.class);
//调用此方法
method2.invoke(person,2); //person作为对象进行调用, arge参数
System.out.println("-----------------------------");
//3.2调用私有的有参方法
Method method3 = clazz.getDeclaredMethod("show3", int.class, int.class);
//调用此方法
method3.setAccessible(true);
int sum = (int)method3.invoke(person,1,2); //person作为对象进行调用
System.out.println("sum:" + sum);
System.out.println("-----------------------------");
//3.4一次性调用所有的公共方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method); //会得到Object类的方法
}
}
}
- 结果演示:可以看出只有俩个为Person类的公共方法,其余为Object类方法
- ?总结:通过”全类名“先获得Class对象,根据Class对象获得Constructor对象(构造器对象)通过构造器创建Person类对象,通过Class对象获取方法,再通过method类方法结合Person类对象调用方法
?六??通过反射方式获取成员变量并使用
- Field对象:域(属性、成员变量)对象,及其对应类的成员变量
- 通过Class对象获取属性
1 getField(String name):返回一个Field对象,仅公共属性
其中:name:属性名
2 getDeclaredField(String name):返回一个Field对象,可获取私有属性
3 getField():返回此类中的所有属性(不含私有)方法数组
4 getDeclaredFields():返回此类中的所有属性方法数组
- ?Field的常用方法
1 set(Object obj, Object value):设置obj对象的指定属性值为value
2 setAccessible(boolean flag):将此属性的可访问性设置为指定布尔值
3 get(Object obj):访问obj对象的属性
4 equals?(Object obj):将此Field与指定的对象进行比较
5 getName():返回此Field对象表示的属性的名称 - 代码演示
Person类:
package javatest;
//Person类
public class Person {
//公有的属性
public String name;
//私有的属性
private int age;
//重写toString方法为了方便打印各个属性值
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) throws Exception{
//需求:通过反射获取成员变量并使用
//1.获取Person类的字节码文件对象
Class clazz = Class.forName("javatest.Person");
//或:Class clazz = Person.class;
//2.通过字节码文件对象获取构造器对象,然后创建Person类对象
Constructor con = clazz.getConstructor();
Person person = (Person)con.newInstance();
//合并版 Person person = (Person)clazz.getConstructor().newInstance(); 链式编程
//3.设置Person类对象的属性值
Field field1 = clazz.getField("name");
field1.set(person, "张三");
Field age = clazz.getDeclaredField("age");
age.setAccessible(true); //将私有属性设置为可设置权限
age.set(person, 1);
System.out.println(person);
}
} - 结果演示:可以看出只有俩个为Person类的公共方法,其余为Object类方法
- ?总结:通过”全类名“先获得Class对象,根据Class对象获得Constructor对象(构造器对象)通过构造器创建Person类对象,通过Class对象获取方法,再通过method类方法结合Person类对象调用方法
|