文档章节

类在什么时候加载和初始化

 本杰明刘能
发布于 2017/01/25 10:10
字数 1515
阅读 11
收藏 1

本文由 ImportNew - 刘志军 翻译自 Javarevisited。欢迎加入翻译小组。转载请见文末要求。

理解类在JVM中什么时候被加载和初始化是Java编程语言中的基础概念,正因为有了Java语言规范,我们才可以清晰的记录和解释这个问题,但是很多Java程序员仍然不知道什么时候类被加载,什么时候类被初始化,类加载和初始化好像让人很困惑,对初学者难以理解,在这篇教程中我们将看看类加载什么时候发生,类和接口是如何被初始化的,我并不会拘泥于类加载器的细节或者说类加载器的工作方式。仅仅使这篇文章更加专注和简结。

类什么时候加载

类的加载是通过类加载器(Classloader)完成的,它既可以是饿汉式[eagerly load](只要有其它类引用了它就加载)加载类,也可以是懒加载[lazy load](等到类初始化发生的时候才加载)。不过我相信这跟不同的JVM实现有关,然而他又是受JLS保证的(当有静态初始化需求的时候才被加载)。

类什么时候初始化

加载完类后,类的初始化就会发生,意味着它会初始化所有类静态成员,以下情况一个类被初始化:

  1. 实例通过使用new()关键字创建或者使用class.forName()反射,但它有可能导致ClassNotFoundException。
  2. 类的静态方法被调用
  3. 类的静态域被赋值
  4. 静态域被访问,而且它不是常量
  5. 在顶层类中执行assert语句

反射同样可以使类初始化,比如java.lang.reflect包下面的某些方法,JLS严格的说明:一个类不会被任何除以上之外的原因初始化。

类是如何被初始化的

现在我们知道什么时候触发类的初始化了,他精确地写在Java语言规范中。但了解清楚 域(fields,静态的还是非静态的)、块(block静态的还是非静态的)、不同类(子类和超类)和不同的接口(子接口,实现类和超接口)的初始化顺序也很重要类。事实上很多核心Java面试题和SCJP问题都是基于这些概念,下面是类初始化的一些规则:

  1. 类从顶至底的顺序初始化,所以声明在顶部的字段的早于底部的字段初始化
  2. 超类早于子类和衍生类的初始化
  3. 如果类的初始化是由于访问静态域而触发,那么只有声明静态域的类才被初始化,而不会触发超类的初始化或者子类的初始化即使静态域被子类或子接口或者它的实现类所引用。
  4. 接口初始化不会导致父接口的初始化。
  5. 静态域的初始化是在类的静态初始化期间,非静态域的初始化时在类的实例创建期间。这意味这静态域初始化在非静态域之前。
  6. 非静态域通过构造器初始化,子类在做任何初始化之前构造器会隐含地调用父类的构造器,他保证了非静态或实例变量(父类)初始化早于子类

初始化例子

这是一个有关类被初始化的例子,你可以看到哪个类被初始化

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

35

36

37

38

39

40

41

42

/**

 * Java program to demonstrate class loading and initialization in Java.

 */

public class ClassInitializationTest {

 

    public static void main(String args[]) throws InterruptedException {

 

        NotUsed o = null; //this class is not used, should not be initialized

        Child t = new Child(); //initializing sub class, should trigger super class initialization

        System.out.println((Object)o == (Object)t);

    }

}

 

/**

 * Super class to demonstrate that Super class is loaded and initialized before Subclass.

 */

class Parent {

    static { System.out.println("static block of Super class is initialized"); }

    {System.out.println("non static blocks in super class is initialized");}

}

 

/**

 * Java class which is not used in this program, consequently not loaded by JVM

 */

class NotUsed {

    static { System.out.println("NotUsed Class is initialized "); }

}

 

/**

 * Sub class of Parent, demonstrate when exactly sub class loading and initialization occurs.

 */

class Child extends Parent {

    static { System.out.println("static block of Sub class is initialized in Java "); }

    {System.out.println("non static blocks in sub class is initialized");}

}

 

Output:

static block of Super class is initialized

static block of Sub class is initialized in Java

non static blocks in super class is initialized

non static blocks in sub class is initialized

false

从上面结果可以看出:

  1. 超类初始化早于子类
  2. 静态变量或代码块初始化早于非静态块和域
  3. 没使用的类根本不会被初始化,因为他没有被使用

再来看一个例子:

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

/**

 * Another Java program example to demonstrate class initialization and loading in Java.

 */

 

public class ClassInitializationTest {

 

    public static void main(String args[]) throws InterruptedException {

 

       //accessing static field of Parent through child, should only initialize Parent

       System.out.println(Child.familyName);

    }

}

 

class Parent {

    //compile time constant, accessing this will not trigger class initialization

    //protected static final String familyName = "Lawson";

 

    protected static String familyName = "Lawson";

 

    static { System.out.println("static block of Super class is initialized"); }

    {System.out.println("non static blocks in super class is initialized");}

}

 

Output:

static block of Super class is initialized

Lawson

分析:

  1. 这里的初始化发生是因为有静态域被访问,而且不一个编译时常量。如果声明的”familyName”是使用final关键字修饰的编译时常量使用(就是上面的注释代码块部分)超类的初始化就不会发生。
  2. 尽管静态与被子类所引用但是也仅仅是超类被初始化

还有另外一个例子与接口相关的,JLS清晰地解释子接口的初始化不会触发父接口的初始化。强烈推荐阅读JLS14.4理解类加载和初始化细节。以上所有就是有关类被初始化和加载的全部内容。

原文链接: Javarevisited 翻译: ImportNew.com - 刘志军
译文链接: http://www.importnew.com/6579.html
[ 转载请保留原文出处、译者和译文链接。]

本文转载自:http://www.importnew.com/6579.html

粉丝 0
博文 11
码字总数 218
作品 0
乌鲁木齐
程序员
私信 提问
JVM详解之类加载过程

JVM详解之类加载过程 什么叫类?类在什么时候进行加载?类加载过程发生了什么?或者叫jvm做了哪些工作? 类的概念(.java/.class的区别)和范围扩充 java语言和Java虚拟机的概念 java可以运行...

默默学习中
2016/04/15
654
0
类中静态块,静态变量,静态常量,块,成员变量,成员常量,静态方法,成员方法,构造器,内部类加载顺序

如题,他们分别在什么时候加载,是在类加载时加载还是类new对象的时候加载,加载顺序又是什么?在西三旗学习,筋疲力尽啊

小石头哥哥
2014/12/09
202
4
类的加载机制,复习一下

本文根据《深入理解java虚拟机》第7章部分内容整理 Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是...

lightUp
2016/03/02
54
0
Java类加载、卸载、占用内存、静态方法占用内存分析

一、类的加载时机: 类在什么时候加载和初始化 二、类的卸载时机: 类卸载,满足下面三个条件: 1、该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例。2、加载该类的ClassLo...

GalaxyBruce
2016/10/04
96
0
使用代码自定义UIView注意一二三

这一块由两个东西组成:一个imageView和一个label。首先我们新建一个继承自UIView的类MyView. 在MyView的.m文件里,你可以根据自己的意愿将两个子控件设置成MyView的属性或者成员变量,这里我...

王素年
2016/12/17
16
0

没有更多内容

加载失败,请刷新页面

加载更多

代理模式之JDK动态代理 — “JDK Dynamic Proxy“

动态代理的原理是什么? 所谓的动态代理,他是一个代理机制,代理机制可以看作是对调用目标的一个包装,这样我们对目标代码的调用不是直接发生的,而是通过代理完成,通过代理可以有效的让调...

code-ortaerc
今天
5
0
学习记录(day05-标签操作、属性绑定、语句控制、数据绑定、事件绑定、案例用户登录)

[TOC] 1.1.1标签操作v-text&v-html v-text:会把data中绑定的数据值原样输出。 v-html:会把data中值输出,且会自动解析html代码 <!--可以将指定的内容显示到标签体中--><标签 v-text=""></......

庭前云落
今天
8
0
VMware vSphere的两种RDM磁盘

在VMware vSphere vCenter中创建虚拟机时,可以添加一种叫RDM的磁盘。 RDM - Raw Device Mapping,原始设备映射,那么,RDM磁盘是不是就可以称作为“原始设备映射磁盘”呢?这也是一种可以热...

大别阿郎
今天
12
0
【AngularJS学习笔记】02 小杂烩及学习总结

本文转载于:专业的前端网站☞【AngularJS学习笔记】02 小杂烩及学习总结 表格示例 <div ng-app="myApp" ng-controller="customersCtrl"> <table> <tr ng-repeat="x in names | orderBy ......

前端老手
昨天
16
0
Linux 内核的五大创新

在科技行业,创新这个词几乎和革命一样到处泛滥,所以很难将那些夸张的东西与真正令人振奋的东西区分开来。Linux内核被称为创新,但它又被称为现代计算中最大的奇迹,一个微观世界中的庞然大...

阮鹏
昨天
20
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部