Java虚拟机JVM学习04 类的初始化
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 类的初始化的更多相关文章
- Java虚拟机JVM学习07 类的卸载机制
Java虚拟机JVM学习07 类的卸载机制 类的生命周期 当Sample类被加载.连接和初始化后,它的生命周期就开始了. 当代表Sample类的Class对象不再被引用,即不可触及时,Class对象就 ...
- Java虚拟机JVM学习02 类的加载概述
Java虚拟机JVM学习02 类的加载概述 类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对 ...
- Java虚拟机JVM学习01 流程概述
Java虚拟机JVM学习01 流程概述 Java虚拟机与程序的生命周期 一个运行时的Java虚拟机(JVM)负责运行一个Java程序. 当启动一个Java程序时,一个虚拟机实例诞生:当程序关闭退出,这 ...
- Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论
Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论 创建用户自定义的类加载器 要创建用户自定义的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的f ...
- Java虚拟机JVM学习05 类加载器的父委托机制
Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...
- Java虚拟机JVM学习03 连接过程:验证、准备、解析
Java虚拟机JVM学习03 连接过程:验证.准备.解析 类被加载后,就进入连接阶段. 连接就是将已经读入到内存的类的二进制数据合并到虚拟机的运行时环境中去. 连接阶段三个步骤:验证.准备和解析. 类 ...
- Java虚拟机(JVM) - 学习总结(全)
深入理解java虚拟机---学习总结: 1.Java内存区域 1.1 java运行时数据区 Java 虚拟机所管理的内存如下图所示,基于JDK1.6. 基于jdk1.8画的JVM的内存模型 (1) 程 ...
- java虚拟机JVM学习笔记-基础知识
最近使用开发的过程中出现了一个小问题,顺便记录一下原因和方法--java虚拟机 媒介:JVM是每一位从事Java开发工程师必须翻越的一座大山! JVM(Java Virtual Machine)JRE ...
- JVM学习04:类的文件结构
JVM学习04:类的文件结构 写在前面:本系列分享主要参考资料是 周志明老师的<深入理解Java虚拟机>第二版. 类的文件结构知识要点Xmind梳理
随机推荐
- Azure SQL Database (1) 用户手册
<Windows Azure Platform 系列文章目录> 最新更新2016年6月17日 下载地址:Azure SQL Database用户手册
- Theano入门神经网络(二) 实现一个XOR门
与非门的图片如下 示意图 详细解释: 1 定义变量的代码,包括了输入.权值.输出等.其中激活函数采用的是sigmod函数 # -*- coding: utf-8 -*- __author__ = 'A ...
- Theano入门神经网络(一)
Theano是一个Python库,专门用于定义.优化.求值数学表达式,效率高,适用于多维数组.特别适合做机器学习.一般来说,使用时需要安装python和numpy. 首先回顾一下机器学习的东西,定义一 ...
- CSS魔法堂:再次认识font
一.前言 文字承载着站点内涵,而良好的字体.排版则为用户提供舒适的阅读体验.本文打算对字体稍微深入一下子网页字体的内容,若有纰漏请大家指正,谢谢! 目录一坨: 二, 字体分类 1. 衬线体(Serif ...
- 30天C#基础巩固----程序集,反射
一:认识程序集 只要是使用VS就会和程序集打交道,我们通过编辑和生产可执行程序就会自动生成程序集.那么什么事程序集呢,.net中的dll与exe文件的都是程序集(Assembly). ...
- 使用ExpandoObject来实现多个Model传送至视图
昨天Insus.NET有实现<使用ViewModel来实现多个Model传送至视图>http://www.cnblogs.com/insus/p/5594134.html 那今天Insus ...
- 【SQL】CLR聚合函数什么鬼
之前写过一个合并字符串的CLR聚合函数,基本是照抄MS的示例,外加了一些处理,已经投入使用很长时间,没什么问题也就没怎么研究,近日想改造一下,遇到一些问题,遂捣鼓一番,有些心得,记录如下. 一.杂项 ...
- C#中使用Sql对Excel条件查询
如何在C#中实现对Excel的条件查询呢? 在使用Sql条件语句对Excel进行查询时,遇到"至少一个参数没有被指定值"的问题,如何解决? 使用OleDbConnection对象创 ...
- Tomcat8访问管理页面localhost出现:403 Access Denied
问题: Access Denied You are not authorized to view this page. If you have already configured the Manag ...
- Android使用SAX解析XML(6)
应用截图: (1)开始运行时 (2)选择学院 (3)选择专业 (4)选择班级 (5)选择班级的详细信息 本文参考了<Android平台开发之旅>.