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. Mina工作原理分析

    Mina是Apache社区维护的一个开源的高性能IO框架,在业界内久经考验,广为使用.Mina与后来兴起的高性能IO新贵Netty一样,都是韩国人Trustin Lee的大作,二者的设计理念是极为相似 ...

  2. SQL Server存储(6/8) :理解DCM页

    我们已经讨论了各种不同的页,包括数据页.GAM与SGAM页.PFS页,还有IAM页.今天我们来看下差异变更页(Differential Change Map:DCM ),还有差异备份(differen ...

  3. 【Swift学习】Swift编程之旅(四)基本运算符

    Swift支持大部分标准C语言的运算符, 且改进许多特性来减少常规编码错误.如赋值符 = 不返回值, 以防止错把等号 == 写成赋值号 = 而导致Bug. 数值运算符( + , -, *, /, %等 ...

  4. Net.Sockets

    #region 程序集 System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 // C:\Program ...

  5. 呼叫外部js文件并使用其内部方法

    很久没有学习jQuery了,都快忘记了.今天学习一个小功能,使用jQuery的$.getScript()方法,是读取外部js文件,读取之后,并执行js文件内的一个方法. 首先我们创建这个js文件,如i ...

  6. 在Linux(Ubuntu)下搭建ASP.NET Core环境并运行 继续跨平台

    最新教程:http://www.cnblogs.com/linezero/p/aspnetcoreubuntu.html 无需安装mono,在Linux(Ubuntu)下搭建ASP.NET Core环 ...

  7. WinForm 简单蒙版实现控件遮盖

    在Web上面要实现一个遮罩层或者说是蒙版吧,有了DIV那不算什么难事,只要给div定好位置和大小,把颜色的Alpha值设一下就有透明的效果.不过在Winform中实现起来就没那么简单了事.尝试过用一个 ...

  8. 对于一些Http远程连接Api安全的看法;

    文章来源于 :http://lesg.cn/?p=122 我的个人博客站点 对于一些Http远程连接Api安全的看法: 当不同系统需要互相通信的时候:如果无法用webservice等方式链接的时候另一 ...

  9. SignalR入门之从外部访问持久性连接或Hub

    有的时候,需要从外部访问持久性连接或Hub服务. 比如,假设A和B两个客户端正在聊天,那么系统或第三方在不参与聊天的情况需要为他们发送系统消息,那么此时,就需要独立来访问持久性连接或Hub服务. 之前 ...

  10. DOS 和 Linux 常用命令的对比

    DOS 和 Linux 常用命令的对比 许多在 shell 提示下键入的 Linux命令都与你在 DOS 下键入的命令相似.事实上,某些命令完全相同. 本附录提供了 Windows的 DOS 提示下的 ...