首先根据下面的这个一段代码:引入关于java初始化顺序的问题
public class InitationTest extends Person {
public InitationTest() {
System.out.println("InitationTest constructor");
} static int j = 10;
static {
System.out.println("j="+j);
System.out.println("InitationTest static...");
} public static void main(String[] args) {
InitationTest obj = new InitationTest();
}
} class Person {
static {
// System.out.println("i="+i); Cannot reference a field before it is
// defined
i = 10;
System.out.println("Person static...");
}
static int i = 5; Person() {
System.out.println("Person constructor");
}
}
打印依次为:

Person static...
j=10
InitationTest static...
Person constructor
InitationTest constructor

想要理解类中初始化顺序,就必须先理解jvm加载原理

一:jvm加载顺序和原理

类的初始化顺序有点类似jvm中类加载器的模式:(双亲委派模型)的工作过程为:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的加载器都是如此,因此所有的类加载请求都会传给顶层的启动类加载器,只有当父加载器反馈自己无法完成该加载请求(该加载器的搜索范围中没有找到对应的类)时,子加载器才会尝试自己去加载。都是从父类中开始,当然类加载器并不是继承关系而是组合关系.

类从被加载到虚拟机内存中开始,到卸载出内存为止,它的生命周期包括了:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸载(Unloading)七个阶段,其中验证、准备、解析三个部分统称链接

1.加载:

1.通过“类全名”来获取定义此类的二进制字节流

2.将字节流所代表的静态存储结构转换为方法区的运行时数据结构

3.在java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口

2.验证:

1.文件格式验证

2.元数据验证

3.字节码验证

4.符号引用验证

3.准备:

准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。这个阶段中有两个容易产生混淆的知识点,首先是这时候进行内存分配的仅包括类变量(static 修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在java堆中。其次是这里所说的初始值“通常情况”下是数据类型的零值,假设一个类变量定义为:

public static int value  = 12;

那么变量value在准备阶段过后的初始值为0而不是12,因为这时候尚未开始执行任何java方法,而把value赋值为12的putstatic指令是程序被编译后,存放于类构造器<clinit>()方法之中,所以把value赋值为12的动作将在初始化阶段才会被执行。

上面所说的“通常情况”下初始值是零值,那相对于一些特殊的情况,如果类字段的字段属性表中存在ConstantValue属性,那在准备阶段变量value就会被初始化为ConstantValue属性所指定的值,建设上面类变量value定义为:

public static final int value = 123;

编译时javac将会为value生成ConstantValue属性,在准备阶段虚拟机就会根据ConstantValue的设置将value设置为123

4.解析:

解析阶段是虚拟机常量池内的符号引用替换为直接引用的过程

5.初始化:

类的初始化阶段是类加载过程的最后一步,在准备阶段,类变量已赋过一次系统要求的初始值,而在初始化阶段,则是根据程序员通过程序制定的主观计划去初始化类变量和其他资源,或者可以从另外一个角度来表达:初始化阶段是执行类构造器<clinit>()方法的过程。在以下四种情况下初始化过程会被触发执行:

1.遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需先触发其初始化。生成这4条指令的最常见的java代码场景是:使用new关键字实例化对象、读取或设置一个类的静态字段(被final修饰、已在编译器把结果放入常量池的静态字段除外)的时候,以及调用类的静态方法的时候。

2.使用java.lang.reflect包的方法对类进行反射调用的时候

3.当初始化一个类的时候,如果发现其父类还没有进行过初始化、则需要先出发其父类的初始化

4.jvm启动时,用户指定一个执行的主类(包含main方法的那个类),虚拟机会先初始化这个类

在上面准备阶段 public static int value  = 12;  在准备阶段完成后 value的值为0,而在初始化阶调用了类构造器<clinit>()方法,这个阶段完成后value的值为12。

*类构造器<clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static块)中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的,静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句快可以赋值,但是不能访问。

二:加载顺序:

(1)静态内容:静态的成员变量、静态代码块、静态的成员方法按顺序加载。

(2)非静态内容:成员变量、代码块、成员方法按顺序加载。

总顺序:静态内容--》非静态内容--》类构造方法

编译器生成的class文件主要对定义在源文件中的类进行了如下的更改:

1) 先按照(静态or非静态)成员变量的定义顺序在类内部声明成员变量。

2) 再按照原java类中对(静态or非静态)成员变量的初始化顺序进行初始化。

左图是源文件,右图类似编译后的文件:

   

java类成员变量与代码块初始化的更多相关文章

  1. java中成员变量、代码块、构造函数运行顺序

    1.java虚拟机执行程序,首先须要装载类,安装现装载父类,初始化父类的静态代码块和静态成员变量 再load子类. 初始化子类静态代码块和成员变量 2.load完成父类与子类后,从main函数入口运行 ...

  2. Java类成员变量、普通成员变量、初始化块、构造方法的初始化和执行顺序

    结论:执行的大致顺序如下, (1) 在一个不存在继承的类中:初始化static变量,执行static初始化块-->初始化普通成员变量(如果有赋值语句),执行普通初始化块-->构造方法 (2 ...

  3. java 子类的实例化和代码块初始化过程

    1,子类的实例化 1,子父类中的构造函数的特点. 在子类构造对象时,发现,访问子类构造函数时,父类也运行了. 为什么呢? 原因是:在子类的构造函数中第一行有一个默认的隐式语句. super(); 子类 ...

  4. 【转】Java类成员变量默认初始化规则

    一个变量作为类成员使用的时候,如果没有被初始化,java会为其分配默认值: -------------------------------------------------------------- ...

  5. java 类、方法、代码块修饰式关键字总结

    super 关键字 this和super的区别 访问成员的区别 this关键字 this特点 this使用场景 static关键字 例子 访问权限修饰符 特点 总结: 四个修饰符的特点 访问权限修饰符 ...

  6. Java类成员变量的默认值

    1.布尔型(boolean)变量默认值为false,byte.short.int.long为0,字符型为'\u0000'(空字符),浮点型(float double)为0.0,引用类型(String) ...

  7. Delphi会自动初始化全局变量和类成员变量,但不初始化局部变量

    If you don't explicitly initialize a global variable, the compiler initializes it to 0. Object insta ...

  8. Java类的成员之四:代码块.

    3.2类的成员之四:代码块 ①初始化块(代码块)作用:对Java对象进行初始化 ②程序的执行顺序: ③一个类中初始化块若有修饰符,则只能被static修饰,称为静态代码块(static block ) ...

  9. java 面向对象(二十):类的结构:代码块

    类的成员之四:代码块(初始化块)(重要性较属性.方法.构造器差一些)1.代码块的作用:用来初始化类.对象的信息2.分类:代码块要是使用修饰符,只能使用static分类:静态代码块 vs 非静态代码块3 ...

随机推荐

  1. easyui datagrid自定义按钮列,即最后面的操作列(转)

    做项目的时候因为需求,要在表格的最后添加一列操作列,easyUI貌似没有提供这种功能,不过没关系,我们可以自定义来实现 版本:jQuery easyUI 1.3.2 这里我的实现方式是采用HTML形式 ...

  2. github+hexo搭建自己的博客网站(三)主题之外的一些基本配置(图片位置,文章目录功能)

    使用的yilia主题之后,还需要进行自己的定制配置 1.图片的位置 比如打赏的支付宝二维码图片,是在当前博客的source/assets/img/下 (不是当前主题) 配置:(在yilia主题下文件里 ...

  3. SVG交互动画制作

    前面我们已经说过了要怎样制作CSS3动画,但是SVG动画一直都没有时间研究过,正好趁现在有一点时间静下心来研究一下. 一般来说,常见前端动画实现方案分为三种,CSS3动画,HTML动画(SVG动画), ...

  4. python注释方法以及编码问题

    一.单行注释 在python中常使用"#"来进行单行注释,其快捷键为"ctrl+/",如果要对多行代码也就是代码块进行注释时,也可以选中多行按下 "c ...

  5. postman 第6节录制case

    我们先安装Postman Interceptor 记得一定要安装Postman Interceptor插件,这样在谷歌浏览器上访问的信息都会在postman的app端同步显示.安装好后Postman ...

  6. MySql数据库导入导出

    1.导出整个数据库     mysqldump -u 用户名 -p 数据库名 > 存放位置     比如:     mysqldump -u root -p project > c:/a. ...

  7. 富文本编辑器UEditor的配置使用方法

    将下载的富文本编辑器的文件解压后放到 webcontent 下 如果 文件中的jsp文件夹下的controller.java文件报错的话    就将jsp下的lib文件夹中的文件都复制到  web-i ...

  8. spring整合mybatis错误:Could not autowire field: com.kjczwl.ssm.service.ItemsService com.kjczwl.ssm.controller.ItemsController.itemsservice;

    运行环境:jdk1.7.0_17+tomcat 7 + spring:3.2.0 +mybatis:3.2.7+ eclipse 错误:Could not autowire field: com.kj ...

  9. Tomcat利用Redis存储Session

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt269 如果英文不错的看,建议直接看官网吧,官网写的挺清楚.下面的内容是转载的 ...

  10. dubbo+zookeeper伪集群配置

    zookeeper作为注册中心,服务器和客户端都要访问,如果有大量的并发,肯定会有等待.所以可以通过zookeeper集群解决. 一.为什么需要zookeeper呢? 大部分分布式应用需要一个主控.协 ...