1. 类加载流程
2. 加载
类加载过程的第⼀步,主要完成下⾯3件事情:
(1)通过全类名获取定义此类的⼆进制字节流
(2)将字节流所代表的静态存储结构转换为方法区的运行时数据结构(本质是C++的instanceKlass作为元数据来描述Java类)
(3)在内存中⽣成⼀个代表该类的 Class 对象(在堆中,和方法区里的java_mirror对应),作为⽅法区这些数据的访问⼊⼝
3. 验证
验证类是否符合 JVM规范,安全性检查
4. 准备
为static变量分配空间,设置默认值
5. 解析
将常量池中的符号引用解析为直接引用
public class Load2 { public static void main(String[] args) throws ClassNotFoundException, IOException { ClassLoader classloader = Load2.class.getClassLoader(); // loadClass 方法不会导致类C的解析和初始化,由于懒加载,类D还没被用到,这样查看类C加载情况里类D只是一个符号 Class<?> c = classloader.loadClass("cn.itcast.jvm.t3.load.C"); // new C(); 会加载且解析且初始化,这样类C的加载情况里D的内存地址都有了 System.in.read(); } }
class C { D d = new D(); }
class D { }
6. 初始化
(1)初始化即调用<cinit>方法,虚拟机会保证该方法执行的线程安全
补充:<cinit>方法是在类加载过程中执行的,而<init>是在对象实例化执行的
<cinit>:父类静态变量初始化 > 父类静态代码块 > 子类静态变量初始化 > 子类静态代码块
<init>:父类变量初始化 > 父类构造代码块 > 父类构造函数 > 子类变量初始化 > 子类构造代码块 > 子类构造函数
(2)初始化是“懒惰的” (类开始加载的时机没有明确规定,但是初始化时机规定了)
比如子类访问父类静态变量只有父类初始化,访问final常量(基本类型和字符串)不会初始化,loadClass不会初始化,创建类的数组不会初始化等
应用:懒加载的单例模式
class Singleton{ //volatile关键字禁止指令重排 private volatile static Singleton instance; private Singleton(){} public Singleton getInstance(){ if(instance==null){ //对.class加锁,获得的是所有类对象的锁 synchronized (Singleton.class){ //双重检查,防止多个线程同时进入第一层检查(因单例模式只允许存在一个对象,故在创建对象之前无引用指向对象,所有线程均可进入第一层检查) //当某一线程获得锁创建一个Singleton对象时,即已有引用指向对象,singleton不为空,从而保证只会创建一个对象 //假设没有第二层检查,那么第一个线程创建完对象释放锁后,后面进入对象也会创建对象,会产生多个对象 if(instance==null){ //singleton=new Singleton语句为非原子性,实际上会执行以下内容: //(1)在堆上开辟空间;(2)属性初始化;(3)引用指向对象 //volatile关键字可保证singleton=new Singleton()语句执行顺序为123,引用指向对象时一定有对象了 } } } return instance; } }
7. 类加载器
(1)层级关系
(2)双亲委派
系统中的 ClassLoader 在协同工作的时候会默认使用双亲委派模型 ;
即类加载时,首先会委派上级的类加载器的loadClass()方法,因此所有类加载器都会传到顶层启动类加载器,当父类加载器无法处理时,才有自己处理。
(3)判断两个类完全相同
包名,类名,类加载器均相同
8. javap工具分析类结构
javap -v HelloWorld.class
9. HSDB工具
HSDB是一个很强大的JVM运行时状态分析工具,根据线程id或name,可以查找类加载情况,内存地址,常量池内容等
java -cp ./lib/sa-jdi.jar sun.jvm.hotspot.HSDB
10. 语法糖
所谓的语法糖 ,其实就是指 java 编译器把 *.java 源码编译为 *.class 字节码的过程中,自动生成和转换的一些代码,主要是为了减轻程序员的负担,算是 java 编译器给我们的一个额外福利(给糖吃嘛)
(1)默认构造器
(2)自动拆装箱JDK5
(3)泛型擦除:java 在编译泛型代码后会执行泛型擦除的动作,即泛型信息在编译为字节码之后就丢失了,实际的类型都当做了 Object 类型来处理(反射可以获得这些信息)
(4)可变参数:转换为数组
(5)foreach:转换为普通for循环