一个类在 内存中只有一个Class对象
一个类被加载后,类的整个结构都会被封装在Class对象中
//通过对象获得
Class c1 = c.getClass();
//forname获得
Class c2 = Class.forName("com.fan.annuo.Person");
System.out.println("c2 = " + c2);
//通过类名获得
Class c3= Student.class;
System.out.println("c3 = " + c3);
//基本类型的包装类都有一个Type属性
Class c4 = Integer.class;
System.out.println("c4 = " + c4.getName());
//获取类的父类类型
Class c5 = c1.getSuperclass();
System.out.println("c5 = " + c5);
类的加载与ClassLoader的理解
加载:将Class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象
类加载是指将class文件读入内存,并为之创建一个 java.lang.Class 对象任何类被使用时,系统都会为之建立一个 java.lang.Class 对象。 类加载过程一般要经历三个过程。
1、通过一个类的全限定名来获取其定义的二进制字节流(获取流)
2、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构(方法区)。
3、在 Java 堆中生成一个代表这个类的 java.lang.Class 对象,作为对方法区中这些数据的访问入口(生成访问入口)。
链接:将java类的二进制代码合并到JVM的运行状态之中的过程。
验证:确保加载的类信息符合JVM规范,没有安全方面的问题
准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
( 1、验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致
2、准备阶段:负责为类的类变量分配内存,并设置默认初始化值。
3、解析阶段:将类的二进制数据中的符号引用替换为直接引用。)
验证
验证是链接第一步,这一阶段的目的是为了确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
验证阶段大致会完成 4 个阶段的检验动作
文件格式验证:验证字节流是否符合 Class 文件格式的规范;例如:是否以 0xCAFEBABE 开头、主次版本号是否在当前虚拟机的处理范围之内、常量池中的常量是否有不被支持的类型。
元数据验证:对字节码描述的信息进行语义分析(注意:对比 javac 编译阶段的语义分析),以保证其描述的信息符合 Java 语言规范的要求;例如:这个类除了java.lang.Object 之外是否有父类。
字节码验证:通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。 符号引用验证:确保解析动作能正确执行。
验证阶段是非常重要的,但不是必须的,它对程序运行期没有影响,如果不需要验证,那么可以考虑采用-Xverifynone 参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。
初始化
执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。
当初始化一个类的时候。如果发现其父类还没有进行初始化,则需先触发其父类的初始化。
虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。
为类的静态变量赋予正确的初始值,JVM 负责对类进行初始化,主要对类变量进行初始化。在 Java 中对类变量进行初始值设定有两种方式:
1,声明类变量是指定初始值。
2,使用静态代码块为类变量指定初始值。
类的初始化步骤或 JVM 初始化的步骤如下:
1)如果这个类还没有被加载和链接,那先进行加载和链接 ;
2)假如这个类存在直接父类,并且这个类还没有被初始化(注意:在一个类加载器中,类只能初始化一次),那就直接初始化父类(不适用于接口);
3 ) 假如类中存在初始化语句(如 static 变量和 static 块),那就依次执行这些初始化语句
什么时候会发生类初始化
主动引用
被动引用
不会发生类的初始化
java类加载器
java类加载器的主要作用是负责将.class文件加载到内存中,并为之生成对应的 java.lang.Class 对象。
JVM的类加载器
全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入。
父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。
缓存机制: 保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存储到缓存区。
Java中的内置类加载器
Bootstrap class loader:它是虚拟机的内置类加载器,通常表示为null,并且没有父加载类。 Platform class loader:平台类加载器可以看到所有平台类 ,平台类包括由平台类加载器或其祖先定义的JavaSE平台API,其实现类和JDK特定的运行时类 System class loader:它也被称为应用程序类加载器,与平台类加载器不同。系统类加载器通常用于定义应用程序类路径,模块路径和JDK特定工具上的类 类加载器的继承关系:System的父加载器为Platform,而Platform的父加载器为Bootstrap。
反射
反射是指在运行时去获取一个类的变量和方法信息(成员方法和构造方法)。然后通过获取到的信息来创建对象,是调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可 以扩展,下面是一个简单的反射示意图。 其实讲的接地气一点反射就是将一个类的各个部分封装成其他对象
反射简例示意图
\
反射的优点
通过上面案例来思考反射为什么说反射是框架的灵魂?
1) 反射使得程序在运行过程中操作目标对象提供了可行的方法,大大提高了程序运行的灵活性。
2)基于反射机制使程序的解耦增强,耦合性降低,程序可扩展性增强。