在经过了前面的加载  和 连接分析之后,这一节我们进入重要的初始化分析过程:

一、认识初始化

初始化:这个似乎与上面的初始化为默认值有点矛盾,我们再看一遍:为累的静态变量赋予正确的初始值,上面是赋予默认值,这里是赋予正确的初始值,什么是正确的初始值,就是用户给赋予的值。我们来看一个例子

class Test{
private static int a = 1; }

我们知道,这个类加载好之后,a的值就是1,但实际是这样子的,类在加载的连接阶段,将a初始化为默认值0(int的默认值是0),然后在初始化阶段将a的值赋予为正确的初始值1. 我们看到最终a的值是等于1,但是实际的运行中是有一个将0赋予a的过程,这个过程放生在连接的准备阶段。类的初始化还有另外的一种形式,代码如下:

class Test{

private static int a ;

static{

a=1;

  }

}

这里强调一点,这个时候还是没有类的实例生成的,这点一定要注意!《深入java虚拟机第二版》里面有一个图阐述了对应的关系,如下:

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

二、初始化步骤

1.假如这个类还没有被加载和连接,那就先进行加载和连接。【加载和连接执行完成后不一定会执行初始化,要符合主动使用才会进行初始化,否则只会执行加载和连接】
2.假如类存在直接的父类,并且这个父类还没有被初始化,那就先初始化直接的父类。
3.假如类中存在初始化语句,那就依次执行这些初始化语句。

三、主动使用和被动使用【初始化时机】

Java程序对类的使用方式可分为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类的方式都被看作是对类的被动使用,都不会导致类的初始化 (除了上述6种情况以外,都不会执行初始化,只会执行加载和连接)

这个时候我们再来看第一节学习中那段比较诡异的代码:

class Singleton{

    private static Singleton singleton=new Singleton();

    private static int counter1;

    private static int counter2 = 0;

    public Singleton() {

       counter1++;

       counter2++;

    }

    public static int getCounter1() {

       return counter1;

    }

    public static int getCounter2() {

       return counter2;

    }

    public static Singleton getInstance(){

       return singleton;

    }

}

public class ClassLoaderTest {

    @SuppressWarnings("static-access")

    public static void main(String[] args) {

       Singleton singleton=Singleton.getInstance();

       System.out.println("counter1:"+singleton.getCounter1());

       System.out.println("counter2:"+singleton.getCounter2());

       System.out.println(singleton.getClass().getClassLoader());

    }

}

我们调用Singleton singleton=Singleton.getInstance();调用Singleton的静态方法,相当于主动使用了类Singleton,因此Singleton被初始化!
当我们看到显示的是:

private static Singleton singleton=new Singleton();

    private static int counter1;

    private static int counter2 = 0;

这样的时候,顺序执行,先赋予初始值,singleton为null,counter1为0,counter2为0,然后顺序对singleton赋予正确的值newSingleton(),执行构造函数,counter1增加变为1,然后counter2变为1,然后继续执行初始化,将counter2赋值为正确的值,将counter2修改为0,因此运行结果是1、0

反过来

private static int counter1;

    private static int counter2 = 0;

    private static Singleton singleton=new Singleton();

先赋予初始值counter1为0,counter为0,singleton为null,然后对counter2赋值为正确的值,counter2为0,然后对singleton执行初始化赋予正确的值new Singleton(),执行构造函数,counter1为1,counter2为1,因此执行结果是1、1

到此,是不是感觉前面的问题豁然开朗了呢?

参考资料:

圣思园张龙老师深入Java虚拟机系列

JVM学习四:JVM之类加载器之初始化分析的更多相关文章

  1. jvm系列(四):jvm知识点总结

    原文链接:http://www.cnblogs.com/ityouknow/p/6482464.html jvm 总体梳理 jvm体系总体分四大块: 类的加载机制 jvm内存结构 GC算法 垃圾回收 ...

  2. jvm学习006 jvm内存结构分配

    主要内容如下: JVM启动流程 JVM基本结构 内存模型 编译和解释运行的概念 一.JVM启动流程: JVM启动时,是由java命令/javaw命令来启动的. 二.JVM基本结构: JVM基本结构图: ...

  3. JVM学习笔记-JVM模型

    JVM学习笔记 == 标签(空格分隔): jvm 学习笔记全部来自于<深入理解java虚拟机>总结 jvm内存示意图 虚拟机栈(Java Virtual Machine Stacks): ...

  4. JVM学习三:JVM之类加载器之连接分析

    学习完类加载之加载篇后,让我们继续来看加载之连接,连接分为三个步骤:验证.准备和解析三步,我们将一一分析之. 连接就是将已经读入到内存的类的二进制数据合并到虚拟机的运行时环境中去. 类加载完毕之后进入 ...

  5. JVM学习四:深入分析ClassLoader

    一.什么是ClassLoader? 大家都知道,当我们写好一个Java程序之后,不是管是CS还是BS应用,都是由若干个.class文件组织而成的一个完整的Java应用程序,当程序在运行时,即会调用该程 ...

  6. JVM体系结构之二:类加载器之2:JVM 自定义的类加载器的实现和使用

    一.回顾一下jdk自带的类加载器: 1.java虚拟机自带的加载器     根类加载器(Bootstrap,c++实现)     扩展类加载器(Extension,java实现)     应用类加载器 ...

  7. 【JVM学习笔记】系统类加载器

    可以通过“java.system.class.loader"属性指定系统类加载器 默认情况下,该属性值为空: public class Test { public static void m ...

  8. 【JVM学习笔记】扩展类加载器

    扩展类加载器独有的特点,代码如下 public class Sample { } public class Test { static { System.out.println("Test ...

  9. JVM学习--(四)垃圾回收算法

    我们都知道java语言与C语言最大的区别就是内存自动回收,那么JVM是怎么控制内存回收的,这篇文章将介绍JVM垃圾回收的几种算法,从而了解内存回收的基本原理. stop the world 在介绍垃圾 ...

随机推荐

  1. Ubuntu16.04安装oracle-java8-installer

    本篇博客参考 1. 安装默认JRE/JDK 更新 sudo apt-get update 检查是否安装了Java java -version 如果返回The program java can be f ...

  2. C++ Primer Plus学习:第五章

    C++入门第五章:循环和关系表达式 for循环 for循环的组成部分 设置初始值. 执行测试,看循环是否应该继续执行. 执行循环操作. 更新用于测试的值. 以上操作由括号括起,每个部分均是一个表达式, ...

  3. vsftpd:非常安全的ftp服务端程序

    主程序:/usr/sbin/vsftpd 主配置文件:/etc/vsftpd/vsftpd.conf CentOS 6  /etc/rc.d/init.d/vsftpd chkconfig vsftp ...

  4. (二)MySQL中级篇

    1.视图view 视图是一个虚拟表,其内容由查询定义.定义视图的筛选可以来自当前或其它数据库的一个或多个表,或者其它视图. 视图的优点: ①简化了操作,把经常使用的数据定义为视图. ②安全性,用户只能 ...

  5. 201621123037 《Java程序设计》第9周学习总结

    作业09-集合与泛型z 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 这次改一个方式,就不用思维导图了,用图文结合方式来总结 1. Map三视图 键值: S ...

  6. 1st 英文文章词频统计

    英文文章词频统计: 功能:统计一篇英文文章的单词总数及出现频数并输出,之后排序,输出频数前十的单词及其频数. 实现方法:使用C语言,用fopen函数读入txt文件,fscanf函数逐个读入单词,结构体 ...

  7. PAT 甲级 1020 Tree Traversals

    https://pintia.cn/problem-sets/994805342720868352/problems/994805485033603072 Suppose that all the k ...

  8. 【linux】linux的数据流重定向

    首先说一下什么是数据流重定向,所谓数据流重定向简单来说就是一个过程,这个过程捕捉一个文件,或者命令,程序,脚本,甚至脚本中的代码块(code block)的输出,然后把捕捉到的输出,作为输入发送给另外 ...

  9. 一个Vue实例-添加、显示列表、删除

    <link href="~/Content/css/bootstrap-theme.min.css" rel="stylesheet" /> < ...

  10. 【明哥报错簿】之【 "javax.servlet.http.HttpServlet" was not found on the Java Build Path || HttpServletRequest/HttpServletResponse cannot be resolved to a type】

    The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path login ...