宏观看来就是:将一份被 javac 编译过的 class 文本文件,通过加载,生成某种形式的 Class 数据结构进入内存,程序可以调用这个数据结构构造出 object
java 类的生命周期:
在 javac 编译之后会进行如下 *** 作
类加载的过程:
1.加载1.1 将类的二进制字节码载入 JVM 中
1.2 将二进制字节码转换为某种静态数据结构,存放在方法区
1.3 在堆中生成一个便于用户调用的java.land.Class 对象,表示堆方法区中类的引用。
加载之后,虽然JVM内存中堆里已经存在这个对象了。但是这个时候这个对象并不被JVM认可,要想能够调用这个类,JVM需要对这个类继续连接。
- 连接
2.1 验证: 对class静态结构进行语法和语义上的分析,保证载入的类不会危害JVM
2.2 准备:给静态变量常量设置默认值(不包含成员变量)
2.3 解析: 将类中的符号引用转化为直接引用
**符号引用/直接引用:**若A引用了B,但是在编译之前A肯定不知道B的真实地址,此时将用一个字符串 S 来代替B的地址。在加载A时会触发B的类加载。此时会将A中的符号引用替换为B的实际地址。这就是直接引用。
**解析又分为静态解析和动态解析:**因为Java有多态机制,如果上面提到的B是一个实体类,那么这样的解析称为静态解析,如果B是一个接口或者是抽象类的时候,这个时候就没有办法确定这个引用的实际地址,既然没有那就先留着。等到运行阶段发生了调用,这个时候虚拟机中的调用栈将会得到具体的类型信息,这个时候再进行解析就可以得到明确的直接引用。这个过程就叫做动态绑定。这也是为什么解析阶段会发生在初始化阶段之后,实现后期绑定。
- 初始化:
执行资源主动初始化,初始化静态常量和成员变量。而只有new 才会调用对象层面的初始化,构造函数什么的
private Class A { private int a = 1;// 初始化 static private int b; static { b = 1; //初始化 } }
要求除了顶层的启动类加载器之外,其余的类加载器都应该有自己的父类加载器,双亲委派在父子关系采取的并不是继承的关系,而是采用组合的关系复用父类加载器的相关代码。
(ps: 为啥叫双亲,一开始笔者也很困惑,知道发现了 双亲 的 英文叫 parent ,就大彻大悟了!我们可以将其理解为父类委派)
工作原理:
如果一个类收到了类加载的请求,他并不会自己先去加载,而时将这个请求委托给父类加载器,若父类加载器还存在父类,则继续向上委托,依次递归,请求最终会到达顶层的启动类加载器。父类完成类的加载任务后,成功返回。若父类无法完成才会由子类加载器去自己加载,这就是双亲委派模式。
(我愿称之为 啃老机制,儿子懒,啥事都让爹干,爹也不会整了,才稀的自己动手)
优点:
- 类的加载具有优先级的层次关系,通过这种层级关系可以避免类的重复加载。安全。Java核心API中定义类型不会被随意替换,假设通过网路传递一个名为java.lang.Integer的类,通过双亲委派的的模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字类,发现该类已经被加载,并不会重新加载网络传递过来的java.lang.Integer.而之际返回已经加载过的Integer.class,这样便可以防止核心API库被随意篡改。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)