1、java初始化基础知识:
Object
System.out.println( }
Initialization init = init.print(); } |
输出结果为:
int a=0 char b= short s=0 float f=0.0 long lo=0 double dou=0.0 byte e=0 boolean flag=false Object obj=null |
可见,java会为类的基本类型的变量提供一个初始值,各类型初始值不同,非基本类型初始为null。
static数据的初始化:
static 数据会发生上述同样的事情(基本类型,获得对应基本类型的初始化值;非基本类型,初始化为null) 。但是,由于
static值只有一个存储区域
,所以
static值只会被初始化一次
,看下面的例子:
System. } }
System.out.println( } }
Cupboard cup =
cup = } |
输出结果:
init ing Bowl~ initialization Cupboard initialization Cupboard |
所以说,static数据只会在第一次进行初始化,之后就不会了。
其实,当我们创建一个类之后,这个类可能包含一些静态变量和一些非静态变量。当我们创建了这个类的很多实例之后,这些许许多多的实例都有自己的一块独立空间来存放自己的数据,
而这些数据就是非静态变量
。静态变量的数据是唯一存放一块存储区域中,每个实例都有一个指向这块区域的指针,这就是每个实例也能调用静态变量的原因。
2、初始化顺序:
首先我们要知道,
当我们通过构造函数来初始化某个实例时,这个实例的成员域和成员方法是先于构造方法被初始化的
。举个例子:
System. } }
System. }
Test02 test = } } |
输出结果是:
Test01 of constractor : 1 Test01 of constractor : 2 Test02 of constructor : 10 |
通过输出,可见当生成Test02的实例test时,它并不是首先调用其构造方法而是
先是成员变量的初始化,而且成员的初始化的顺序以成员变量的定义顺序有关,先定义的先初始化
,初始化后再调用构造方法。
其实成员变量的初始化,在类的所有方法调用之前进行,包括构造方法
。
这也好理解,我们经常用构造方法来对某些成员变量进行操作,有时也要调用成员方法,既然能调用,肯定是成员域和成员方法先初始化了嘛。
实例变量初始化器与实例初始化器 :
实例变量初始化器:我们可以在定义实例变量的同时,对实例变量进行赋值。比如:
} |
如果我们以这种方式为实例变量赋值,那么在
构造函数执行之前会先完成这些初始化操作
。
那么我们知道如果一个类的成员变量没有在定义时,系统会给予系统默认的值,当我们像上面那样对实例变量赋值时,系统在给予初值和实例变量初始化器赋初值这两种方式,在执行时间上有先后吗?为了解决这个问题,我们看看下面的例题:
i1++; i2++; }
Test04 t2 =
System.out.println(
System.out.println( } } |
运行结果:
t2.i1 = 2 t2.i2 = 3 |
为什么是这种结果呢?其实整个过程是这样的:首先执行给t1,i1,i2分别给予初始值null,0,0,再执行Test04 t1 =new Test04(),这样i1++,i2++被执行,i1,i2都变为1,执行完毕后接着执行int i1; i1,i2的值仍然是1,1,当执行int i2 = 2时i2被赋予了值,即i1 = 1,i2=2;再执行Test04 t2 = new Test04(),i1,i2再执行++,此时i1 =2,i2 =3,输出i1,i2,结果就是:t2.i1 = 2,t2.i2 = 3。 通过上面的代码我们可以认为
系统默认值的给予比通过等号的赋予先执行
。
接着,我们加上普通变量,试验一下,一个类同时含有static变量与普通变量时,两种变量定义与初始化的顺序:
User02(){
System. } }
} |
运行结果:
i=0, j=2 i=1, j=2 |
通过这个结果,表面上是普通变量的定义初始化这两个步骤处于静态变量的初始化和定义之间,但我认为首先定义及初始化静态变量,当运行到new User02()时,再定义及初始化普通变量。
实例初始化器:
{
} } |
上面代码中花括号内代码,在Java中就被称作实例初始化器,
其中的代码同样会先于构造函数被执行
。
其实两种初始化器都是一样的,实例变量初始化器只是把两个步骤表面上合成了一个,但内部还是分两步:先定义变量并赋予默认值,接着对变量赋值实行初始化。
如果我们定义了实例变量初始化器与实例初始化器,那么
编译器会将其中的代码放到类的构造函数中去,这些代码会被放在对超类构造函数的调用语句之后
(Java强制要求Object对象之外的所有对象构造函数的第一条语句必须是超类构造函数的调用语句或者是类中定义的其他的构造函数,如果我们即没有调用其他的构造函数,也没有显式调用超类的构造函数,那么编译器会为我们自动生成一个对超类构造函数的调用指令),
构造函数本身的代码之前
。
Java是按照编程顺序来执行实例变量初始化器和实例初始化器中的代码的,并且不允许顺序靠前的实例初始化器或者实例变量初始化器使用在其后被定义和初始化的实例变量。所以下面的两段代码编译是不会通过的:
{
}
} |
} |
之所以不会通过是因为系统认为i是未经定义的变量,不过下面的代码却绕过了这个检查:
}
}
InstanceInitializer ii =
System. } } |
最后输出的值是0。为什么是0呢?原因就是首先程序先定义j和i,并赋初值0,接着先运行
j
= getI()语句,getI()方法返回i值,i此时当然是0嘛,所以j就是0,接着运行
i
= 1语句,接着再运行构造函数中的
i
= 2语句。
刚刚讲述了一个类(不讲继承)中初始化的顺序,下面我们加上继承,加上静态变量,我们来看看整个顺序是怎么样的:
{ Sample(String s)
{
System. } Sample()
{
System. } }
System. }
Sample
System. } Test()
{
System. }
Sample }
{
TestSub()
{
System. }
Sample
Sample }
{
} |
输出结果如下:
父类 static 块 1 执行 父类 静态成员staticSam1初始化 父类 静态成员staticSam2初始化 父类 static 块 2 执行
子类 静态成员staticSamSub初始化 子类 静态成员staticSamSub1初始化 子类 static 块 执行
父类 sam1成员初始化 父类 sam2成员初始化 父类 Test默认构造函数被调用
子类 sam1成员初始化 子类 sam2成员初始化 子类 TestSub 默认构造函数被调用
|
由此我们可以得出java初始化顺序的结论:
1 继承体系的所有静态成员初始化(
先父类,后子类
);
2 父类初始化完成(普通成员的初始化–>构造函数的调用);
3 子类初始化(普通成员–>构造函数);
说到继承还要讲一点,子类构造函数中的内容开始运行前肯定是要先初始化父类的成员然后调用父类的构造函数,但如果父类的构造函数中调用了自己的一个与子类重名的方法,那系统会调用哪个呢?我们来看例程:
System.out.println( m(); }
System.out.println( } }
System.out.println(
m(); }
System.out.println( }
SuperClass t = } } |
运行结果是:
SuperClass of constructor SubClass.m(): i = 0 SubClass of constructor SuperClass.m() SubClass.m(): i = 10 |
在生成对象时,父类调用的M()方法,不是父类的 M()方法,而时子类中被重写了的M()方法!!并且还出现一个怪异的现象,子类的privte int i 也被父类访问到,这不是和我们说private的成员只能在本类使用的原则相违背了吗?其实我们说的这条原则是编译期间所遵守的,在JAVA程序的编译期间,它只检查语法的合法性,在JAVA的JVM中,即运行期间,不管你声明的什么,对于JVM来说都是透明的,而多态是在运行期间执行的,所以能拿到SubClass的private成员,一点都不奇怪,只是此时还没执行 i = 10,所以在父类的构造方法中调用m()时,系统只能将i赋予系统初值0。
运用以上知识解释下面的例程:
一:
System.
System.
System. }
System.out.println(
System.out.println( j = 10; m();
System.out.println(
System.out.println(
System.out.println( }
System.out.println(
}
System.out.println(
System.out.println(
System.out.println(
System.out.println( }
System.out.println( } }
System.
System. }
System.out.println( m();
System.out.println( }
System.out.println( } }
A a = } } |
输出结果为:
— Load First SuperClass of static block start!– j = 0 — Load First SuperClass of static block End — Load SuperClass.getInt() — Load Second SuperClass of static block!——- j = 0 k = 11 — Load Second SuperClass of static block End — —- Load SubClass of static block!—— — Load SubClass of static block End — ——- Load SuperClass of structor start ——– Frist print j = 0 SubClass.m() ,a = 0 k = 11 Second print j = 10 ———– Load SuperClass End ———– Load SubClass of structor SubClass.m() ,a = 10 — Load SubClass End —- |
二:
}
System. } }
User02 u02=User02.getUser02();
System. } } |
上面的输出结果是:
1,5。
整个过程是这样的:首先程序执行到User02 u02=User02.getUser02();,接着初始化User02的静态成员变量u02和n,此时只是对这两个静态变量进行简单的定义,赋予他们默认值u02=null,n=0。然后再运行
u02
=
new
User02();,调用User02的构造函数,构造函数中对n进行了自加,所以n输出1,好了,这时u02初始化完毕,轮到n了,于是n就重新被赋值5,所以在main函数中输出的是5。
但是当我把
static
int
n
=5;语句中的static去掉之后,系统就会先执行
int
n
=5;,接着再执行构造器中的n++,,所以最后输出的结果是6,6。此时系统还是先初始化User02中的静态变量u02,为其设置默认值null,然后做的事不是初始化n为其设默认值0,而是要运行
u02
=
new
User02();接着初始化u02。当调用构造函数之前,
要对对象的成员域和成员方法首先进行初始化
,所以此时运行语句
int
n
=5;,接着构造函数中的动作开始,n自加成6,所以两处输出都为6.
三:
}
User01
System. } }
}
User
System. } }
User u=User.getUser(); User01 u01=User01.getUser01();
System.
System. } } |
输出结果:
User01新建一次 |
本篇文章是对以下网页的总结:
http://blog.csdn.net/yangyan19870319/article/details/6202403
http://mysun.iteye.com/blog/1596959
http://blog.163.com/douspirit@126/blog/static/92463502007101543613550/
http://www.ibm.com/developerworks/cn/java/j-lo-clobj-init/
http://www.cnblogs.com/miniwiki/archive/2011/03/25/1995615.html