java类成员初始化_理解Java成员初始化

本文详细探讨了Java中成员初始化的顺序,包括直接赋值、初始化块、构造器以及静态块等。在单类和继承体系中,初始化顺序遵循特定规则,例如先执行静态块,然后是实例成员的默认初始化,接着是初始化块和构造器。对于继承,会从基类到子类递归调用构造函数,并按类中成员声明顺序进行初始化。理解这一顺序对于避免编程错误至关重要。
摘要由CSDN通过智能技术生成

在任何程序语言中,对成员的初始化和消除是非常重要的两个工作,而在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 之后,对初始化顺序有个彻底的理解,总结了一下,错误之处请指出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值