您的位置:首页 > 职场人生

黑马程序员--03.Java面向对象--05.【继承】【子父类中成员的特点】【子父类中构造函数的特点】

2013-07-19 17:04 573 查看

Java面向对象--05

继承    子父类中变量、函数、构造函数的特点

----------- android培训java培训、java学习型技术博客、期待与您交流! ------------

1.    继承

1). 继承的相关知识

(1). 继承的优势

[1]. 提高了代码的重用性

[2]. 让类与类之间产生了关系。只因为有了继承,才会有多态    (继承 == >多态)
注意:继承是类与类之间关系的一种,是一种所属关系,“isa”的关系。

(2). Java中继承的特点

[1]. 和C++相比,Java支持单继承

[2]. 为了支持类似C++中的多继承的机制,Java采用多实现的方式来支持多继承

(3). 如何使用是个继承体系的功能呢?

[1]. 查阅父类的描述。因为父类中定义的是该类体系中的共性的功能

       通过了解父类的共性功能,就可以知道这个体系中,就可以使用这个体系中的基本功能。

[2]. 查阅子类的描述。

(4). 类中的成员:

变量、函数和构造函数。

(5). 继承子类中关键字

[1]. this代表本类对象引用

[2]. super代表父类对象引用

2). 继承中子类、父类成员(变量/方法)的特点

(1). 子类、父类成员变量全部存放在子类的内存区域

       【个人理解】由于类中成员函数存储位置方法区,而不是堆内存,所以父类同名的方法不会存到子类的方法区对应的代码中。

(2). 类和类出现同名非私有成员的时候:

[ 对于父类的私有成员, 子类是无法直接访问到的 ]

       {1}.子类方法的内部要访问本类中的该成员,使用关键字this

       {2}.子类方法的内部要访问到从父类继承的该成员,使用的是关键字super

(3). 如果子类中的某个变量没有在父类中出现过,那么这个时候,使用thissuper访问到的都是该子类成员变量。

3). 子类重写 (覆盖) 父类中方法

(1). 何时应用重写

[1]. 当子类继承了父类,这样子类就具备了父类的所有非私有的功能(方法)

[2]. 当子类同样具备父类相同功能的函数,但是功能的实现和父类中该功能的实现不一样的时候,这时候,没有必要在子类中定义新的功能,而是重写这个功能的实现。

       保留父类的功能定义(函数名),并重写功能的内容

---------------【子类继承类似生物中的遗传

---------------【子类重写类似生物中的变异: 原有功能上的进化

       ---------------【子类添加父类中没有功能类似生物中的变异: 新的功能

(2). 重写的条件

[1].重写产生的时机:当满足子类中的函数的名字、参数列表和父类的某个非私有功能具有相同的函数名字、参数列表的时候,Java的编译器就会把子类这个同名同参数的功能作为父类对应功能的重写。为了保证重写的正确性(也就是Javac编译通过),还要满足以下两个条件:(所以,是否为重写,最大原则就是父类的该方法是非private的访问权限!!!)

[2]. 正确重写的条件1:重写的两个方法的其余部分满足以下条件:

       {1}.返回值类型一致、

{2}. 子类访问权限修饰符 >=父类的访问权限修饰符 (子类和父类中对应的方法都不能出现private!!!)

         {3}. 子类函数抛出的异常类型必须是父类函数抛出来的异常类型的子类或者子集

【注意】子类中的静态方法只能覆盖父类中的静态方法 ====>静态只能覆盖静态

(3). 重写跟重载的区别

[1]. 重载:只看是否是       相同的函数名不相同的参数列表 (javac认定此时为重载)

                     其余部分无所谓是否一致。

[2]. 重写:只看子类、父类中是否是相同函数名参数列表(javac认定此时为重写)

                     如果是,函数剩下的部分必须满足正确重写的条件!!javac才能编译通过。

                     否则,javac认为这是错误的重写方式,编译报错!!

2.    子类对象的实例化过程

(1). 子类能覆盖父类的构造方法么

子类不能覆盖父类的构造函数。

原因:由于构造函数的名字是和类名是一样的。子类和父类类名又不相同,所以不满足子类父类之间的函数重载的充分条件,所以子类不能覆盖父类的构造函数。

(2). 隐式的super语句和显式super语句

[1]. 子类的构造函数中的第一行如果没有通过super语句显式调用父类构造函数的时候,JVM在执行的时候,会默认为子类的该构造函数的第一行加上代表空参数的父类构造函数的super语句: super()

【结论】子类的构造方法的第一行默认是super()

[2]. 由于子类的构造函数的第一行默认是super(),这要求父类的构造方法必须有相应的空参构造函数。如果父类没有相对应的空参构造函数,javac就会报错!!!

[3]. 当父类没有空参构造函数的时候,子类的构造方法必须通过super(xxxx)语句指定要调用父类的哪一个构造函数来对自己进行初始化。

【举例1:子类调用默认的父类空参构造函数】

class Fu{
Fu(){
System.out.println("Fu runs");
}

Fu(int x){
System.out.println("Fu runs..."+ x);
}
}

class Zi extends Fu{
Zi(){
System.out.println("Zi runs");//没有指定,默认调用super();
}

Zi(int x){
System.out.println("Zi runs..."+ x); //没有指定,默认调用super();
}
}

class Test{
public static void main(String[] args) {
Ziz =new Zi();
System.out.println("*****");
ZizX =new Zi(4);
}
}


运行结果:



【举例2:子类调用指定的父类构造函数】

class Fu{
Fu(int x){
System.out.println("Fu runs..."+ x);
}
}

class Zi extends Fu{
Zi(){
super(3);
System.out.println("Zi runs");
}

Zi(int x){
super(x);
System.out.println("Zi runs..."+ x);
}
}

class Test{
public static void main(String[] args) {
Ziz =new Zi();
System.out.println("*****");
ZizX =new Zi(4);
}
}


由于Fu类没有空参构造函数,所以继承自这个类的子类必须在子类的构造方法中的第一行指定要调用的父类的构造函数。
运行结果:



(3). 使用super语句的注意事项

[1]. super()语句必须放在子类构造函数的第一行

       原因:先使用老的内容(代码复用)。如果不喜欢老的内容,再super语句之后再修改或者增加新的内容。非常类似生物学中的遗传和变异

[2]. 一个子类的构造函数中,只能有super()语句或者this()语句,两者不能同时存在

原因:

super()为了获取父类的财产,初始化父类的成员变量。继承老的东西,所以放在第一行;

this()为了在本次构造中进行更细致的构造,所以放在第一行;

如果一个构造方法中,出现了super()语句放在第一行,那么要求也在第一行的this()就不能出现。反之亦然。

[3]. 子类中至少有一个子类构造函数能访问父类的构造函数

举例:

class Fu{
Fu(){}

Fu(int x){
System.out.println(x);
}
}

class Zi extends Fu{
Zi(){}

Zi(int x){
this();
System.out.println("x="+ x);
}
}


问题:Zi(int x)这个构造中第一行加了this(); 他这里面还有super()语句么?他还调用父类的构造函数么?
【1】. 这个构造函数的第一行是this(),那么这个构造方法中就没有super()语句了。

【2】. 但是,由于这个构造函数调用了this(),对的是Zi(){}。这个构造函数的第一行没有this语句,所以,JVM会为这个构造函数加上一个super()。这样 构造函数Zi(int x)通过调用Zi()达到了访问父类构造函数的目的。

(4). 总结super语句

【1】. 子类的所有构造函数都会默认访问父类空参构造函数

【2】. 父类没有空参数构造函数时,必须手动为子类构造函数指定要调用的父类构造函数。

【3】. 当子类构造函数形成重载的情况时,如果某一个构造函数第一行被this()语句占用,则这个子类的构造函数中必须保证至少有一个构造函数能够访问父类的构造函数

【问题】父类的构造函数中有super()语句么?

有!这个super()语句是Object的默认空参构造函数 Object(){…..}

3.    子类this的真实指向(盲点)

1). 子类对象实例化过程 调用父类构造方法---- this的指向

(1). 误区:认为实例化的过程中,调用父类的构造也同时实例化了父类实例

注意子类通过自身构造方法实例化过程中再次调用父类的构造方法,实际上是通过父类的构造方法父类的财产“成员变量”进行特有的初始化之后,再来继承父类的特有财产“成员变量”

注意绝对不是实例化子类对象之后,又同时实例化相应的父类对象!!!!

(2). 通过MyEclipse进行验证

[1]. 验证代码

class Fu {
public Fu(){
System.out.println(this);
}
public void showName(){
System.out.println("Fu..." +this);
}
}

class Zi extends Fu{
public Zi(){
System.out.println(this);
}
public void showNameII(){
this.showName();
super.showName();
}
}

public class Test{
public static void main(String[] args){
Zizi =new Zi();
zi.showNameII();
}
}

[2]. 调试验证
执行 Zi zi =new Zi();时候,父类的构造中this指向的是Zi@834e7   是子类的实例。

说明:子类调用父类的构造方法,在父类中的方法的内部this仍然是子类的this,而不是父类的this.



2). 子类对象调用从父类继承的方法中---- this的指向

(1). 此时this同样是指向子类实例对象

由于构造子类实例的时候,仅仅是调用父类的构造但是并没有进行父类实例化。所以子类调用从父类继承的方法时候,在这个方法的内部this同样也是指向子类的实例

(2). MyEclipse验证

执行到zi.showNameII();的时候,在Zi类的showNameII中又调用了从父类继承的showName方法,进入到Fu的showName()代码中的时候,this显示的值是Zi@834e7这个值,证明Fu中的方法被子类调用的时候,this仍然指向的Zi的实例



----------- android培训java培训、java学习型技术博客、期待与您交流! ------------
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐