黑马程序员--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). 如果子类中的某个变量没有在父类中出现过,那么这个时候,使用this和super访问到的都是该子类成员变量。
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学习型技术博客、期待与您交流! ------------
相关文章推荐
- 黑马程序员--03.Java面向对象--08.【多态中成员的特点】
- 黑马程序员--03.Java面向对象--05.子类对象初始化的过程【个人总结】
- 黑马程序员__java面向对象__构造函数和继承
- java面向对象-子父类中构造函数的特点-子类实例化过程super
- Java基础05:面向对象;类与对象;匿名对象;成员变量;局部变量;封装;构造函数
- 黑马程序员--03.Java面向对象--04.【对象初始化的过程】【对象调用成员的过程】
- 黑马程序员--03.Java面向对象--01.【OOP基本概念】【匿名对象】【封装】【构造函数】【this关键字】
- 黑马程序员_Java基础_面向对象(继承、子父类变量和函数以及构造函数特点、final关键字、抽象类、模版方法模式、接口)
- 黑马程序员--03.Java面向对象--02.【static关键字】【main方法】【静态成员】
- java面向对象值继承-子父类中变量的特点
- JAVA进阶 面向对象程序设计——第4章 继承与多态【不同访问属性使子类在使用这些成员时不同】
- java study 07day--面向对象继承中类成员中的特点
- 面向对象第二个特征----继承III(子父类中的构造函数)
- 面向对象第二个特征----继承III(子父类中构造函数初始化过程2)
- 黑马程序员_JAVA笔记05 ——面向对象1(类与对象关系、封装、构造函数、this)
- java——构造函数、构造代码块、Static关键字、main函数 & 继承:子父类中的变量、函数的特点、抽象类、接口等
- 面向对象第二个特征----继承III(子父类中的构造函数)
- 黑马程序员——Java基础---面向对象(面向对象,类,对象,匿名对象,封装,构造函数,构造代码块,this,static,main,帮助文档,静态代码块,单例)
- 黑马程序员——Java语言基础——03.面向对象(1)思想和一些概念
- java基础十二 面向对象特点_继承