1.反射的概述
Java给我们提供了一套API,使用这套API我们可以在运行时动态的获取指定对象所属的类,创建
运行时类的对象,调用指定的结构,(属性,方法)等
API:
-
java.lang.Class
:代表一个类 -
java.lang.reflect.Method:代表类的方法
-
java.lang.reflect.Field:代表类的成员变量
-
java.lang.reflect.Constructor:代表类的构造器
-
反射的优点:提高Java程序的灵活性和扩展性,降低了耦合性,提高自适应能力。
-
允许创建和控制任意类对象,无需提前硬编码目标类
-
缺点:
-
反射的性能低
-
反射机制主要在对灵活性和扩展性要求很高的系统框架上。
-
放射会模糊内部逻辑,可读性较差。
-
反射,在日常开发中使用不多,主要是底层框架的使用
-
2.class:反射的源头
- class类的理解(掌握)
- 针对于编写好的.java源文件进行编译(使用javac.exe),会生成一个或多个.class字节码文件。接着,我们使用java.exe命令对指定的.class文件进行解释运行。这个解释运行的过程中,我们需要将.class字节码文件加载(使用类的加载器)到内存中(存放在方法区)。加载到内存中的.class文件对应的结构即为Class的一个实例。
- 获取类实例的几种方式(前三种)
- 类.class
- 对象.getclass
- (使用较多) class调用静态方法forName(String className);这里的String classname 是类的全类名
- (了解)使用classLoader的方式loadclass(String className)
- Class可以指向那些结构
- class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
- 注解
- 接口
- [ ]数组
- enum:枚举
- 基本数据类型
- void
- 3.类的加载过程
- 过程1类的装载(loading):
- 将类的Class读入文件,并为之创建一个java.lang.Class对象。此过程由类加载器完成
过程2:链接(LInking)
> 验证:(Verify)确保信息符合jvm规范,列如,符合cafebabe开头,没有安全问题
> 准备:(Prepare)为静态(static)分配内存并设置默认初始值的过程
> 解析(Resolve):虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
过程3:初始化(initialization)
- 执行类构造器<clinit>()方法的过程。
类构造器<clinit>()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。
4.反射的应用1:创建运行时类的对象(重点)
Class clazz= person.class
person per =(preson)clazz.newInstance();
System.out.println(per);
条件: 要想创建对象成功,需要满足:
条件1:要求运行时类中必须提供一个空参的构造器
条件2:要 求 提 供的空参的构造器的权限要足够。
5.反射的应用2:获取运行时类的所有结构
(了解)获取运行时类的的内部结构:所有属性,所有方法,所有构造器
另一种方法,也是一样的,这里的forName()是静态方法
(熟悉) 获取运行时类的内部结构2:父类,接口们,包,带泛型的父类,父类的泛型等
6.反射的应用3;调用指定的结构(重点)
3.1 调用指定的属性(步骤)
步骤1.通过Class实例调用getDeclaredField(String fieldName),获取运行时类指定名的属性
步骤2. setAccessible(true):确保此属性是可以访问的
步骤3. 通过Filed类的实例调用get(Object obj) (获取的操作) 或 set(Object obj,Object value) (设置的操作)进行操作。
调用普通属性(任何权限)
调用静态属性:静态不需要对象去调,所以这里不用创建对象,用类.class去调。
3.2 调用指定的方法(步骤)
步骤1.通过Class的实例调用getDeclaredMethod(String methodName,Class ... args),获取指定的方法
步骤2. setAccessible(true):确保此方法是可访问的
步骤3.通过Method实例调用invoke(Object obj,Object ... objs),即为对Method对应的方法的调用。
invoke()的返回值即为Method对应的方法的返回值
特别的:如果Method对应的方法的返回值类型为void,则invoke()返回值为null
普通方法:
静态方法;这里的返回值是void,所有最后直接null,如果不是void,那就可以用类.class
3.3 调用指定的构造器(步骤)
步骤1.通过Class的实例调用getDeclaredConstructor(Class ... args),获取指定参数类型的构造器
步骤2.setAccessible(true):确保此构造器是可以访问的
步骤3.通过Constructor实例调用newInstance(Object ... objs),返回一个运行时类的实例。
//有参构造
无参构造
7体会反射的动态性
public class ReflectTest {
//体会:静态性
public Person getInstance(){
return new Person();
}
//体会:反射的动态性
//举例1:
public <T> T getInstance(String className) throws Exception {
Class clazz = Class.forName(className);
Constructor con = clazz.getDeclaredConstructor();
con.setAccessible(true);
return (T) con.newInstance();
}
@Test
public void test1() throws Exception {
Person p1 = getInstance();
System.out.println(p1);
String className = "com.atguigu04.other.dynamic.Person";
Person per1 = getInstance(className);
System.out.println(per1);
String className1 = "java.util.Date";
Date date1 = getInstance(className1);
System.out.println(date1);
}
//体会:反射的动态性
//举例2:
public Object invoke(String className,String methodName) throws Exception {
//1. 创建全类名对应的运行时类的对象
Class clazz = Class.forName(className);
Constructor con = clazz.getDeclaredConstructor();
con.setAccessible(true);
Object obj = con.newInstance();
//2. 获取运行时类中指定的方法,并调用
Method method = clazz.getDeclaredMethod(methodName);
method.setAccessible(true);
return method.invoke(obj);
}
@Test
public void test2() throws Exception {
String className = "com.atguigu04.other.dynamic.Person";
String methodName = "show";
Object returnValue = invoke(className,methodName);
System.out.println(returnValue);
}
}