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. 搭建PHP开发环境

    通过两种方式搭建PHP开发环境,而这种开发环境实际是:Apache+MySQL+PHP.  第一种: 通过套件来安装和配置php开发环境. Apache+MySQL+PHP开发环境的搭建(一) 第二种 ...

  2. maven基础知识

    1.maven基础知识 1.1maven坐标 maven坐标通常用冒号作为分割符来书写,像这样的格式:groupId:artifactId:packaging:version.项目包含了junit3. ...

  3. Vim魔法堂:认识快捷键绑定

    Brief 习惯在VS上按<F5>来编译运行程序,刚用上VIM上就觉得无比的麻烦,而随着对VIM的学习我们分阶段的简化这一操作 1. 退出VIM,在shell下编译&&运行 ...

  4. 一对多关系domain Model中设置使用AutoMapper时出错

    在使用AutoMapper时,把数据从VO-PO时显示如下错误,错误提示说在一对多关系中已将集合设置为EntityCollection,那么这个是为什么呢. 看下action中的代码,我们可以发现这是 ...

  5. 使用Dom4j进行XML解析

    1  概述 在进行ESB集成项目中,使用到了很多系统的接口,这些接口传输的数据大部分都采用了XML的格式,这样在使用ESB开发服务时就需要对XML数据进行解析或拼接的操作,本文以项目中流程服务为例,讲 ...

  6. 【SQL】sql版Split函数。用于拆分字符串为单列表格

    功能与.net版string.Split函数类似,只不过.net返回的是数组,这个返回的是一个单列表格,每个拆分出来的子串占一行.可选是否移除空格子串和重复项.市面上类似的函数不算少,但大多都是在循环 ...

  7. 为什么说Babel将推动JavaScript的发展

    Babel是一个转换编译器,它能将ES6转换成可以在浏览器中运行的代码.Babel由来自澳大利亚的开发者Sebastian McKenzie创建.他的目标是使Babel可以处理ES6的所有新语法,并为 ...

  8. Android使用SAX解析XML(1)

    可扩展标记语言XML是一种数据交换格式,允许用户自己定义,适合Web传输,能提供独立于程序的数据.XML在Android中也有广泛的应用,Android解析XML的方法有很多,本文介绍使用SAX(Si ...

  9. 【Java每日一题】20161021

    20161020问题解析请点击今日问题下方的"[Java每日一题]20161021"查看 package Oct2016; public class Ques1021 { publ ...

  10. Python 第二模块学习总结

    学习总结: 1.掌握对装饰器的用法 2.掌握生成器的用法 3.掌握迭代器的用法 4.熟悉Python内置函数 5.熟悉Python shutil/shelve/configparse/hashlib/ ...