Father father=new Father(); Son son=new Son(); grandfather.write(); father.write(); son.write(); } }
在这个测试程序中我建立了三个类,爷爷,父亲,儿子他们都具有写的行为,在面向对象中称为方法,如果父亲的写的行为和爷爷一样,我们就不需要重新定义它, 只要用关键字extends来表示它们之间的一种继承关系就行了,但是到了儿子这一代除了要声明他和父类的继承关系之外,由于写的方法发生了重大的变化, 所以我们必须重新定义它,在面向对象中我们称它为方法的覆盖。关于覆盖还有几点要声明一下
1、覆盖的方法必须存在父子类的继承关系,方法的名称,参数,返回值必须完全一致。
2、子类的覆盖方法的权限不能低于父类方法的权限,抛出的异常不能多余父类定义的异常
以上两点必须牢记!
除此以外就是静态方法只能被静态方法覆盖,父类的私有方法不能被子类覆盖
还有两点就是语法规定了,我觉得用处不大感兴趣的话可以了解一下。 父类的抽象方法被子类覆盖的方式有两种:一种是重新声明为抽象方法,另一种就是在子类中实现它。
父类的非抽象方法可以被覆盖为抽象方法。
Abstract是修饰符,代表抽象,这里还没有接触到抽象和异常所以我们暂时不管它,跳过去就好了。
之前我曾说过,方法的覆盖有点难,学过C++的朋友可能会说这有什么难的和我们学的语法一样吗!正如我之前所说Java的语法并不困难,困难的是一种思想的学习和思维的转变。
试想一下只要存在父子类关系就可以随便的覆盖非私有的方法是一件很方便的事情还是很可怕的事情?还是举一个生活中常见的例子看一下,如果把银行看成一个 类,那么储蓄所应该就是它的子类了,银行是为公众服务的,自然会有一个公开的取钱的方法,如果银行的子类储蓄所被人恶意覆盖了取钱的方法会怎么样?想想如 果是你会怎样改?假如银行类有这样的方法取钱public int withdraw(int i){return i}到了储蓄所这个方法会被恶意修改成public int withdraw(int i){return i*10}这样的方法可是一个快速致富的捷径呀! 所以说父子类中方法的覆盖会给编程带来重大隐患。也许你会问,Java已经是世界第一大编程语言了,为什么还会犯这种错误呀?其实这个问题很好回答,因为 面向对象的思想是在逐步深刻的,就是现在也没有被完全认识清楚,何况是在1995年,即使Java的创始者是一个计算机的天才,但他也是一个人,是人就会 发错误!这就是我们常说的认识有多远,就能走多远。在Java的语法中还会有很多这样的不足,解决的办法我会在模式设计中给出一些方案!
3、多态
这次我们聊一聊多态,多态应该是Java基础中非常重要的一部分,它的地位应该仅次于接口。为什么这么说呢?因为它使我们的编程更加灵活!如果你之前读过 一些Java的书籍,可能会经常看到这样的话:学习Java就像搭积木,或者是迷宫,走进去出不来,这句话恐怕就是对多态而言的。大部分的书中讲多态都是 从语法开始的,我想别出心裁换一种方法,从这种思想的诞生讲起,试想一下我们每个人都有手机,手机的供电方式有两种,一种是电池供电,另一种就是线充。这 是完全不同的两种供电方式,但是对手机供电的效果都是一样的。于是我们大胆设想一下,可不可以把这种生活中的例子引入面向对象的程序中,为我们服务呢?从 手机的观点来看无论是电池还是线充它们都是手机供电装置,唯一不同的只是供电的具体手段不同罢了。于是多态的观念就产生了。 我们先写一个供电类 package blog;
public class ElectricProvider {
public void provide()//供电方法 {} }
他有两个子类一个是电池类,一个是线充类 package blog;
public class Battery extends ElectricProvider{ public void provide() {
System.out.println(\电池供电\ } }
package blog;
public class Transformer extends ElectricProvider{
public void provide() {
System.out.println(\线充供电\ }
}
再写一个手机类 package blog;
public class Mobile {
public void work(ElectricProvider ep) {
ep.provide(); } }
以下是测试 package blog;
public class Test {
public static void main(String[] args) {
Mobile mobile=new Mobile(); ElectricProvider ep=new Battery();
mobile.work(ep);//根据实参的类型自动选择方法,这个实参是Battery类型,所以会运行电池供电的方法
ep=new Transformer();
mobile.work(ep);//根据实参的类型自动选择方法,这个实参是Transformer类型,所以运行线充供电的方法 } }
看到mobile.work(ep)这个方法了吗,它就是多态,第一次出现的时候被理解成电池,第二次出现的时候别理解成充电器,多种状态都出现了,多态故此得名!
它有两点好处:第一编程大大简单了,无论是电池还是线充只要用
mobile.work(ep)就搞定了,是不是很方便,还有就是实参的方法被隐藏了。从mobile.work(ep)看不出ep是如何调用方法的!
这种机制在机器内部是如何执行的?请慢慢听我道来。众所周知程序要先编译再运行,Java也不例外,在执行编译的时候mobile.work(ep)这条 语句是这样处理的:首先会判断ep的类型是什么?在编译时是按照声明的类型来判断的,所以ep是ElectricProvider类型,由于 ElectricProvider中有provide方法,所以编译顺利通过。在运行程序时ep的类型并非按照声明的类型来处理,而是它的实际类型,第一 次ep的实际类型是Battery所以会执行Battery的provide方法,第二次ep的实际类型是Transformer所以执行的方法是 Transformer的provide方法。这就是多态的运行机制!
多态对于Java是很重要的,它体现了一种思想,针对父类编程。看一看在程序中public void work(ElectricProvider ep)是不是真对父类,
ElectricProvider ep=new Battery();ep=new Transformer();是不是把子类对象都声明为父类的引用。它的好处还不止于此,以后我们还要接触到针对接口编程,那是Java的核心,如果你能对“针对父类编程”有深刻体会的话,那么针对接口编程你也会很快理解的。
PS:什么是对象的引用?