以下内容若有误,欢迎私信我或在下方留言,谢谢^_?
1.类加载器
1.1 类加载
当程序需要使用某个类时,如果该类未被加载进内存,则系统会对类进行初始化,初始化有三个步骤,即类加载、类连接、类初始化。
(1)类加载
- 类加载就是指将编译后的.class文件加载到内存,并为之创建一个java.lang.Class对象。
(2)类连接
- 验证阶段:检验被加载类的内部结构是否正确
- 准备阶段:负责为类的类变量分配内存,并设置默认初始值
- 解析阶段:将类的二进制数据中的符号引用替换为直接引用
(3)类初始化
(4)类的初始化时机
- 创建类的实例
- 调用类的类方法
- 访问类或者接口的类变量,或者为该类变量赋值
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令运行某个主类
1.2 类加载器(ClassLoader)
(1)类加载器的作用
- 顾名思义,就是进行类加载的一个工具,即将.class文件加载进内存,并为之生成对应的java.lang.Class对象
(2)JVM的类加载机制
- 全盘负责:当某个类加载器负责加载某个Class时,该Class所依赖或所引用的Class也由该类加载器负责加载,除非明确显示由其他类加载加载。
类似于株连九族,对于罪犯的核心家庭成员及其父族家属都要被杀。全盘负责就是与某个Class相关联的都要被加载进来。
- 父类委托:当某个类加载器负责加载某个Class时,先由父类加载器尝试加载该Class。只有当父类加载器无法加载该类时,才从自己的类路径加载该类。
大致意思就是先让父亲去带儿子过来,如果带不过来,再让儿子自己过来。
- 缓存机制:缓存机制主要是为了保证所有被加载过的Class都会被缓存。当程序需要使用某个Class对象时,类加载器将先从缓存区查找该Class。只有当缓冲区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换为Class对象,存储到缓存区。
就好比是炒菜时需要葱蒜,你会先在家里找是否有葱蒜,如果没有再出去买,多余的葱蒜就存起来,等下次需要再用。
(3)内置类加载器
Bootstrap class loader :引导类加载器,通常表示为null,并且没有父nullPlatform class loader :平台类加载器,可以看到所有平台类,包括由平台类加载器或其祖先定义的Java SE平台API、其实现类和JDK特定的运行时类。System class loader :系统类加载器,也称为应用程序类加载器。与平台类加载器不同,系统类加载器通常用于定义应用程序类路径、模块路径和JDK特定工具上的类。- 类加载器的继承关系:System的父加载器为Platform,而Platform的父加载器为Bootstrap
方法名 | 说明 |
---|
static Classloader getSystemClassLoader() | 返回用于委派的系统类加载器 | ClassLoader getParent() | 返回父类加载器进行委派 |
2.反射
2.1 概述
Java反射机制是指允许程序在运行时获取任何类的内部信息,并能操作任意对象的属性和方法(包括被private修饰的),通过获取到的信息创建对象、调用方法的一种机制。正因为有这样一种机制,使得原本是静态的Java语言具有一定的动态性,增强了程序的灵活性。
简单一提:框架=反射+注解+设计模式
2.2 获取Class类对象的三种方式
public class Demo {
public static void main(String[] args) throws ClassNotFoundException {
Class c1 = Class.forName("pojo.Student");
Student student = new Student();
Class c2 = student.getClass();
Class c3 = Student.class;
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
}
}
2.3 反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
2.4 反射获取构造方法的方法
方法名 | 说明 |
---|
Constructor<?>[] getConstructors() | 返回所有公共构造方法对象的数组 | Constructor<?>[] getDeclaredConstructors() | 返回所有构造方法对象的数组 | Constructor<T> getConstructor(Class<?>… parameterTypes) | 返回单个公共构造方法对象 | Constructor<T> getDeclaredConstructor(Class<?>… parameterTypes) | 返回单个构造方法对象 |
2.5 反射获取成员变量的方法
方法名 | 说明 |
---|
Field[] getFields() | 返回所有公共成员变量对象的数组 | Field[] getDeclaredFields() | 返回所有成员变量对象的数组 | Field getField(String name) | 返回单个公共成员变量对象 | Field getDeclaredField(String name) | 返回单个成员变量对象 |
2.6 反射获取成员方法的方法
方法名 | 说明 |
---|
Method[] getMethods() | 返回所有公共成员方法对象的数组(包括继承的) | Method[] getDeclaredMethods() | 返回所有成员方法对象的数组(不包括继承的) | Method getMethod(String name, Class<?>… parameterTypes) | 返回单个公共成员方法对象 | Method getDeclaredMethod(String name, Class<?>… parameterTypes) | 返回单个成员方法对象 |
2.7 其它方法
方法名 | 说明 |
---|
T newInstance(Object… initargs) | 根据指定的构造方法创建对象 | void set(Object obj,Object value) | 给obj对象的成员变量赋值为value | Object invoke(Object obj,Object…args) | 调用obj对象的成员方法,参数是args,返回值是Object类型 | void setAccessible(boolean flag) | 当为true时,取消访问检查。用于获取非公有属性和方法时取消访问检查 |
拓展:反射机制和面向对象的封装性是否矛盾?
答案很明显是不矛盾。原因:封装体现的是高内聚、低耦合,建议使用公有的(public)属性、方法,不去使用私有的(private)。但这只是建议,如果非要调用私有的,那可以用反射机制实现,而反射体现的是动态性,是为了提供程序的灵活性,因此,两者并不矛盾。
小小例子加强理解:从前有一个富豪,富豪有一个小金库,为了便于对金库的管理,富豪就派了一名管家负责金库的收支。任何人想要取钱或者存钱都需要经过管家,不能直接去金库里面存取;但偏偏有个叫“反射”的人,嫌找管家太麻烦了,并仗着自己有着翻墙的本领,所以,每次存钱取钱都直接翻墙去操作金库。
|