📄 java中的初始化类、变量、程序块加载探讨-java面向对象 - it电子教育门户 高端java培训.htm
字号:
"+d);<BR> }<BR> public static void main(String[]
args) {<BR> InitialValues iv=new
InitialValues();<BR>
iv.printInitialValues();<BR> }<BR>}<BR>结果:<BR>boolean
false<BR>char _<BR>byte 0<BR>short
0<BR>int 0<BR>long 0<BR>float
0.0<BR>double
0.0<BR> <BR>2.变量初始化<BR>
在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量定义散布于方法定义之间,它们仍旧在任何方法(包括构造器)被调用之前得到初始化。看下面的代码:<BR>OrderOfInitialzation.java(执行顺序在代码中已标出,按类标注,罗马字母标注主类中执行顺序。)<BR>class
Tag{<BR> Tag(int marker){<BR>
System.out.println("Tag("+marker+")");<BR> }<BR>}<BR>class
Card{<BR> Tag t1=new
Tag(1);//Ⅰ①<BR> Card(){<BR>
System.out.println("Card()");//Ⅰ④<BR> t3=new
Tag(33);//Ⅰ⑤<BR> }<BR> Tag t2=new
Tag(2);//Ⅰ②<BR> void f(){<BR>
System.out.println("f()");//Ⅱ⑥<BR> }<BR> Tag t3=new
Tag(3);//Ⅰ③<BR>}<BR>public class OrderOfInitialzation
{<BR> public static void main(String[] args) {<BR>
Card t=new Card();//Ⅰ<BR>
t.f();//Ⅱ<BR> }<BR>}<BR>结果:<BR>Tag(1)<BR>Tag(2)<BR>Tag(3)<BR>Card()<BR>Tag(33)<BR>f()<BR> <BR>3.静态数据初始化<BR>看下面的代码:<BR>StaticInitialization
.java<BR>class Bowl{<BR> Bowl(int marker){<BR>
System.out.println("Bowl("+marker+")");<BR> }<BR> void
f(int marker){<BR>
System.out.println("f("+marker+")");<BR> }<BR>}<BR>class
Table{<BR> static Bowl b1=new
Bowl(1);//Ⅰ①<BR> Table(){<BR>
System.out.println("Table()");//Ⅰ③<BR>
b2.f(1);//Ⅰ④<BR> }<BR> void f2(int
marker){<BR>
System.out.println("f2("+marker+")");<BR> }<BR> static
Bowl b2=new Bowl(2);//Ⅰ②<BR>}<BR>class Cupboard{<BR> Bowl
b3=new Bowl(3);//Ⅱ③,Ⅳ①,Ⅵ①<BR> static Bowl b4=new
Bowl(4);//Ⅱ①<BR> Cupboard(){<BR>
System.out.println("Cupboard");//Ⅱ④,Ⅳ②,Ⅵ②<BR>
b4.f(2);//Ⅱ⑤,Ⅳ③,Ⅵ③<BR> }<BR> void f3(int
marker){<BR>
System.out.println("f3("+marker+")");<BR> }<BR> static
Bowl b5=new Bowl(5);//Ⅱ②<BR>}<BR>public class
StaticInitialization {<BR> public static void
main(String[] args) {<BR> System.out.println("Creating
new Cupboard() in main");//Ⅲ<BR> new
Cupboard();//Ⅳ<BR> System.out.println("Creating new
Cupboard() in main");//Ⅴ<BR> new
Cupboard();//Ⅵ<BR> t2.f2(1);//Ⅶ<BR>
t3.f3(1);//Ⅷ<BR> }<BR> static Table
t2=new Table();//Ⅰ<BR> static Cupboard
t3=new
Cupboard();//Ⅱ<BR>}<BR>结果:<BR>Bowl(1)<BR>Bowl(2)<BR>Table()<BR>f(1)<BR>Bowl(4)<BR>Bowl(5)<BR>Bowl(3)<BR>Cupboard<BR>f(2)<BR>Creating
new Cupboard() in
main<BR>Bowl(3)<BR>Cupboard<BR>f(2)<BR>Creating new Cupboard()
in
main<BR>Bowl(3)<BR>Cupboard<BR>f(2)<BR>f2(1)<BR>f3(1)<BR>
由输出可见,静态初始化只有在必要时刻才会进行。如果不创建Table
对象,也不引用Table.b1或Table.b2,那么静态的Bowl b1 和b2 永远都不会被创建。只有在第一个Table
对象被创建(或者第一次访问静态数据)的时候,它们才会被初始化。此后,静态对象不会再次被初始化。
<BR>
初始化的顺序是先“静态”,(如果它们尚未因前面的对象创建过程而被初始化),后“非静态”。从输出结果中可以观察到这一点。<BR> <BR>4.静态块的初始化<BR>
Java
允许你将多个静态初始化动作组织成一个特殊的“静态子句”(有时也叫作“静态块”)。与其他静态初始化动作一样,这段代码仅执行一次:当你首次生成这个类的一个对象时,或者首次访问属于那个类的一个静态成员时(即便从未生成过那个类的对象)。看下面的代码:<BR>class
Cup{<BR> Cup(int marker){<BR>
System.out.println("Cup("+marker+")");<BR> }<BR> void
f(int marker){<BR>
System.out.println("f("+marker+")");<BR> }<BR>}<BR>class
Cups{<BR> static Cup c1;<BR> static Cup
c2;<BR> static{<BR> c1=new Cup(1);<BR> c2=new
Cup(2);<BR> }<BR> Cups(){<BR>
System.out.println("Cups()");<BR> }<BR>}<BR>public class
ExpilicitStatic {<BR> public static void main(String[]
args) {<BR> System.out.println("Inside
main()");<BR> Cups.c1.f(99);//(1)<BR> }<BR>//
static Cups x=new Cups();//(2)<BR>// static Cups y=new
Cups();//(2)<BR>}<BR>结果:<BR>Inside
main()<BR>Cup(1)<BR>Cup(2)<BR>f(99)<BR>
无论是通过标为(1)的那行程序访问静态的 c1对象,还是把(1)注释掉,让它去运行标为(2) 的那行,Cups
的静态初始化动作都会得到执行。如果把(1)和(2)同时注释掉,Cups
的静态初始化动作就不会进行。此外,激活一行还是两行(2)代码都无关紧要,静态初始化动作只进行一次。<BR> <BR>5.非静态实例初始化<BR>看下面的代码:<BR>class
Mug{<BR> Mug(int marker){<BR>
System.out.println("Mug("+marker+")");<BR> }<BR> void
f(int marker){<BR>
System.out.println("f("+marker+")");<BR> }<BR>}<BR>public
class Mugs {<BR> Mug c1;<BR> Mug
c2;<BR> {<BR> c1=new Mug(1);<BR> c2=new
Mug(2);<BR> System.out.println("c1&c2
initialized");<BR> }<BR> Mugs(){<BR>
System.out.println("Mugs()");<BR> }<BR> public
static void main(String[] args) {<BR>
System.out.println("Inside main()");<BR> new
Mugs();<BR> }<BR>}<BR>结果:<BR>Inside
main()<BR>Mug(1)<BR>Mug(2)<BR>c1&c2
initialized<BR>Mugs()<BR> <BR>6.数组初始化<BR>
注意区分基本类型数据与类数据的初始化。看以下代码:<BR>int[] a; <BR>a = new
int[rand.nextInt(20)]; <BR>与<BR>Integer[] a = new
Integer[rand.nextInt(20)]; <BR>for(int i = 0; i <
a.length; i++) { <BR> a[i] = new
Integer(rand.nextInt(500)); <BR>}<BR>
对于类数据类型的初始化,每个数组子成员都要重新new一下。<BR> <BR>7.涉及继承关系的初始化<BR>
当创建一个导出类的对象时,该对象包含了一个基类的子对象。看下面代码:<BR>class
Art{<BR> Art(){<BR> System.out.println("Art
constructor");<BR> }<BR>} <BR>class Drawing extends
Art{<BR> Drawing(){<BR> System.out.println("Drawing
constructor");<BR> }<BR>}<BR>public class Cartoon extends
Drawing{<BR> public Cartoon(){<BR>
System.out.println("Cartoon
constructor");<BR> }<BR> public static void
main(String[] args) {<BR> new
Cartoon();<BR> }<BR>}<BR>结果:<BR>Art
constructor<BR>Drawing constructor<BR>Cartoon
constructor<BR>
可以发现,构建过程是从基类“向外”扩散的,所以基类在导出类构造器可以访问它之前,就已经完成了初始化。<BR>
如果类没有缺省的参数,或者想调用一个带参数的基类构造器,就必须用关键字super显示地调用基类构造器的语句,并且配以适当的参数列表。看下面代码:<BR>class
Game{<BR> Game(int i){<BR> System.out.println("Game
constructor");<BR> }<BR>} <BR>class BoardGame extends
Game{<BR> BoardGame(int i){<BR> super(i);<BR>
System.out.println("BoardGame
constructor");<BR> }<BR>}<BR>public class Chess extends
BoardGame{<BR> Chess(){<BR> super(11);<BR>
System.out.println("Chess
constructor");<BR> }<BR> public static void
main(String[] args) {<BR> new
Chess();<BR> }<BR>}<BR>结果:<BR>Game
constructor<BR>BoardGame constructor<BR>Chess
constructor<BR>
如果不在BoardGame()中调用基类构造器,编译器将无法找到符合Game()形式的构造器。而且,调用基类构造器必须是你在导出类构造器中要做的第一件事。<BR> <BR>8.构造器与多态<BR>8.1构造器的调用顺序<BR>
基类的构造器总是在导出类的构造过程中被调用的,而且按照继承层次逐渐向上链接,以使每个基类的构造器都能得到调用。这样做是有意义的,因为构造器具有一项特殊的任务:检查对象是否被正确地构造。导出类只能访问它自己的成员,不能访问基类中的成员(基类成员通常是private类型)。只有基类的构造器才具有恰当的知识和权限来对自己的元素进行初始化。因此,必须令所有构造器都得到调用,否则就不可能正确构造完整对象。这正是编译器为什么要强制每个导出类部分都必须调用构造器的原因。<BR> <BR>8.2构造器内部的多态方法的行为<BR>看下面代码:<BR>abstract
class Glyph{<BR> abstract void
draw();<BR> Glyph(){<BR>
System.out.println("Glyph() before draw()");<BR>
draw();<BR> System.out.println("Glyph() after
draw()");<BR> }<BR>} <BR>class RoundGlyph extends
Glyph{<BR> private int radius=1;<BR> RoundGlyph(int
r){<BR> radius=r;<BR>
System.out.println("RoundGlyph.RoundGlyph(),radius="+radius);<BR> }<BR> void
draw(){<BR>
System.out.println("RoundGlyph.draw(),radius="+radius);<BR> }<BR>}<BR>public
class PolyConstructors {<BR> public static void
main(String[] args) {<BR> new
RoundGlyph(5);<BR> }<BR>}<BR>结果:<BR>Glyph() before
draw()<BR>RoundGlyph.draw(),radius=0<BR>Glyph() after
draw()<BR>RoundGlyph.RoundGlyph(),radius=5<BR>
在Glyph中,draw()方法是抽象的,这样设计是为了覆盖该方法。我们确实在RoungGlyph中强制覆盖了draw()。但是Glyph构造器会调用这个方法,结果导致了对RoundGlyph.draw()的调用,这看起来似乎是我们的目的。但是如果看到输出结果,我们会发现当Glyph的构造器调用draw()方法时,radius不是默认初始值1,而是0。<BR>
解决这个问题的关键是初始化的实际过程:<BR>1)在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制零。</FONT></P>
<P><FONT
face="Times New Roman">2)如前所述那样调用基类构造器。此时,调用被覆盖后的draw()方法(要在调用RoundGlyph构造器之前调用),由于步骤1的缘故,我们此时会发现radius的值为0。<BR>3)按照声明的顺序调用成员的初始化方法。<BR>4)调用导出类的构造器主体。<BR>9.初始化及类的加载<BR>看以下代码:<BR>class
Tag{<BR> Tag(int marker){<BR>
System.out.println("Tag("+marker+")");<BR> }<BR>}
<BR>class Insect {<BR> private int i =
9;<BR> protected int j,m;<BR> Insect() {<BR>
System.out.println("i = " + i + ", j = " + j);<BR> j =
39;<BR> }<BR> private static int x1 = print("static
Insect.x1 initialized");<BR> static int print(String s)
{<BR> System.out.println(s);<BR> return
47;<BR> }<BR> Tag t1=new Tag(1);<BR>}<BR>public
class Beetle extends Insect {<BR> private int k =
print("Beetle.k initialized");<BR> public Beetle()
{<BR> System.out.println("k = " + k);<BR>
System.out.println("j = " + j);<BR>
System.out.println("m = " + m);<BR> }<BR> private
static int x2 = print("static Beetle.x2
initialized");<BR> public static void main(String[] args)
{<BR> System.out.println("Beetle
constructor");<BR> Beetle b = new
Beetle();<BR> }<BR> Tag t2=new
Tag(2);<BR>}<BR>结果:<BR>static Insect.x1 initialized<BR>static
Beetle.x2 initialized<BR>Beetle constructor<BR>Tag(1)<BR>i =
9, j = 0<BR>Beetle.k initialized<BR>Tag(2)<BR>k = 47<BR>j =
39<BR>m = 0<BR> 你在Beetle 上运行Java
时,所发生的第一件事情就是你试图访问Beetle.main( ) (一个static 方法),于是加载器开始启动并找出
Beetle 类被编译的程序代码(它被编译到了一个名为Beetle .class
的文件之中)。在对它进行加载的过程中,编译器注意到它有一个基类(这是由关键字 extends
告知的),于是它继续进行加载。不管你是否打算产生一个该基类的对象,这都要发生。<BR>
如果该基类还有其自身的基类,那么第二个基类就会被加载,如此类推。接下来,根基类中的静态初始化(在此例中为Insect)即会被执行,然后是下一个导出类,以此类推。这种方式很重要,因为导出类的静态初始化可能会依赖于基类成员能否被正确初始化的。<BR>
至此为止,必要的类都已加载完毕(静态变量和静态块),对象就可以被创建了。<BR>
首先,对象中所有的原始类型都会被设为缺省值,对象引用被设为null——这是通过将对象内存设为二进制零值而一举生成的。然后,基类的构造器会被调用。在本例中,它是被自动调用的。但你也可以用super
来指定对基类构造器的调用。基类构造器和导出类的构造器一样,以相同的顺序来经历相同的过程,即向上寻找基类构造器。在基类构造器完成之后(即根部构造器找到之后),实例变量(instance
variables
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -