Java初始化理解与总结

Java的初始化可以分为两个部分:
(a)类的初始化
(b)对象的创建
(a)类的初始化


**一、概念介绍: ** 一个类(class)要被使用必须经过装载,连接,初始化这样的过程。

  • 在装载阶段,类装载器会把编译形成的class文件载入内存,创建类相关的Class对象,这个Class对象封装了我们要使用的类的类型信息。
  • 连接阶段又可以分为三个子步骤:验证、准备和解析。
  • 验证就是要确保java类型数据格式 的正确性,并适于JVM使用。
  • 准备阶段,JVM为静态变量分配内存空间,并设置默认值,注意,这里是设置默认值,比如说int型的变量会被赋予默认值0 。在这个阶段,JVM可能还会为一些数据结构分配内存,目的是提高运行程序的性能,比如说方法表。
  • 解析过程就是在类型的常量池中寻找类、接口、字段和方法的符号引用,把这些符号引用替换成直接引用。这个阶段可以被推迟到初始化之后,当程序运行的过程中真正使用某个符号引用的时候 再去解析它。
  • 类的初始化:
  • 初始化类,是指初始化static静态变量和执行static静态代码块。
  • 初始化接口,是指初始化定义在该接口中的filed。

二、类的初始化条件:
类会在首次被“主动使用”时执行初始化,以下情况第一次发生之前类会被初始化:

  1. 创建类的实例;
  2. 调用类的静态方法;
  3. 调用类的静态变量,并且该变量不是一个常变量
  4. 为类的静态字段赋值;
  5. 在顶层类中执行assert语句;(?)
  6. 调用类Class及包java.lang.reflect中的某些反射方法;

JLS严格的说明:在任何其他的情况下,都不会对类或接口进行初始化。

三、类的初始化规则:

  1. 类初始化时,该类的父类将首先被初始化,此过程一直递归到 java.lang.Object为止。但是父类实现的接口并不会被初始化。
class Parent implements J{
    { System.out.println("父类初始化块"); }  
    static{
        System.out.println("父类静态初始化块");  
    }
}
class Child extends Parent implements I{
    { System.out.println("子类初始化块"); }  
    static{
        System.out.println("子类静态初始化块");  
    }
}
interface I {  
    int i = Test.out("interface : i", 1);  
}  
interface J {  
    int j = Test.out("interface : j", 2);  
}  
public class Test { 
    static int out(String s, int i) {  
        System.out.println(s + "=" + i);  
        return i;  
    }  
    public static void main(String [] args){
        new Child();
    }
}
接口只有被用到时才会被初始化
输出: 
父类静态初始化块
子类静态初始化块
父类初始化块
子类初始化块
  • 接口初始化时,只会初始化该接口本身,并不会初始化它的父接口。
interface I {  
    int i = Test.out("interface : i", 1);  
    int ii = Test.out("interface : ii", 11);  
}  
interface J extends I{  
    int j = Test.out("interface : j", 2);  
    int jj = Test.out("interface : jj", 22);  
}  
public class Test { 
    static int out(String s, int i) {  
        System.out.println(s + "=" + i);  
        return i;  
    }  
    public static void main(String [] args){
        System.out.println(J.j);  
    }
}
输出:
interface : j=2
interface : jj=22
2
  • 如果类的初始化是由于访问静态域而触发,那么只有真正定义该静态域的类才被初始化,而不会触发超类的初始化或者子类的初始化即使静态域被子类或子接口或者它的实现类所引用。
    示例1:(如上所述)
class Parent{
    static int p = 10;
    static{
        System.out.println("父类静态初始化块");  
    }
}
class Child extends Parent{ 
    static int c = 20; 
    static{
        System.out.println("子类静态初始化块");  
    }
} 
public class Test { 
    public static void main(String [] args){
        System.out.println(Child.p);    //静态域p被子类引用
    }
}
父类静态初始化块
10

示例2:(满足类的初始化条件,父类也会被初始化,与示例1不同)

public class Test { 
    public static void main(String [] args){
        System.out.println(Child.c); 
    }
}
父类静态初始化块
子类静态初始化块
20
  • 如果一个静态变量是编译时常量,则对它的引用不会引起定义它的类的初始化。
    示例1:
class Parent{
    static{
        System.out.println("父类静态初始化块");  
    }
}
class Child extends Parent{
    static final int x = 2005;  
    static{
        System.out.println("子类静态初始化块");  
    }
} 
public class Test { 
    public static void main(String [] args){
        System.out.println(Child.x); 
    }
}
输出:
2005

示例2:(I.i是一个编译时常量,因此它不会引起I被初始化。)

interface I {  
    int i = 1;  
    int ii = Test.out("ii", 2);  
}  
public class Test { 
    static int out(String s, int i) {  
        System.out.println(s + "=" + i);  
        return i;  
    }  
    public static void main(String [] args){
        System.out.println(I.i);  
    }
}
1
  • 初始化类是指初始化static静态变量和执行static静态代码块,所以只会进行一次。
    初始化接口也只会进行一次。
    示例1:
class Child extends Parent{ 
    static int c = 20; 
    static{
        System.out.println("子类静态初始化块");  
    }
} 
public class Test { 
    public static void main(String [] args){
        System.out.println(Child.c); 
        System.out.println(Child.c); 
    }
}
父类静态初始化块
子类静态初始化块
20
20

示例2:

interface J{   
    int j = Test.out("j", 3);  
    int jj = Test.out("jj", 4);  
}    
public class Test { 
    static int out(String s, int i) {  
        System.out.println(s + "=" + i);  
        return i;  
    }  
    public static void main(String [] args){
        System.out.println(J.j);  
        System.out.println(J.j);  
    }
}
j=3
jj=4
3
3

(b)对象创建过程中的Java初始化

一. 对象的创建过程总结

假设有个名为Dog的类:

  1. 当首次创建类型为Dog的对象时,或者Dog类的静态方法/静态域首次被访问时,java解释器必须查找类路径,以定位Dog.class文件。
  2. 然后载入Dog.class(这将创建一个Class对象),有关静态初始化的所有动作都会执行。因此,静态初始化只在class对象首次加载的时候进行一次。
  3. 当用new Dog()创建对象的时候,首先将在堆上为Dog对象分配足够的存储空间。
  4. 这块存储空间会被清零,这就自动地将Dog对象中的所有基本类型数据都设置成了默认值(对数字来说就是0,对布尔型与字符型也相同),而引用则被设置成了null。
  5. 执行所有出现于字段定义处的初始化动作。
  6. 执行构造器。
    ---《Thinking in java》

二. 对象创建过程中初始化顺序
父静态成员>子静态成员>父普通成员初始化>父构造>子普通成员初始化>子构造.
( 静态初始化块以静态变量对待)

  • 示例1:
public class Test { 
    public static void main(String [] args){
        new Child();
    }
}
class Parent{
    { 
        System.out.println("父类普通成员初始化块"); 
    }  
    static{
        System.out.println("父类静态成员及初始化块");
    }
    
    public Parent(){
        System.out.println("父类构造函数"); 
    }
}
class Child extends Parent{
    { 
        System.out.println("子类普通成员初始化块"); 
    }  
    static{
        System.out.println("子类静态成员及初始化块");
    }
    
    public Child(){
        super();
        System.out.println("子类构造函数"); 
    }
}
父类静态成员及初始化块
子类静态成员及初始化块
父类普通成员初始化块
父类构造函数
子类普通成员初始化块
子类构造函数
  • 示例2:(证明 父构造>子普通成员初始化>子构造)
public class Test { 
    public static void main(String [] args){
        new Child();
    }
}
class Parent{
    public Parent(){
        System.out.println("父类构造函数"); 
        System.out.println("子类成员变量 height:" + ((Child)this).height);
    }
}
class Child extends Parent{
    public int height= 20;
    { 
        System.out.println("子类非静态成员初始化块"); 
        System.out.println("子类成员变量 height:" + this.height);
    }  
    
    public Child(){
        super();
        System.out.println("子类构造函数"); 
    }
}
父类构造函数
子类成员变量 height:0
子类非静态成员初始化块
子类成员变量 height:20
子类构造函数

三. 对象创建过程的说明

  1. 静态域的初始化是在类的初始化期间,非静态域的初始化时在类的实例创建期间。这意味这静态域初始化在非静态域之前。
  • 非静态域通过构造器初始化,子类在做任何初始化之前构造器会隐含地调用父类的构造器,这保证了父类非静态实例变量初始化早于子类。
  • 调用Class的类成员变量时,构造函数和成员变量不会执行
public class Test { 
    public static void main(String [] args){
        System.out.println(Parent.a); 
    }
}
class Parent{
    public static int a = 10;
    {
        System.out.println("父类普通成员初始化块");
    }
    public Parent(){
        System.out.println("父类构造函数"); 
    }
}
输出:10
  • 在类的内部,变量定义的先后顺序决定了初始化的顺序;
    即使变量定义散布于方法定义之间,它们仍会在任何方法(包括构造器)被调用之前得到初始化。
public class Test { 
    public static void main(String [] args){
        new Parent(); 
    }
}
class Parent{
    {
        System.out.println("普通成员初始化块1");
    }
    public Parent(){
        System.out.println("构造函数"); 
    }
    {
        System.out.println("普通成员初始化块2");
    }
}
普通成员初始化块1
普通成员初始化块2
构造函数
  • 多态情况下的对象初始化
public class Test {    
  public static void main(String [] args){
      Parent parent = new Child();
  }
}
class Parent{
  { 
      System.out.println("父类普通成员初始化块"); 
  }  
  static{
      System.out.println("父类静态成员及初始化块");
  }

  public Parent(){
      System.out.println("父类构造函数"); 
  }
}
class Child extends Parent{
  { 
      System.out.println("子类普通成员初始化块"); 
  }  
  static{
      System.out.println("子类静态成员及初始化块");
  }

  public Child(){
      super();
      System.out.println("子类构造函数"); 
  }
}
父类静态成员及初始化块
子类静态成员及初始化块
父类普通成员初始化块
父类构造函数
子类普通成员初始化块
子类构造函数

可见多态情况下的初始化与示例1中继承情况下的初始化是一样的,因为都是创建的子类的实例。

  • 程序的执行过程,即Java虚拟机执行Test 的静态方法main,这也会引起Test 类的初始化。(理解面向对象而非面向过程)
public class Test {    
    public static void main(String [] args){
      System.out.println("Test- main");
    }
  
    static{
      System.out.println("静态成员及初始化块");
    }
}
静态成员及初始化块
Test- main

2015/8

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,560评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 小编费力收集:给你想要的面试集合 1.C++或Java中的异常处理机制的简单原理和应用。 当JAVA程序违反了JA...
    八爷君阅读 4,565评论 1 114
  • (一)Java部分 1、列举出JAVA中6个比较常用的包【天威诚信面试题】 【参考答案】 java.lang;ja...
    独云阅读 7,062评论 0 62
  • 寒雨连江夜入吴,平明送客楚山孤。洛阳亲友如相问,一片冰心在玉壶。 唐代诗人王昌龄的一首《芙蓉楼送辛渐》,将“冰心”...
    玉天下阅读 392评论 0 1