免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1364 | 回复: 0
打印 上一主题 下一主题

理解Java成员初始化 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-08-05 22:03 |只看该作者 |倒序浏览
在任何程序语言中,对成员的初始化和消除是非常重要的两个工作,而在java中由于有了垃圾回收器,我们一般都不需要对成员进行消除,绝大多数情况下,只需要注意初始化就可以了。
以下是本人最近阅读《Thinking in java 3rd》所做的在java中对成员初始化的总结,结合了书上的讲解和自己的理解,错误之处请大家指出。
为了保证在变量使用前已经对变量进行初始化,java提供以下几种机制:
1,    定义时直接赋值,如 int i=10;
2,    利用 {
//初始化代码
}
3,    用static{ //初始化代码}
4,    用构造器(构造函数)初始化。
5,    将变量赋予默认值,如 int,char型为 0,对象类型为null。
其实以上几种方式理解起来很容易,问题是JVM是按照什么样的顺序进行初始化的,也是很多刚学java的人犯晕的地方。
1,单类中的初始化
最简单的是单个类,没有继承其他类。
看以下例子:
package com.aaron.TIJTest.InitialationTest;
public class InitialationTest {
   
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Initialation ini = new Initialation(1,2,"aaa");
        System.out.println("\n");
        Initialation.message();
        System.out.println("\n\ncreate ini2");
        Initialation ini2=new Initialation(3,4,"bbb");
    }
}
class Initialation{
    int i=10;
    {
        System.out.println("i= "+i);
        i=20;
        System.out.println("i= "+i);
    }
    String s;
    {
        System.out.println("s= "+s);
        s="abcd";
        System.out.println("s= "+s);
    }
    static{
        System.out.println("static initialation block 1");
    }// static block 1
   
    public Initialation(int x,int y,String s){
        System.out.println("Inside Constructor of Initialation");
        i=x;
        j=y;
        this.s=s;
        System.out.println("i= "+i);
        System.out.println("j= "+j);
        System.out.println("s= "+s);
    }
    public static void message(){
        System.out.println("static method message()");
    }
   
    static{
        System.out.println("static initialation block 2");
    }//static block 2
    int j=10;
    {
        System.out.println("j= "+j);
        j=20;
        System.out.println("j= "+j);
    }
   
   
}
输出结果是:
static initialation block 1
static initialation block 2
i= 10
i= 20
s= null
s= abcd
j= 10
j= 20
Inside Constructor of Initialation
i= 1
j= 2
s= aaa
static method message()
create ini2
i= 10
i= 20
s= null
s= abcd
j= 10
j= 20
Inside Constructor of Initialation
i= 3
j= 4
s= bbb
如果在main函数中注释掉
// Initialation ini = new Initialation(1,2);
输出结果:
static initialation block 1
static initialation block 2
static method message()
create ini2
i= 10
i= 20
s= null
s= abcd
j= 10
j= 20
Inside Constructor of Initialation
i= 3
j= 4
s= bbb
为什么呢?
因为初始化的顺序是这样的:
1,    一旦第一次用new 创建一个对象或者调用这个类的静态函数(message()),编译器就寻找这个类的定义。
2,    找到这个类后,首先初始化类成员,按顺序执行static{}块的初始化,即
先出现
static initialation block 1
static initialation block 2
如果不是第一次调用的话,static{}就不再执行,也就是说它只执行一次,从本例子中创建第二对象产生的结果可以看出来。
3,    如果是调用静态函数的话,那么接下来就是直接调用该函数,然后就执行main中调用该静态函数的语句后面的语句。因此注释掉Initialation ini = new Initialation(1,2);就出现以上的结果。
而如果是用new 创建对象的话(没有注释掉Initialation ini = new Initialation(1,2)),就先按照构造的过程,先在内存里分配足够大的空间给Initialation对象,并把所有的成员变量赋予初始值,如 i=0,s=null 。
4,    再接着就按照 i=10,{}这样的顺序初始化,两种的初始化按出现顺序进行,就算是构造函数夹在这些块的,也不执行构造函数的里头代码,而是先将构造函数外的初始化执行。 所以紧接着就出现i=10,i=20…这样的结果。
5,    再接着就是执行构造函数里头的代码。
注意:先 static{},默认值,初始化语句:i=10…{…},最后才是构造函数。
以上是将main函数和类分离,如果将main放在Initialation类中,又会怎么样呢?
public class Initialation {
    int i = 10;
    {
        System.out.println("i= " + i);
        i = 20;
        System.out.println("i= " + i);
    }
    String s;
    {
        System.out.println("s= " + s);
        s = "abcd";
        System.out.println("s= " + s);
    }
    static {
        System.out.println("static initialation block 1");
    }// static block 1
    public Initialation(int x, int y, String s) {
        System.out.println("Inside Constructor of Initialation");
        i = x;
        j = y;
        this.s = s;
        System.out.println("i= " + i);
        System.out.println("j= " + j);
        System.out.println("s= " + s);
    }
    public static void message() {
        System.out.println("static method message()");
    }
    static {
        System.out.println("static initialation block 2");
    }// static block 2
    int j = 10;
    {
        System.out.println("j= " + j);
        j = 20;
        System.out.println("j= " + j);
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println("Inside main");
        Initialation ini = new Initialation(1, 2, "aaa");
        System.out.println("\n");
        Initialation.message();
        
    }
}
输出结果:
static initialation block 1
static initialation block 2
Inside main
i= 10
i= 20
s= null
s= abcd
j= 10
j= 20
Inside Constructor of Initialation
i= 1
j= 2
s= aaa
static method message()
道理和以上说得一样。此时可以简单的将main方法当成普通的static方法。
2,继承体系中的初始化
实际上上面那个初始化也是属于本节中范围,因为任何class继承于Object, 上面的例子只不过为了便于理解而忽略了Object。
看以下的例子:  
// InheritationInitialation.java
public class InheritationInitialation {
   
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Rectangle.message();
        System.out.println();
        new Rectangle();
        System.out.println();
        new Rectangle("another object");
    }
}
class Shape{
    static{
        System.out.println("Shape:static block");
    }
    {
        System.out.println("Shape:non-static block");
    }
    public Shape(){
        System.out.println("Shape Constructor");
    }
    public Shape(String s){
        System.out.println("Shape Constructor with s: "+s);
        
    }
}
class Line extends Shape{
    static{
        System.out.println("Line :static block");
    }
    {
        System.out.println("Line :non-static block");
    }
    public Line(){
        System.out.println("Line Constructor");
    }
    public Line(String s){
        System.out.println("Line Constructor with s: "+s);
        
    }
}
class Rectangle extends Line{
    static{
        System.out.println("Rectangle:static block");
    }
    {
        System.out.println("Rectangle:non-static block");
    }
    public Rectangle(){
        System.out.println("Rectangle Constructor");
    }
    public Rectangle(String s){
        super(s);
        System.out.println("Rectangle Constructor with s: "+s);   
        
    }
    public static void message(){
        System.out.println("static method in Rectangle");
    }
}
结果是:
Shape:static block
Line :static block
Rectangle:static block
static method in Rectangle
Shape:non-static block
Shape Constructor
Line :non-static block
Line Constructor
Rectangle:non-static block
Rectangle Constructor
Shape:non-static block
Shape Constructor
Line :non-static block
Line Constructor with s: another object
Rectangle:non-static block
Rectangle Constructor with s: another object
这里要注意两个问题:
1,    第一次用到类时都是初始化static{}或是static变量,因为编译器此时并不知道你是否需要创建对象,只知道你此时要用到类。
2,    在子类的构造函数中,如果你没有显式的调用父类的构造函数,编译器隐式的调用父类的default构造函数。
也就是说在Rectangle类中的public Rectangle(){}应该是这样的:
Public Rectangle(){
        Super();//编译器自动添加
          System.out.println("Rectangle Constructor");
}
如果父类中没有default构造函数且在子类的构造函数没有显式的调用父类的别的构造函数,则编译出错。
该规则是为了确保子类中从父类继承过来的字段得到初始化。
知道这两点后,再结合单类初始化中的知识,就可以理解继承体系中的初始化顺序:
以下从程序开始讲起(个人的理解)
1,    运行程序java InheritationInitialation, 编译器先找main函数,由于main函数是属于类InheritationInitialation, 所以先对类
InheritationInitialation进行初始化(本例中没有其他的初始化,就只有一个main函数),初始化完毕后调用执行main里头的代码;
2,  遇到Rectangle.message(); 是属于Rectangle类,找到        Rectangle类(至于怎么找,交给编译器),发现继承
了Line类,而Line类又继承了Shape类。
因此先执行Shape类的static{}块的初始化,再执行Line类的Static{}初始化,最后执行Rectangle类的static{}初始化。这是第一步,因为此时只知道要用到这些类,但不清楚是否要创建一个对象。
完成了这些初始化之后,才回来执行
Rectangle.message()里头的代码。
3,接下来执行System.out.println();紧接着遇到new Rectangle();此时要用到Rectangle类,由于这不是第一次使用Rectangle,所以static{}块的初始化代码就不再运行了(可以在main中注释掉Rectangle.message();同样是先输出三个Static{}里头的内容).
遇到new Rectangle() 是先按照单类中的初始化顺序
 初始化Object类中的初始化代码
 然后跑到Shape类按顺序进行成员初始化,最后才执行构造器Shape(){}中的代码。
 接着回到Line类,按顺序进行成员初始化,最后执行构造器Line(){}代码,
 接着执行Rectangle类的初始化,最后执行Rectangle()代码。
至此完成该语句new Rectangle()的执行。
要从年龄最“老“的那个类开始初始化,
4,    执行new Rectangle("another object");顺序同3中描述一样,不过执行的构造函数不一样罢了。
5,    程序结束。
基本的流程就是这样:
1,    递归调用基类的构造函数,先创建根,然后是下一级直到最后一级的构造函数。
2,    每调用一个构造函数呢,先按其所在类的类成员按顺序初始化(按单类里头那样),然后才执行构造函数里面的代码。
以上的内容是看了thinking in java 3rd 之后,对初始化顺序有个彻底的理解,总结了一下,错误之处请指出。


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/52621/showart_1108435.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP