1、Class对象
理解RTTI在Java中的工作原理,首先需要知道类型信息在运行时是如何表示的,这是由Class对象来完成的,它包含了与类有关的信息。Class对象就是用来创建所有“常规”对象的,Java使用Class对象来执行RTTI,即使你正在执行的是类似类型转换这样的操作。
每个类都会产生一个对应的Class对象,也就是保存在.class文件。所有类都是在对其第一次使用时,动态加载到JVM的,当程序创建一个对类的静态成员的引用时,就会加载这个类。Class对象仅在需要的时候才会加载,static初始化是在类加载时进行的。
类加载器首先会检查这个类的Class对象是否已被加载过,如果尚未加载,默认的类加载器就会根据类名查找对应的.class文件。
想在运行时使用类型信息,必须获取对象(比如类Base对象)的Class对象的引用,使用功能Class.forName(“Base”)可以实现该目的,或者使用base.class。注意,有一点很有趣,使用功能”.class”来创建Class对象的引用时,不会自动初始化该Class对象,使用forName()会自动初始化该Class对象。为了使用类而做的准备工作一般有以下3个步骤:
- 加载:由类加载器完成,找到对应的字节码,创建一个Class对象
- 链接:验证类中的字节码,为静态域分配空间
- 初始化:如果该类有超类,则对其初始化,执行静态初始化器和静态初始化块
@PostConstruct public void init() throws NoSuchFieldException { Field[] fields = this.getClass().getDeclaredFields(); Arrays.asList(fields) .forEach( field -> { Method[] methods = field.getType().getMethods(); Map<String, Method> methodMap = new ConcurrentHashMap<>(); Arrays.asList(methods) .forEach( method -> { methodMap.put(method.getName(), method); }); try { field.setAccessible(true); CLASS_METHODS.put(field.get(this), methodMap); } catch (IllegalAccessException e) { e.printStackTrace(); } }); }
上面的代码是填充一个map,map的每一个元素是这个类的成员变量,和这个变量的方法集合。
反射主要使用Class这个类的方法
补充资料:
七、关于Java类加载器内容的详解
1、类的加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化
·加载:
就是指将class文件读入内存,并为之创建一个Class对象,任何类被使用时系统都会建立一个Class对象
·连接:
验证:确保被加载类的正确性
准备:负责为类的静态成员分配内存,并设置默认初始化值
解析:将类中的符号引用替换为直接引用
·初始化:
局部变量保存在栈区:必须手动初始化
new 的对象保存在堆区:虚拟机会进行默认初始化,基本数据类型初始化值为0,引用类型初始化值为null
2、类加载的时机(只加载一次)
以下时机仅表示第一次的时候
① 创建类的实例的时候
② 访问类的静态变量的时候
③ 调用类的静态方法的时候
④ 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
⑤ 初始化某个类的子类的时候
⑥ 直接使用java.exe命令来运行某个主类
3、类加载器
负责将.class文件加载到内存中,并为之生成对应的Class对象
虽然我们在开发过程中不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行
4、类加载器的组成:
①Bootstrap ClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类的加载,比如System类,在JDK中JRE的lib目录下rt.jar文件中的类
②Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载,在JDK中JRE的lib目录下ext目录
③System ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径,主要是我们开发者自己写的类
更多内容请参考《深入理解JVM虚拟机》