反射机制复习总结
1.反射的概述(熟悉)
- java给我们提供了一套API,使用这套API我们可以在运行时动态的获取指定对象所属的类,创建运行时类的对象,调用指定结构(属性、方法)等。
- API:
- java.lang.Class`:代表一个类
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Constructor:代表类的构造器
- … …
- 反射的优缺点
-
优点:
- 提高了Java程序的灵活性和扩展性,
降低了耦合性
,提高自适应
能力 - 允许程序创建和控制任何类的对象,无需提前
硬编码
目标类
- 提高了Java程序的灵活性和扩展性,
-
缺点 :
- 反射的
性能较低
。- 反射机制主要应用在对灵活性和扩展性要求很高的系统框架上
- 反射会
模糊
程序内部逻辑,可读性较差
。
- 反射的
-
- 反射,平时开发中,我们使用的并不多,主要是在框架的底层使用
2.Class:反射的源头
- Class的理解(掌握)
针对于编写好的.java源文件进行编译(使用javac.exe),会生成一个或多个.class字节码文件
接着我们使用java.exe命令指定的.class文件进行解释运行,这个解释运行过程中,
我们需要将.class字节码文件加载(使用了类的加载器)到内存中
(存放在方法区),加载到内存中的.class文件对应的结构即为Class的一个实例
- 获取Class的几种方式(前三种)
- 类.class
- 对象.getClass()
- (使用最多) Class调用静态方法forName(String calssName)
- (了解)使用ClassLoader的方法loadClass(String className)
- Class 可以指向那些结构
简言之,所有Java类型!
(1)class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
(2)interface:接口
(3)[]:数组
(4)enum:枚举
(5)annotation:注解@interface
(6)primitive type:基本数据类型
(7)void
3.类的加载过程、类的加载器(理解)
- 类的加载过程
过程1:类的装载(loading)
将类的class文件读入内存,并为之创建一个java.lang.Class对象。此过程由类加载器完成
过程2:链接(linking)
>验证(Verify):确保加载的类信息符合JVM规范,例如:以cafebabe开头,没有安全方面的问题。
>准备(Prepare):正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
>解析(Resolve):虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
过程3:初始化(initialization):
- 执行类构造器<clinit>()方法的过程。
类构造器<clinit>()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。
- 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
- 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。
- 类的加载器
6. 关于类的加载器(了解、JDK8版本为例)
6.1 作用:负责类的加载,并对应于一个Class的实例
6.2 分类(分为两种):
>BootstrapClassLoader:引导类加载器、启动类加载器
> 使用C/C++语言编写的,不能通过java代码获取其实例
> 负责加载Java的核心库(JAVA_HOME/jre/lib/rt.jar或sun.boot.class.path路径下的内容)。用于提供JVM自身需要的类。
>继承于ClassLoader的类加载器
>ExtensionClassLoader:扩展类加载器
> 负责加载从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录下加载类库。
>SystemClassLoader/ApplicationClassLoader:系统类加载器、应用程序加载器
> 我们自定义的类,默认使用的类的加载器
>用户自定义类的加载器
>实现应用的隔离(同一个类在一个应用程序中可以加载多份);数据加密
4.反射的应用1:创建运行时类的对象(重点)
Class clazz = Person.class;
//创建Person类的实例
Person p1 = (Person) clazz.newInstance();
System.out.println(p1);
要想创建对象成功,需要满足:
条件1:要求运行时类中必须提供一个空参的构造器
条件2:要求提供的空参的构造器权限要足够
5.反射的应用2:获取运行时类所有的结构
(了解)获取运行时类的内部结构1:所有属性、所有方法、所有构造器
(熟悉)获取运行时类的内部结构2:父类、接口们、包、带泛型的父类、父类的泛型等
6.反射的应用3:调用指定的结构(重点)
3. (掌握)反射的应用3:调用指定的结构:指定的属性、方法、构造器
3.1 调用指定的属性(步骤):
> 步骤1.通过Class实例调用getDeclaredField(String fieldName),获取运行时类中指定名的属性
> 步骤2.setAccessible(true):确保此属性是可以访问的
> 步骤3.通过Filed类的实例调用get(Object obj)(获取的操作) 或 set(Object obj,Object value)(设置的操作)
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
3.3 调用指定的构造器:
> 步骤1:通过Class的实例调用getDeclaredConstructor(Class ... args),获取指定参数类型的构造器
> 步骤2:setAccessible(true):确保此构造器是可以访问的
> 步骤3:通过Constructor实例调用newInstance(Object ... objs),返回一个运行时类的实例
7.反射的应用4:注解使用的问题(了解)
略
8.体会:反射的动态性
public class ReflectTest {
//体会:静态性
public Person getInstance() {
return new Person();
}
//体会:反射的动态性
//举例1:
public <T> T getInstance(String className) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> clazz = Class.forName(className);
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
return (T) constructor.newInstance();
}
@Test
public void test1() throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
Person p1 = getInstance();
System.out.println(p1);
String className="com.huige04.other.dynamic.Person";
Person p2 = getInstance(className);
System.out.println(p2);
}
//体会:反射的动态性
//举例2:
public Object invoke(String className,String methodName) throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException {
//1.创建全类名对应的运行时类的对象
Class<?> clazz = Class.forName(className);
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Object objl = constructor.newInstance();
//2.获取运行时类的指定方法,并调用
Method declaredMethod = clazz.getDeclaredMethod(methodName);
declaredMethod.setAccessible(true);
return declaredMethod.invoke(objl);
}
@Test
public void test2() throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
String className = "com.huige04.other.dynamic.Person";
String methodName = "show";
Object invoke = invoke(className, methodName);
System.out.println(invoke);
}
}