Class对象

概述

  1. 每个类都有一个Class对象;
  2. 类Class对象是由JVM的子系统 “类加载器”生成的。

类加载器

类加载器实际上可以包含一条加载器链,但是只有一个原声类加载器,可以挂接额外类加载器

  1. 原生类加载器
    原生类加载的是所谓的可信类,如:JavaAPI等

  2. 额外类加载器
    额外类加载器用来满足自身的特殊需求,如:以某种特殊方式加载类,以支持WEB服务器应用等

类的动态加载

类的动态加载分为三个步骤:

  1. 加载
    由类加载器执行,该步骤将查找字节码,并从字节码中创建一个Class对象

  2. 链接
    链接将验证类中的字节码,为静态域分配存储空间,并且如果必须的话将解析这个类创建其它类的所有引用

  3. 初始化
    如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块

“惰性”初始化

当程序创建第一个对类的静态成员的引用时,就会 加载 该类;
根据java虚拟机规范,所有java虚拟机实现必须在每个类或接口被java程序首次 主动使用 时才初始化。

主动使用有如下几种

  • 创建类的实例

  • 访问某个类或者接口的静态域(静态变量、方法)

    • 如果访问静态编译时常量(static final)不会导致类的初始化
    • 构造方法也是类的静态方法,因此使用 new关键字会被当成是对静态成员的引用
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
package org.gavin.clazz;

class LazyInitialization1 {
static final int FIELD = 0;

static int FIELD1 = 1;

static {
System.out.println("Initial");
}
}

/**
* <p>如果 static域是final的,那么不对类进行初始化也可以进行读取;
* <p>若 static域不是final的,那在读取之前进行链接(为这个域分配空间)和初始化(初始化该空间)。
*
* @author gavin
*/
public class ClassInitialize1 {
public static void main(String[] args) {
System.out.println("static final");
System.out.println(LazyInitialization1.FIELD);

System.out.println("static");
System.out.println(LazyInitialization1.FIELD1);
}
}

// 输出结果:
static final
0
static
Initial
1
  • 反射(Class.forName(xxx.xxx.xxx))
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
package org.gavin.clazz;

class LazyInitialization {
static {
System.out.println("Initial");
}
}

/**
* <p>使用类字面常量 .class 来创建对Class对象的引用时,不会自动初始化该Class对象;
* <p>但是使用 Class.forName() 会立即进行初始化。
*
* @author gavin
*/
public class ClassInitialize {
@SuppressWarnings("unused")
public static void main(String[] args) throws ClassNotFoundException{
System.out.println(".class");
Class<LazyInitialization> clazz = LazyInitialization.class;

System.out.println("Class.forName()");
Class.forName("org.gavin.clazz.LazyInitialization");
}
}


// 输出结果:
.class
Class.forName()
Initial
  • 初始化一个类的子类(相当于对父类的主动使用),不过直接通过子类引用父类元素,不会引起子类的初始化
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
package org.gavin.clazz;

class Parent {
static int field = 0;
static {
System.out.println("parent");
}
}

class Child extends Parent{
static {
System.out.println("child");
}
}

/**
*当访问一个Java类或接口的静态域时,只有真正声明这个域的类或接口才会被初始化
*/
public class ClassInitialize2 {
public static void main(String[] args) {
System.out.println(Child.field);
}
}

// 输出结果:
parent
0
  • Java虚拟机被标明为启动类的类(包含main方法的类)

获取Class对象

  1. 类字面量常量 .class
  2. 反射 Class.forName( )
  3. 实例对象 obj.getClass

参考

Java编程思想 (14章)
Java系列笔记(1) - Java 类加载与初始化