posts - 188,comments - 176,trackbacks - 0


类Person和子类Son,体现对象初始化流程的代码:

public class Person {
    Bow b1 
= new Bow(1);
    
static {
        System.out.println(
"Person staticstatic块1");
    }

    
{
        System.out.println(
"Person hello非static块1");
    }

    Bow b2 
= new Bow(2);
    
static Bow b3 = new Bow(3);
    Person() 
{
        System.out.println(
"PersonPerson类的构造器");
    }

    
{
        System.out.println(
"Person hello非static块2");
    }

    
static {
        System.out.println(
"Person staticstatic块2");
    }

}

class Son extends Person {
    Bow b4 
= new Bow(4);
    
static {
        System.out.println(
"Test staticstatic块1");
    }

    
{
        System.out.println(
"Test hello1非static块1");
    }

    Bow b5 
= new Bow(5);
    
static Bow b6 = new Bow(6);
    Son() 
{
        System.out.println(
"TestTest类的构造器");
    }

    
{
        System.out.println(
"Test hello2非static块2");
    }

    
static {
        System.out.println(
"Test static2static块2");
    }

    
public static void main(String[] args) {
        System.out.println(
"main");
        
new Son();
    }

}

class Bow {
    Bow(
int m) {
        System.out.println(
"Bow(" + m + ")");
    }

}

执行结果:
Person static...static块1
Bow(3)
Person static...static块2
Test static...static块1
Bow(6)
Test static2...static块2
main...
Bow(1)
Person hello...非static块1
Bow(2)
Person hello...非static块2
Person...Person类的构造器
Bow(4)
Test hello1...非static块1
Bow(5)
Test hello2...非static块2
Test...Test类的构造器


从上面结果,可以总结出JAVA对象初始化的流程:
 【1】父类static块和父类static属性(两者的执行先后顺序依据它们在类中具体位置由上至下)
 【2】子类static块和子类static属性(两者的执行先后顺序依据它们在类中具体位置由上至下)
 【3】执行main方法中new关键字之前的代码块
 【4】父类非static块执行和父类非static属性(两者的执行先后顺序依据它们在类中具体位置由上至下,如果属性为实例类型,则执行该实例所在类的非static块和非static属性,再执行该类的构造器,即[4][5]步骤)
 【5】父类构造器(完成非static属性初始值的设置) 
 【6】子类非static块执行和子类非static属性(两者的执行先后顺序依据它们在类中具体位置由上至下,如果属性为实例类型,则执行该实例所在类的非static块和非static属性,再执行该类的构造器,即[6][7]步骤)
 【7】子类构造器(完成非static属性初始值设置,如果在子类构造器执行前就返回该非static属性,则该非static属值为属性默认值
   注:针对第【7】点“子类构造器执行前就返回该非static属性”,具体可以见下例:
public class ParentObjectInit {

    
public void test() {

    }

    
public ParentObjectInit() {
        test();
    }

}

class ChildObjectInit extends ParentObjectInit {
    
private int instanceValue = 20;
    
public void test() {
        System.out.println(
"instance1 value : " + instanceValue);
    }

    
public static void main(String[] args) {
        
new ChildObjectInit();
    }

}
这段代码的执行结果:
instance1 value is: 0 因为new 子类ChildObjectInit时,会先初始化父类ParentObjectInit,继而调用父来构造器器,接着执行此构造器中的test()方法,基于多态性,会转而调用子类的test()方法执行子类test()方法代码体,打印出instanceValue的属性默认值。(因为此时打印instanceValue值后,父类构造器才执行结束返回,根本还没轮到子类的初始化工作和子类构造器的执行。)
posted on 2008-05-24 21:26 cheng 阅读(2176) 评论(3)  编辑  收藏 所属分类: JBS

FeedBack:
# re: 对象初始化流程梳理
2008-05-25 15:12 | 隔叶黄莺
1. 初始化分为为的初始化和实例的初始化
2. 每个类在 JVM 中都对应一个 Class 实例
3. 父类实例是作为子例的部分存在的 (Class 实例之间也存在父子关系)
4. 初始化实例之前要初始化类

基于以上几点就可以理解以下初始化顺序
1. 父类静态属性、静态类 (父类的初始化,对应 JVM 方法 cinit())
2. 子类的静态属性、静态类 (子类的初始化,对应 JVM 方法 cinit())
3. 父类的实例构造,实例变量初始化 (实例变量初始实际会放到 JVM 的 init() 中)
4. 子类的实例构造,实例变量初始化 (也是对应的 init() 方法)

关于类的 Class 实例,可以回想 Log 的声明
Log log = LogFactory.getLog(TestClass.class);
也就是无论你,new 多少个 TestClass 实例,它们对应着同一个 TestClass 的 Class 实例,也就是为什么很多地方把静态方法、静态属性说成是类的方法、类的属性,其实质就是在 JVM 中存在同一个 Class 实例的方法、属性,也就能保持一致性。

关于父类实例是作为子类的一部分存在,可借鉴 C++ 或是有面向对象特性的 C 函数库(如 gtk),来理解,父类实例会居于子类实例的首地址,所以对子类转型成父类实例时,它是安全的,因为首地址一样的,所以从首地址到 size(父类)就是父类实例地址空间,到size(子类)就是子类实例的地址空间。  回复  更多评论
  
# re: 对象初始化流程梳理
2008-05-26 14:32 | cheng
看过楼上朋友见解,体会又深一层,谢谢。  回复  更多评论
  
# re: 对象初始化流程梳理
2008-05-26 14:32 | Happy漫步者
不错 收藏了  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航: