Java虚拟机JVM学习04 类的初始化

类的初始化

  在初始化阶段,Java虚拟机执行类的初始化语句,为类的静态变量赋予初始值。

  在程序中,静态变量的初始化有两种途径:

  1.在静态变量的声明处进行初始化;

  2.在静态代码块中进行初始化。

  没有经过显式初始化的静态变量将原有的值。

  一个比较奇怪的例子:

package com.mengdd.classloader;

class Singleton {

    // private static Singleton mInstance = new Singleton();// 位置1
// 位置1输出:
// counter1: 1
// counter2: 0
public static int counter1;
public static int counter2 = 0; private static Singleton mInstance = new Singleton();// 位置2 // 位置2输出:
// counter1: 1
// counter2: 1 private Singleton() {
counter1++;
counter2++;
} public static Singleton getInstantce() {
return mInstance;
}
} public class Test1 { public static void main(String[] args) { Singleton singleton = Singleton.getInstantce();
System.out.println("counter1: " + Singleton.counter1);
System.out.println("counter2: " + Singleton.counter2);
}
}

  可见将生成对象的语句放在两个位置,输出是不一样的(相应位置的输出已在程序注释中标明)。

  这是因为初始化语句是按照顺序来执行的。

  静态变量的声明语句,以及静态代码块都被看做类的初始化语句,Java虚拟机会按照初始化语句在类文件中的先后顺序来依次执行它们。

类的初始化步骤

  1.假如这个类还没有被加载和连接,那就先进行加载和连接

  2.假如类存在直接的父类,并且这个父类还没有被初始化,那就先初始化直接的父类

  3.假如类中存在初始化语句,那就依次执行这些初始化语句。

类的初始化时机

  Java程序对类的使用方式可以分为两种:

  1.主动使用

  2.被动使用

  所有的Java虚拟机实现必须在每个类或接口被Java程序首次主动使用才初始化它们。

  主动使用的六种情况:

  1.创建类的实例。

new Test();

  

  2.访问某个类或接口的静态变量,或者对该静态变量赋值。

int b = Test.a;
Test.a = b;

  

  3.调用类的静态方法

Test.doSomething();

  

  4.反射

Class.forName(“com.mengdd.Test”);

  

  5.初始化一个类的子类

class Parent{
}
class Child extends Parent{
public static int a = 3;
}
Child.a = 4;

  

  6.Java虚拟机启动时被标明为启动类的类

java com.mengdd.Test

  除了以上六种情况,其他使用Java类的方式都被看作是对类的被动使用,都不会导致类的初始化

接口的特殊性

  当Java虚拟机初始化一个类时,要求它的所有父类都已经被初始化,但是这条规则并不适用于接口

    在初始化一个类时,并不会先初始化它所实现的接口。

    在初始化一个接口时,并不会先初始化它的父接口。

  因此,一个父接口并不会因为它的子接口或者实现类的初始化而初始化,只有当程序首次使用特定接口的静态变量时,才会导致该接口的初始化。

final类型的静态变量

  final类型的静态变量是编译时常量还是变量,会影响初始化语句块的执行。

  如果一个静态变量的值是一个编译时的常量就不会对类型进行初始化(类的static块不执行);

  如果一个静态变量的值是一个非编译时的常量,即只有运行时会有确定的初始化值,则就会对这个类型进行初始化(类的static块执行)。

  例子代码:

package com.mengdd.classloader;

import java.util.Random;

class FinalTest1 {
public static final int x = 6 / 3; // 编译时期已经可知其值为2,是常量
// 类型不需要进行初始化
static {
System.out.println("static block in FinalTest1");
// 此段语句不会被执行,即无输出
}
} class FinalTest2 {
public static final int x = new Random().nextInt(100);// 只有运行时才能得到值
static {
System.out.println("static block in FinalTest2");
// 会进行类的初始化,即静态语句块会执行,有输出
}
} public class InitTest { public static void main(String[] args) {
System.out.println("FinalTest1: " + FinalTest1.x);
System.out.println("FinalTest2: " + FinalTest2.x);
}
}

主动使用的归属明确性

  只有当程序访问的静态变量或静态方法确实在当前类或当前接口中定义时,才可以认为是对类或接口的主动使用。

package com.mengdd.classloader;

class Parent {
static int a = 3; static {
System.out.println("Parent static block");
} static void doSomething() {
System.out.println("do something");
}
} class Child extends Parent {
static {
System.out.println("Child static block");
}
} public class ParentTest { public static void main(String[] args) { System.out.println("Child.a: " + Child.a);
Child.doSomething(); // Child类的静态代码块没有执行,说明Child类没有初始化
// 这是因为主动使用的变量和方法都是定义在Parent类中的
}
}

ClassLoader类

  调用ClassLoader类的loadClass()方法加载一个类,并不是对类的主动使用,不会导致类的初始化。

package com.mengdd.classloader;

class CL {

    static {
System.out.println("static block in CL");
}
} public class ClassLoaderInitTest { public static void main(String[] args) throws Exception {
ClassLoader loader = ClassLoader.getSystemClassLoader();
Class<?> clazz = loader.loadClass("com.mengdd.classloader.CL");
// loadClass方法加载一个类,并不是对类的主动使用,不会导致类的初始化 System.out.println("----------------");
clazz = Class.forName("com.mengdd.classloader.CL");
} }

参考资料

  圣思园张龙老师Java SE系列视频教程。

Java虚拟机JVM学习04 类的初始化的更多相关文章

  1. Java虚拟机JVM学习07 类的卸载机制

    Java虚拟机JVM学习07 类的卸载机制 类的生命周期 当Sample类被加载.连接和初始化后,它的生命周期就开始了. 当代表Sample类的Class对象不再被引用,即不可触及时,Class对象就 ...

  2. Java虚拟机JVM学习02 类的加载概述

    Java虚拟机JVM学习02 类的加载概述 类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对 ...

  3. Java虚拟机JVM学习01 流程概述

    Java虚拟机JVM学习01 流程概述 Java虚拟机与程序的生命周期 一个运行时的Java虚拟机(JVM)负责运行一个Java程序. 当启动一个Java程序时,一个虚拟机实例诞生:当程序关闭退出,这 ...

  4. Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论

    Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论 创建用户自定义的类加载器 要创建用户自定义的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的f ...

  5. Java虚拟机JVM学习05 类加载器的父委托机制

    Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...

  6. Java虚拟机JVM学习03 连接过程:验证、准备、解析

    Java虚拟机JVM学习03 连接过程:验证.准备.解析 类被加载后,就进入连接阶段. 连接就是将已经读入到内存的类的二进制数据合并到虚拟机的运行时环境中去. 连接阶段三个步骤:验证.准备和解析. 类 ...

  7. Java虚拟机(JVM) - 学习总结(全)

    深入理解java虚拟机---学习总结: 1.Java内存区域 1.1 java运行时数据区 Java 虚拟机所管理的内存如下图所示,基于JDK1.6. 基于jdk1.8画的JVM的内存模型 (1) 程 ...

  8. java虚拟机JVM学习笔记-基础知识

    最近使用开发的过程中出现了一个小问题,顺便记录一下原因和方法--java虚拟机 媒介:JVM是每一位从事Java开发工程师必须翻越的一座大山! JVM(Java Virtual Machine)JRE ...

  9. JVM学习04:类的文件结构

    JVM学习04:类的文件结构 写在前面:本系列分享主要参考资料是  周志明老师的<深入理解Java虚拟机>第二版. 类的文件结构知识要点Xmind梳理

随机推荐

  1. Windows Azure Virtual Network (10) 使用Azure Access Control List(ACL)设置客户端访问权限

    <Windows Azure Platform 系列文章目录> 本文介绍的是国内由世纪互联运维的China Azure. 我们在创建完Windows Azure Virtual Machi ...

  2. 在Elasticsearch中查询Term Vectors词条向量信息

    这篇文章有点深度,可能需要一些Lucene或者全文检索的背景.由于我也很久没有看过Lucene了,有些地方理解的不对还请多多指正. 更多内容还请参考整理的ELK教程 关于Term Vectors 额, ...

  3. PHP 中的Closure

    PHP 中的Closure Closure,匿名函数,又称为Anonymous functions,是php5.3的时候引入的.匿名函数就是没有定义名字的函数.这点牢牢记住就能理解匿名函数的定义了. ...

  4. 纯Shading Language绘制HTML5时钟

    今天是2014年的最后一天,这个时刻总会让人想起时钟,再过几个小时地球人都要再老了一岁,于是搞个HTML5版的时钟就是我们今天要完成的任务,实现HTML5的时钟绘制一般会采用三种方式,第一种采用CSS ...

  5. 阅读《LEARNING HARD C#学习笔记》知识点总结与摘要一

    本人有幸在Learning Hard举行的整点抢书活动<Learninghard C#学习笔记>回馈网友,免费送书5本中免费获得了一本<LEARNING HARD C#学习笔记> ...

  6. 【Swift学习】Swift编程之旅---方法(十五)

    在Swift中结构体和枚举也能够定义方法,而在 Objective-C 中,类是唯一能定义方法的类型. 实例方法 实例方法是属于某个特定类.结构体或者枚举类型实例的方法,实例方法提供访问和修改实例属性 ...

  7. 用Qt写软件系列五:一个安全防护软件的制作(3)

    引言 上一篇中讲述了工具箱的添加.通过一个水平布局管理器,我们将一系列的工具按钮组合到了一起,完成了工具箱的编写.本文在前面的基础上实现窗体分割效果.堆栈式窗口以及Tab选项卡. 窗体分割 窗体分割是 ...

  8. DP - tencent2016实习生笔试A

    tencent2016实习生笔试A Problem's Link ------------------------------------------------------------------- ...

  9. java实现的排序(插入/希尔/归并)

    java实现三种简单的排序,以下是代码: /*插入排序*/ public static void insertionSort(int[] a) { int j; for(int p = 1; p &l ...

  10. vs2010 用户控件拖到aspx页面不可用

    错误描述: 在web项目中添加一个用户控件,直接拖动用户控件ascx到aspx页面出现a标签而不是控件标签 解决办法: 把“源”切换为“设计”视图,然后拖动ascx用户控件到页面即可: