1、类加载器
类的加载器分为两种:
- java虚拟机自带的加载器
1、根类加载器(Bootstrap)
会加载$JAVA_HOME 中jre/lib/rt.jar 里面所有的class,由c++实现,
2、扩展类加载器(Extension)
负责记载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或者-Djava.ext.dirs指定目录下的jar包
3、系统(应用)类加载器(System)
负责加载classpath中指定的jar包及目录下的jar包
- 用户自定义的类加载器
1、java.lang.ClassLoader的类
2、用户可以定制类的加载方式
2、双亲委托机制
2.1 双亲委托机制
当前ClassLoader首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。
每个类加载器都有自己的加载缓存,当一个类被加载了以后就会放入缓存,等下次加载的时候就可以直接返回了。当前classLoader的缓存中没有找到被加载的类的时候,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载,一直到bootstrp ClassLoader.
当所有的父类加载器都没有加载的时候,再由当前的类加载器加载,并将其放入它自己的缓存中,以便下次有加载请求的时候直接返回。
2.2 双亲委托的好处
类加载器的双亲为头模型的好处:
可以确保java核心库的类型安全;所有的java应用豆斋少会引用java.lang.Object类, 也就是说在运行期,java.lang.Object这个类会被加载到java虚拟机中;
如果这个加载过程是由java应用自己的类加载器所完成,那么很可能就会在 jvm中存在多个版本的java.lang.Object类,而且这些之间还是不兼容的,相互不可见的(正是命名空间在发挥着作用)借助于双亲委托机制,java核心类库中的加载工作都是由启动类加载器来统一完成加载工作,从而确保了java应用所使用的
都是同一个版本的java核心类库,他们之间是相互兼容的。可以确保java核心类库所提供的类不会被自定义的类所替代。
- 不同的类加载器可以为相同名称(binary name)的类创建额外的命名空间。
相同名称的类可以并存在java虚拟机中,只需要用不同的类加载器来加载他们即可。 不同类加载器所加载的类之间是不兼容的,这就相当于在java虚拟机内部创建了一个又一个相互隔离的java类空间,这类技术在很多矿建都得到了实际应用。
1 | package com.wangqd.jvm.test; |
2、存在继承关系时,代码块,静态代码块,构造方法执行顺序
结论:
1、当一个类在初始化时,要求其父类全部都已经初始化完毕
2、优先级:静态代码块 > 代码块 > 构造方法
3、创建子类对象时会先加载父类,静态代码块会优先被加载
4、创建对象时 代码块和构造方法会一起被加载,存在父类则先加载父类的代码块和构造方法
1 | package com.wangqd.jvm.test; |
3、static final变量的加载
- static final 修饰常量
结论:引用被static final 修饰的常量( 包含原生数据类型和字符串)时,确定的值则不会初始化常量的类
1 | package com.wangqd.jvm.test; |
获取class文件的字节码:javap -c com.wangqd.jvm.test.Test4
可以看到Test4的字节码文件中存在 hello world,说明static final 的常量编译后直接存储在Test4中,所以Test4调用MyTest4.hello,会直接将其替换为常量。1
2
3
4
5
6
7
8
9
10
11
12
13
14public class com.wangqd.jvm.test.Test4 {
public com.wangqd.jvm.test.Test4();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #4 // String hello world
5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
}
也可以通过反编译工具编译Test4 发现MyTest4.hello直接被替换为hello
- static final 修饰变量
结论:引用被static final 修饰的变量(对象)时,会初始化变量的类1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31package com.wangqd.jvm.test;
import java.util.Random;
public class Test4 {
public static void main(String[] args) {
System.out.println(MyTest4.random);
}
}
class MyTest4 {
public static final Random random = new Random();
static {
System.out.println("MyTest4 static block invoked");
}
{
System.out.println("MyTest4 block invoked");
}
public MyTest4() {
System.out.println("MyTest4 constructor invoked");
}
}
/*
out:
MyTest4 static block invoked
java.util.Random@2b05039f
*/
获取class文件的字节码:javap -c com.wangqd.jvm.test.Test4
发现Random对应的是getstatic(访问某个类或者接口的静态变量)1
2
3
4
5
6
7
8
9
10
11
12
13
14public class com.wangqd.jvm.test.Test4 {
public com.wangqd.jvm.test.Test4();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: getstatic #3 // Field com/wangqd/jvm/test/MyTest4.random:Ljava/util/Random;
6: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
9: return
}
4、 static 变量的加载
结论:引用类的static成员变量会初始化,改static成员变量的类
1 | package com.wangqd.jvm.test; |
5、 存在继承关系时,static 变量的加载
当调用子类的static成员变量时
首先,加载static成员变量会加载成员变量所在的类,当一个类在初始化时,要求其父类全部都已经初始化完,所以会先加载父类,然后在初始化自己1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35package com.wangqd.jvm.test;
public class Test6 {
public static void main(String[] args) {
System.out.println(MyChild6.str2);
}
}
class MyParent6{
public static String str = "hello world";
static {
System.out.println("MyParent1 static block");
}
{
System.out.println("MyParent1 block");
}
}
class MyChild6 extends MyParent6{
public static String str2 = "welcome";
static {
System.out.println("MyChild1 static block");
}
{
System.out.println("MyChild block");
}
}
/*
out:
MyParent1 static block
MyChild1 static block
welcome
*/当调用子类从父类继承的static成员变量时
只会初始化父类,不会初始化自己。即只有直接定义了该字段的类才会被初始化1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35package com.wangqd.jvm.test;
public class Test6 {
public static void main(String[] args) {
System.out.println(MyChild6.str);
}
}
class MyParent6{
public static String str = "hello world";
static {
System.out.println("MyParent1 static block");
}
{
System.out.println("MyParent1 block");
}
}
class MyChild6 extends MyParent6{
public static String str2 = "welcome";
static {
System.out.println("MyChild1 static block");
}
{
System.out.println("MyChild block");
}
}
/*
out:
MyParent1 static block
hello world
*/