问题的引入

还是老规矩,先说说自己遇到的问题。

最近看到了一个比较有意思的Java程序,初次看到这段程序执行的结果还是挺让我意外的,话不多说先上程序,大家也可以揣摩一下(大神自行略过......)

class Singleton{
private static Singleton singleton=new Singleton();
public static int count1=0;
public static int count2; private Singleton(){
count1++;
count2++;
}
public static Singleton getInstance(){
return singleton;
}
} public class MyTest {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
System.out.println("count1:"+instance.count1);
System.out.println("count2:"+instance.count2);
}
}

看到这里我想大家已经有了这个程序的结果了把。不知道大家的结果是否正确:

如果你对这个结果很意外那请你接着往下看吧,嘻嘻。如果你答对了,如果你答对了也建议你看完这篇博文,或许你可以收获一点东西,让你的思路更加清晰。

知识点回顾

(敲黑板了,敲黑板了)
  首先我们需要明确的就是,在Java中静态变量如果在定义时赋初值实际上就是在在静态代码块中赋初值,(这一过程我们可以通过反编译工具查看细节,这里不做赘述);同样的非静态成员的如果在定义时赋初值,实际上就是在构造器的第一行初始化的改变量。

也就是说上面的代码在编译后,会自动将代代码编程这样

class Singleton{
private static Singleton singleton;
public static int count1=0;
public static int count2;
static{
singleton=new Singleton();
count1=0;
} private Singleton(){
count1++;
count2++;
}
public static Singleton getInstance(){
return singleton;
}
} public class MyTest {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
System.out.println("count1:"+instance.count1);
System.out.println("count2:"+instance.count2);
}
}

上面的这个可以通过反编译工具查看,但是部分反编译工具反编译后的效果仍然是我第一次写的那样,但这些都不是重点,我们只需要知道实际上,在定义静态变量时附的初始值实际上在编译后会移到静态代码块中进行,而静态代码块的作用域构造器的优点相似,都是用于初始化,但是不同的是构造器是用于初始化非静态变量的,而静态代码块是用于初始化静态变量的。

上面就是通过XJad反编译工具打开的效果,注意最后的静态代码块(不同的反编译工具,反编译后的代码会有差异)

回顾这一个知识点,就是明确类的静态变量的初始化,是在静态代码块中进行的。

回到正题

我们都知道,一个类在被首次主动使用之前会被类加载进内存并初始化,而在初始化之前,JVM到底做了些什么呢?这里我们来简单的说一下。

类加载的步骤:

第一步:类的加载

第二步:连接

第三步:类的初始化

类的加载

类的加载指的是将.class文件加载进内存

连接

连接就是将已经读入到内存的类的二进制数据合并到虚拟机运行时环境中去。

连接也分为三步:

第一步:验证

确保加载的字节码文件的正确性

第二步:准备

为类的静态变量分配内存,并将初始化默认值。short,int,long的默认值为0,boolean的默认值为false,引用类型的默认                           值为null.

第三步:解析

把类中的符号引用转换为直接引用。

类的初始化

        为类的静态变量赋予正确的初始值,实际上就是执行静态代码块中的内容。

由上面的的描述我们就知道,一个类加载进内存会先为静态变量分配内存,并指定初始值。最后一步才执行初始化。

代码分析

我们在执行Singleton instance = Singleton.getInstance();时,由于此时Singleton还没被加载进虚拟机,所以虚拟机会自动的加载它,在连接阶段会为singleton,count1,count2分配内存,并赋上初始值。在连接阶段完成后会进行类的初始化,这一过程实际上就是执行类的静态代码块,首先会先执行singleton=new Singleton();,执行完毕后,count1和count2都为1。然后执行count1=0,此时count1等于0,count2等于1,这也就是最后输出的结果。

思考:如果把代码改成这样会输出什么?

class Singleton{
public static int count1=0;
private static Singleton singleton=new Singleton();
public static int count2=0; private Singleton(){
count1++;
count2++;
}
public static Singleton getInstance(){
return singleton;
}
} public class MyTest {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
System.out.println("count1:"+instance.count1);
System.out.println("count2:"+instance.count2);
}
}

答案:count1=1 count2=0

总结

一个类被加载进内存分为类的加载、连接、初始化三个阶段。

一个类的静态变量的初始值是在类加载过程中的连接阶段中完成的,而类的初始化过程就是在类的初始化阶段中完成的,这个阶段中会执行静态代码块,为静态变量赋予正确的值。(程序员想要的指定的值)

Java类加载过程&&静态代码块的初始化过程的更多相关文章

  1. java基础课程笔记 static 主函数 静态工具类 classpath java文档注释 静态代码块 对象初始化过程 设计模式 继承 子父类中的函数 继承中的构造函数 对象转型 多态 封装 抽象类 final 接口 包 jar包

    Static那些事儿 Static关键字 被static修饰的变量成为静态变量(类变量) 作用:是一个修饰符,用于修饰成员(成员变量,成员方法) 1.被static修饰后的成员变量只有一份 2.当成员 ...

  2. “全栈2019”Java第四十二章:静态代码块与初始化顺序

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  3. 面向对象设计中private,public,protected的访问控制原则及静态代码块的初始化顺序

    第一:private, public, protected访问标号的访问范围. private:只能由          1.该类中的函数          2.其友元函数访问 不能被任何其他访问,该 ...

  4. Java面试题 静态代码块 构造代码块 构造方法 的执行顺序

    JAVA中的静态代码块 构造代码块 构造方法执行顺序: 静态代码块(类加载时执行)>>构造代码块>>构造方法 下面展示一个简单的例子,推荐大家动手运行一遍: public cl ...

  5. Java之static静态代码块

    Java之static静态代码块 构造代码块 使用{}包裹的代码区域,这里的代码区域特指位于class{}下面的而不是存在于其他type method(){}这类函数下面的代码区域 public cl ...

  6. java基础之静态代码块,局部代码块,构造代码块区别。

    java中有几种常见的代码块,那怎样区别他们呢? 这里就这些问题,浅谈下我个人的理解. 1.局部代码块 局部代码块,又叫普通代码块.它是作用在方法中的代码块.例如: public void show( ...

  7. 5.1JAVA基础复习——JAVA中的静态代码块、构造代码块、构造函数、局部代码块区别

    构造代码块: 在类中定义可以给所有对象进行初始化.局部代码块: 在方法中定义属性的生命周期.静态代码块: 在类中定义用于给类调用时属性的初始化 构造函数与构造代码块的区别: 构造函数是给对应的对象进行 ...

  8. 非静态代码块(非static初始化块)&静态代码块(static初始化块)

    非静态代码块: TestOrder: package com.aff.singleton; /* 类的第四个成员:初始化块(代码块) 代码块: 如果有修饰的话只能使用static 分类:非静态代码块: ...

  9. Java提高篇——静态代码块、构造代码块、构造函数以及Java类初始化顺序

    静态代码块:用staitc声明,jvm加载类时执行,仅执行一次构造代码块:类中直接用{}定义,每一次创建对象时执行.执行顺序优先级:静态块,main(),构造块,构造方法. 构造函数 public H ...

  10. java静态代码块、初始化块和构造方法的执行顺序

    分析:当执行new Child()时,它首先去看父类里面有没有静态代码块,如果有,它先去执行父类里面静态代码块里面的内容,当父类的静态代码块里面的内容执行完毕之后,接着去执行子类(自己这个类)里面的静 ...

随机推荐

  1. Docker之离线安装和在线安装

    一.离线安装 1.软件包下载 https://download.docker.com/linux/static/stable/x86_64/ 2.安装docker tar xvf /opt/docke ...

  2. CentOS下安装OpenSSH 5.8的三种方法【未测试】

    CentOS下安装OpenSSH 5.8的三种方法 Category : 工作学习 / Tags : centos, linux, openssh / Date : 2011.05.19 / 5,29 ...

  3. 重新整理数据结构与算法(c#)——算法套路普利姆算法[二十九]

    前言 看一个题目: 这个问题就是求最小生成树,是图转换为树的一种方式. 最小生成树概念: 最小生成树简称MST. 1.n个顶点,一定有n-1条边 2.包含全部顶点. 3.图转换为最小生成树,权重之和最 ...

  4. 8k中英双语文本嵌入模型效果初探

    一 模型介绍 向量模型用于生成向量表示,被广泛应用于检索.分类.聚类或语义匹配等传统的自然语言处理任务.到了大模型时代,由于上下文长度的限制,需要压缩.存储和查询大量的信息,这就需要用到向量模型对输入 ...

  5. 力扣387(java)-字符串中的第一个唯一字符(简单)

    题目: 给定一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引 .如果不存在,则返回 -1 . 示例 1: 输入: s = "leetcode"输出: 0示例 2: 输 ...

  6. 码住!Flink Contributor 速成指南

    简介: 不管初衷是什么,Flink 都非常欢迎大家一起建设和完善社区.在开始具体的贡献步骤之前,我们先简要介绍一下参与贡献的几种途径,以及 Clarify 关于开源贡献的一些固有印象. 作者:伍翀(云 ...

  7. 深度解析数据湖存储方案Lakehouse架构

    ​简介:从数据仓库.数据湖的优劣势,湖仓一体架构的应用和优势等多方面深度解析Lakehouse架构. 作者:张泊 Databricks 软件工程师 ​ Lakehouse由lake和house两个词组 ...

  8. Flink 和 Pulsar 的批流融合

    简介: 如何通过 Apache Pulsar 原生的存储计算分离的架构提供批流融合的基础,以及 Apache Pulsar 如何与 Flink 结合,实现批流一体的计算. 简介:StreamNativ ...

  9. RedShift到MaxCompute迁移实践指导

    简介: 本文主要介绍Amazon Redshift如何迁移到MaxCompute,主要从语法对比和数据迁移两方面介绍,由于Amazon Redshift和MaxCompute存在语法差异,这篇文章讲解 ...

  10. [Docker] 使 Volume 独立于容器运行时的方式 - 让容器引擎去处理

    在单纯使用 run 命令运行一个容器时,通常会使用 -v 挂载的方式来实现宿主机数据卷映射到容器内. 使用命令: $ docker run --name mysql-con -v /my/custom ...