java继承初始化_JAVA继承初始化过程

我们有必要对整个初始化过程有所认识,其中包括继承,对这个过程中发生的事情有一个整体性的概念。请观察下述代码:

//: Beetle.java//The full process of initialization.

classInsect {int i = 9;intj;staticint x1 = prt("static Insect.x1 initialized");//注意这里是static字段

Insect() {

prt("i = " + i + ", j = " +j);

j= 39;

}static intprt(String s) {

System.out.println(s);return 47;

}

}public class Beetle extendsInsect {int k = prt("Beetle.k initialized");staticint x2 = prt("static Beetle.x2 initialized");//注意这里是static字段

Beetle() {

prt("k = " +k);

prt("j = " +j);

}static intprt(String s) {

System.out.println(s);return 63;

}public static voidmain(String[] args) {

prt("Beetle constructor");

Beetle b= newBeetle();

}

}

该程序的输出如下:

static Insect.x initialized

static Beetle.x initialized

Beetle constructor

i = 9, j = 0

Beetle.k initialized

k = 63

j = 39

对Beetle 运行Java 时,发生的第一件事情是装载程序到外面找到那个类。

在装载过程中,装载程序注意它有一个基础类(即extends 关键字要表达的意思),所以随之将其载入。

无论是否准备生成那个基础类的一个对象,这个过程都会发生(请试着将对象的创建代码当作注释标注出来,自己去证实)。

若基础类含有另一个基础类,则另一个基础类随即也会载入,以此类推。

接下来,会在根基础类(此时是Insect)执行static 初始化,再在下一个衍生类执行,以此类推。

保证这个顺序是非常关键的,因为衍生类的初始化可能要依赖于对基础类成员的正确初始化。

此时,必要的类已全部装载完毕,所以能够创建对象。

首先,这个对象中的所有基本数据类型都会设成它们的默认值,而将对象句柄设为null 。随后会调用基础类构建器。

在这种情况下,调用是自动进行的。但也完全可以用super 来自行指定构建器调用(就象在Beetle()构建器中的第一个操作一样)。

基础类的构建采用与衍生类构建器完全相同的处理过程。基础顺构建器完成以后,实例变量会按本来的顺序得以初始化。

最后,执行构建器剩余的主体部分。

构造器顺序:

classAA{

AA(){System.out.println("AA");}

}classBB{

BB(){System.out.println("BB");}

}public class Test extendsBB{private AA aa = new AA();//组合

Test(){System.out.println("Test");}public static voidmain(String[] args) {

Test ee= newTest();

}

}

//输出:

BB

AA

Test

扩展例子:

packagecom.com;classAA{

AA(){System.out.println("AA");}

}classBB{static int i = prt("BB static");

BB(){System.out.println("BB");}static intprt(String s) {

System.out.println(s);return 47;

}

}public class TestMain extendsBB{private AA aa = newAA();

TestMain(){

System.out.println("Test");

}public static voidmain(String[] args) {

System.out.println("TestMain:main");

TestMain ee= newTestMain();

}

}

//输出:

BB static

TestMain:main

BB

AA

Test

结论:复杂对象构造器顺序如下:

(1)在其他任何事物发生之前,将分配给对象的存储空间初始化为2进制的0,并且加载类(当然就包括初始化类的static成员).

(2)调用基类构造器(并且递归调用)

(3)按声明顺序调用成员初始化方法(这里就是说Test类组合部分AA的初始化部分)

(4)调用派生类构造器主体。

动态绑定:

JAVA中除了static和final方法(private方法属于final方法),其他所有的方法都是动态绑定的。

我们要知道private方法被自动认为是final方法,而且对派生类是屏蔽的,也就是说如果派生类重写了改方法是一个新的方法,所以说只有非private方法才可以被覆盖。

继承和清理:

一般我们是不必担心对象清理的问题,因为会留给垃圾回收器来处理。

如果我们一定要自己清理对象,那么就必须自己维护好清理顺序。主要有这样几个方面需要注意:

1.要为新类定义一个清理函数(比如dispose),我们自己维护,派生类复写基类的该方法

2. 派生类在该方法的实现最后必须调用基类的dispose方法

3.还必须注意成员对象销毁的顺序必须要和声明的顺序相反(因为成员初始化时按声明的顺序来构造的)

实际上也就是和C++的析构函数类似。

构造器内部的多态行为:(在构造器内部调用多态方法)

classAA{void draw(){System.out.println("AA:draw");}

AA()

{

System.out.println("before:draw");

draw();

System.out.println("after:draw");

}

}class BB extendsAA{private int rad = 1;

BB(inti)

{

rad=i;

System.out.println("BB:rad = "+rad);

}void draw(){System.out.println("BB:draw--rad = " +rad);}

}public classTest{public static voidmain(String[] str)

{new BB(5);

}

}

//输出:

before:draw

BB:draw--rad = 0 //这里不是1,因为会先将分配给对象的存储空间初始化为2进制的0.

after:draw

BB:rad = 5

显然调用draw函数的多态性了。上面的输出结果很诡异,也给我们有了很好的提示。

总结:

编写构造器法则:

用尽可能简单的方法使对象进入正常状态,如果可以的话,避免调用其他方法。在构造中唯一能够安全调用的方法就是基类中的final方法(private方法也属于final方法),因为final方法不可能存在多态的可能。

接口基本知识:

interface Intest

{

int VALUE = 5;//接口中字段默认就是static和final的

void play();//默认是public

void adjust();

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值