深入java虚拟机学习 -- 类的加载机制(三)
类的初始化时机
在上篇文章中讲到了类的六种主动使用方式,反射是其中的一种(Class.forName(“com.jack.test”)),这里需要注意一点:当调用ClasLoader类的loadClass方法对类进行加载的时候,并不是对类的主动调用,不会导致类的初始化。
那么接下来我继续给大家2个例子,让我们来看看他们的执行结果分别是什么样的,看看你能猜对吗?
public class Test2
{
public static void main(String[] args)
{
System.out.println(FinalTest2.x);
}
} class FinalTest2{
public static final int x=6/2; static {
System.out.println("I am a final x");
}
}

public class Test2
{
public static void main(String[] args)
{
System.out.println(FinalTest2.x);
}
} class FinalTest2{ public static final int x=new Random().nextInt(); static {
System.out.println("I am a final x");
}
}

是不是看到结果很意外呢,2份代码看起来几乎是一模一样的,而且都是static修饰的,为什么和上篇文章讲的不一样了呢,为什么第一个demo里面的静态块没有执行呢? 下面让我们带着这一系列问题来解决下。
讲解
如果大家细心的话可以看到多了一个final修饰符,是的,结果的造成就是它在起作用。
public static final int x=6/2; 这行代码,java虚拟机在编译期就可以知道x的值是什么,因此在编译期就已经把3放到了常量池,所以在main方法中调用的时候不会触发类的初始化 即此时的x为编译期的常量
public static final int x=new Random().nextInt(); 这行代码中的x在编译期不能确定具体的值,需要等到运行的时候才能确定x的值,所以在运行时会触发类的初始化,即此时的x为编译器的变量
接口和父类
前面讲的类主动加载的7种方式,都是再说单个类的情况,下面我们来介绍下接口和父类
当Java虚拟机初始化一个类的时候,要求他的所有父类都已经被初始化,但是如果此类实现的有接口,则:
- 在初始化一个类的时候,并不会先初始化它所实现的接口
- 在初始化一个接口的时候,并不会先初始化它的父接口
因此,一个父接口并不会因为他的子接口或者实现类的初始化而初始化。只有当程序首次使用他的静态变量时,才会导致该接口的初始化。
只有当程序访问的静态变量或静态方法确实在当前类或当前接口中定义时,才可以认为是对类或接口的主动使用。这句话是什么意思呢?下面让我们看下这个Demo
public class Test3
{
public static void main(String[] args)
{
System.out.println(Child.x);
}
} class Parent{
public static int x=3;
static {
System.out.println("this is a parent");
}
} class Child extends Parent{ static {
System.out.println("this is a Child");
}
}

public class Test3
{
public static void main(String[] args)
{
System.out.println(Child.x);
}
} class Parent{
public static int x=3;
static {
System.out.println("this is a parent");
}
} class Child extends Parent{
public static int x=3;
static {
System.out.println("this is a Child");
}
}

上面两个例子同样是相差一行代码,结果却差别很大,一个Child发生了初始化,另一个没有。
双亲委派模型
类加载器用来把类加载到Java虚拟机中,从JDK1.2版本开始,类的加载过程采用双亲委派模型机制,这种机制能更好的保证Java平台的安全,在这种机制中,除了java虚拟机自带的根加载器以外(根加载器由c++实现,没有父加载器),其余的类加载器有且只有一个父加载器。当Java程序请求加载Loader1加载一个类的时候,loader1首先会委托自己的父加载器去加载这个类,若父加载器还有父加载器的话,依次类推,如果父加载器能加在,则有父加载器完成加载,否则才会有loader1本身加载。
需要注意的事,这里的加载器之间的父子关系实际上指的是加载器对象之间的包装关系,并不是类之间的继承关系。一对父子加载器可能是同一个加载器类的2个实例,也可能不是,只是在子加载器对象中包装了一个父加载器对象。
若有一个类加载器能成功加载Sample类,那么这个类加载器被成为定义类加载器,所有能成功返回Clas对象的引用的类加载器(包括定义类加载器)都被称为初始类加载器。
双亲委派模型的有点是能够提高软件系统的安全性。在此机制下,用户自定义的类加载器不可能加载应该由父加载器加载的可靠类,从而防止不可靠甚至恶意的代码代替由父加载器加载的可靠代码。例如:java.lang.Object类总是由根加载器加载,其他任何用户自定义的类加载器都不可能加载该类。

运行时包
由同一类加载器加载的属于相同包的类组成了运行时包,决定两个类是不是属于同一个运行时包,不仅要看他们的包名是否相同,还要看定义类加载器是否相同。只有属于同一个运行时包的类才能相互访问包可见(即默认访问级别)的类和类成员。这样的限制能避免用户自定义的类冒充核心类库的类去访问核心类库的包可见成员。
假设用户自定义了一个类:java.lang.Spy,并由用户自定义的类加载器加载,由于java.lang.Spy和核心类库java.lang.*由不同的加载器加载, 所以他们属于不同的运行时包,因此java.lang.Spy不能访问核心类库java.lang包中的可见成员。
我们下一篇来着重说下如何来实现一个自定义的类加载器
深入java虚拟机学习 -- 类的加载机制(三)的更多相关文章
- 深入java虚拟机学习 -- 类的加载机制(续)
昨晚写 深入java虚拟机学习 -- 类的加载机制 都到1点半了,由于第二天还要工作,没有将上篇文章中的demo讲解写出来,今天抽时间补上昨晚的例子讲解. 这里我先把昨天的两份代码贴过来,重新看下: ...
- 深入java虚拟机学习 -- 类的加载机制
当看到"类的加载机制",肯定很多人都在想我平时也不接触啊,工作中无非就是写代码,不会了可以百度,至于类,jvm是怎么加载的我一点也不需要关心.在我刚开始工作的时候也觉得这些底层的内 ...
- 深入java虚拟机学习 -- 类的加载机制(四)
类加载的命名空间 每个类加载器都有自己的命名空间,命名空间由所有以此加载器为初始类加载器的类组成,不同命名空间的两个类是不可见的,但只要得到类所对应的Class对象的refrence(反射),还是可以 ...
- Java基础_类的加载机制和反射
类的使用分为三个步骤: 类的加载->类的连接->类的初始化 一.类的加载 当程序运行的时候,系统会首先把我们要使用的Java类加载到内存中.这里加载的是编译后的.class文件 每个类加载 ...
- 深入java虚拟机学习 -- 类的卸载
类的生命周期 在开始本节之前让我们再来回顾下类的生命周期 没看过前6个过程的同学建议从头看下<深入java虚拟机学习 -- 类的加载机制>,这里就不再过多介绍了,着重说下类的卸载 类的卸载 ...
- jvm系列(一):java类的加载机制
java类的加载机制 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装 ...
- JVM(1):Java 类的加载机制
原文出处: 纯洁的微笑 java类的加载机制 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang. ...
- Java虚拟机学习笔记——JVM垃圾回收机制
Java虚拟机学习笔记——JVM垃圾回收机制 Java垃圾回收基于虚拟机的自动内存管理机制,我们不需要为每一个对象进行释放内存,不容易发生内存泄漏和内存溢出问题. 但是自动内存管理机制不是万能药,我们 ...
- jvm系列 (五) ---类的加载机制
类的加载机制 目录 jvm系列(一):jvm内存区域与溢出 jvm系列(二):垃圾收集器与内存分配策略 jvm系列(三):锁的优化 jvm系列 (四) ---强.软.弱.虚引用 我的博客目录 什么是类 ...
随机推荐
- java程序员理解js中的闭包
1.闭包概念: 就是函数内部通过某种方式访问一个函数内部的局部变量 再次理解: 闭包产生原因: 1.内部函数引用了外部函数的变量 作用:延长局部变量的生命周期 让函数外部可以调用到函数内部的数据 利用 ...
- ch7复用类
导出类的初始化是从基类开始向下扩展的,先初始化基类,再初始化由基类继承而来的类. 若类B需要类A中的一些甚至全部方法,但类B实际上不是并不是真正的类A,则可以通过代理的方式在B中实现所需要的A的方法, ...
- alter 和 update的区别?
alter用来增加或者减少列,alter stuednt add name vachar2(30): update用来更改表中的数据:update student set sutudent.name ...
- Java集合框架(六)—— Collections工具类
操作集合的工具类Collections Java提供了一个操作Set.List和Map等集合的工具类:Collections,该工具类里提供了大量方法对集合元素进行排序.查询和修改等操作,还提供了将集 ...
- Qt 如何使用 lambda 表达式连接信号和槽?
connect(camera, static_cast<void(QCamera::*)(QCamera::LockStatus, QCamera::LockChangeReason)>( ...
- es6之let和const命令的一些笔记
let和const命令 let命令 基本用法 let命令用来声明变量,声明的变量只在命令所在的代码块内有效.for循环中很适合使用let命令. 有必要理解的例子: var a = []; for (v ...
- uva12325 暴力枚举
这题刚开始我就贪心,直接wrong了,贪心适合可以取一个物体部分的题. 还是老实枚举吧,注意枚举要分类,不然可能会超时,还有注意答案是long long AC代码: #include<cstdi ...
- JAVA在不确定具体 Annotation 类型时,获得注解参数
package com.lzw.demo; @SpringBootApplication public class DemoApplication { public static void ma ...
- Centos搭建mysql/Hadoop/Hive/Hbase/Sqoop/Pig
目录: 准备工作 Centos安装 mysql Centos安装Hadoop Centos安装hive JDBC远程连接Hive Hbase和hive整合 Centos安装Hbase 准备工作: 配置 ...
- js获取文本的行数
<div class="txt" style="line-height:30px">我是文字<br>我是文字<br>我是文字 ...