JavaSE基础第十章(继承、方法覆盖和多态)

一、继承

1、继承概述

继承是面向对象三大特征之一,封装居首位,封装之后形成了独立体,独立体A和独立体B之间可能存在继承关系。其实程序中的继承灵感来自于现实生活,在现实生活中继承处处可 见,例如,儿子继承了父亲的财产,儿子不需要努力就很有钱。
继承时子类继承父类的特征和行为,使得子类对象(实例)具有父类的属性,或子类从父类继承方法,使得子类具有与父类相同的行为。兔子和羊属于食草动物类,狮子和豹属于食肉 动物类。食草动物和食肉动物又是属于动物类。所以继承需要符合的关系是:is-a(Bird is-a Animal),父类更通用,子类更具体。虽然食草动物和食肉动物都是属于动物,但是两者的属 性和行为上有差别,所以子类会具有父类的一般特性也会具有自身的特性。
为什么要使用继承机制?在不同的类中也可能会有共同的特征和动作,可以把这些共同的特征和动作放在一个类中,让其它类共享。因此可以定义一个通用类,然后将其扩展为其它多 个特定类,这些特定类继承通用类中的特征和动作。继承是 Java 中实现软件重用的重要手段, 避免重复,易于维护。

2、如何继承

继承的语法格式:

class 类名 extends 父类名{ 
	类体;
}

继承可以解决代码臃肿的问题。换句话说,继承解决了代 码复用的问题(代码复用就是代码的重复利用),这是继承机制最基本的作用。 继承的作用中除了可以让代码复用之外,还有非常重要的两个作用,那就是有了继承之后才会衍生出方法的覆盖和多态机制。这在之后会学习到。

3、继承的相关特性

① B类继承A类,则称A类为超类(superclass)、父类、基类,B类则称为子类(subclass)、 派生类、扩展类。
② java 中的继承只支持单继承,不支持多继承,C++中支持多继承,这也是 java 体 现简单性的一点,换句话说,java中不允许这样写代码:class B extends A,C{ }。
③ 虽然 java 中不支持多继承,但有的时候会产生间接继承的效果,例如:class C extends B,class B extends A,也就是说,C直接继承B,其实C还间接继承A。
④ java中规定,子类继承父类,除构造方法和被 private 修饰的数据不能继承外,剩 下都可以继承。 ⑤ java中的类没有显示的继承任何类,则默认继承Object 类,Object 类是 java语言提供的根类(老祖宗类),也就是说,一个对象与生俱来就有Object 类型中所有的特征。
⑥ 继承也存在一些缺点,例如:CreditAccount 类继承 Account 类会导致它们之间的 耦合度非常高,Account 类发生改变之后会马上影响到CreditAccount 类。

二、方法覆盖

1、什么时候需要方法覆盖

方法覆盖(override)和方法重载(overload)很像。学习时要注意区分,先来回顾一下方法重载。
在同一个类当中,如果功能相似,尽可能将方法名定义的相同,这样方便调用的同时 代码也会美观。只要在同一个类当中, 方法名相同,参数列表不同(类型、个数、顺序),即构成方法重载。
什么时候使用方法覆盖(重写)
当从父类中继承过来的方法已经不够子类使用了,此时就需要使用方法覆盖机制。

2、怎么进行方法覆盖

只有当从父类中继承过来的方法无法满足当前子类业务需求的时候,需要将父类中继承过来的 方法进行覆盖。换句话说,父类中继承过来的方法已经不够用了,子类有必要将这个方法重新 再写一遍,所以方法覆盖又被称为方法重写。当该方法被重写之后,子类对象一定会调用重写 之后的方法。

3、方法覆盖的条件及注意事项

当程序具备哪些条件的时候,就能构成方法覆盖呢?
① 方法覆盖发生在具有继承关系的父子类之间,这是首要条件;
② 覆盖之后的方法与原方法具有相同的返回值类型、相同的方法名、相同的形式参
数列表;
另外,在使用方法覆盖的时候,需要注意以下事项:
① 由于覆盖之后的方法与原方法一模一样,建议在开发的时候采用复制粘贴的方式,不建议手写,因为手写的时候非常容易出错,比如在Object 类当中有 toString()方法,该方法中的 S是大写的,在手写的时候很容易写成小写 tostring(),这个时候你会认为toString() 方法已经被覆盖了,但由于方法名不一致,导致最终没有覆盖,这样就尴尬了;
② 私有的方法不能被继承,所以不能被覆盖;
③ 构造方法不能被继承,所以也不能被覆盖;
④ 覆盖之后的方法不能比原方法拥有更低的访问权限,可以更高(学习了访问控制权限修饰符之后会明白了);
⑤ 覆盖之后的方法不能比原方法抛出更多的异常,可以相同或更少(学习了异常之后就明白了);
⑥ 方法覆盖只是和方法有关,和属性无关; ⑦ 静态方法不存在覆盖(不是静态方法不能覆盖,是静态方法覆盖意义不大,学习了多态机制之后就明白了);

三、多态

1、多态基础语法

多态(Polymorphism)属于面向对象三大特征之一,它的前提是封装形成独立体,独立体之间存在继承关系,从而产生多态机制。多态是同一个行为具有多个不同表现形式或形态的能力。
在 java 中允许这样的两种语法出现,一种是向上转型(Upcasting),一种是向下转型(Downcasting),向上转型是指子类型转换为父类型,又被称为自动类型转换,向下转型是指父类型转换为子类型,又被称为强制类型转换。
在 java语言中有这样的一个规定,无论是向上转型还是向下转型,两种类型之间必须要有继承关系,没有继承关系情况下进行向上转型或向下转型的时候编译器都会报错。

演示多态:

public class Animal { 
	public void move(){ 	
		System.out.println("Animal move!");
	}
}

public class Cat extends Animal{ 
	//方法覆盖 public void move(){ 	
		System.out.println("走猫步!");
	} 
	//子类特有 
	public void catchMouse(){ 	
		System.out.println("抓老鼠!");
	} 
}

public class Bird extends Animal{ 
	//方法覆盖 public void move(){ 	
	System.out.println("鸟儿在飞翔!");
	}
	//子类特有 
	public void sing(){ 	
		System.out.println("鸟儿在歌唱!");
		}
}

//多态
public class Test01{ 
	public static void main(String[] args){ 
		//夫类型的引用指向子类型的对象	
		Animal a1 = new Cat();
		a1.move(); 
		Animal a2 = new Bird(); 
		a2.move();
	}
}

以上程序演示的就是多态,多态就是“同一个行为(move)”作用在“不同的对象上”会有不同的表现结果。java中之所以有多态机制,是因为java允许一个父类型的引用指向一个子 类型的对象。也就是说允许这种写法:Animal a2 = new Bird(),因为Bird is a Animal 是能够说 通的。其中Animal a1 = new Cat()或者Animal a2 = new Bird()都是父类型引用指向了子类型对象,都属于向上转(Upcasting),或者叫做自动类型转换。
分析以下代码:

Animal a1 = new Cat();
a1.move(); 

java程序包括编译和运行两个阶段,分析java程序一定要先分析编译阶段,然后再分析运行阶段,在编译阶段编译器只知道a1变量的数据类型是Animal,那么此时编译器会去Animal.class字节码中查找move() 方法,发现 Animal.class 字节码中存在 move()方法,然后将该 move()方法绑定到 a1 引用上, 编译通过了,这个过程我们可以理解为“静态绑定”阶段完成了。紧接着程序开始运行,进入运行阶段,在运行的时候实际上在堆内存中 new的对象是Cat 类型,也就是说真正在move 移 动的时候,是Cat 猫对象在移动,所以运行的时候就会自动执行Cat 类当中的move()方法,这个过程可以称为“动态绑定”。但无论是什么时候,必须先“静态绑定”成功之后才能进入“动态绑定”阶段。

public class Test02{ 
	public static void main(String[] args){ 
		//向上转型 
		Animal a = new Cat(); 
		//向下转型:为了调用子类对象特有的方法 	
		Cat c = (Cat)a; 
		c.catchMouse();
	} 
}

通过以上程序可以看出:只有在访问子类型中特有数据的时候,需要先进行向下转型。 其实向下转型就是用在这种情形之下。
但向下转型往往有一定的风险,在当类型不兼容的情况下进行转型出现这样的异常:ClassCastException,翻译为类型转换异常,之所以出现此异常是因为在程序运行阶段 a引用指向的对象是一只小鸟,然后我们要将一只小鸟转换成一只猫,这显然是不合理的,因为小鸟和猫之间是没有继承关系的。为了避免这种异常的发生,建议在进行向下转型之前进行运行期类型判断,这就需要我们学习一个运算符了,它就是 instanceof。
其语法格式是:
(引用 instanceof 类型)
instanceof运算符的运算结果是布尔类型,可能是 true,也可能是 false,假设(c instanceof Cat)结果是 true 则表示在运行阶段 c 引用指向的对象是 Cat 类型,如果结果是 false 则表示在运行 阶段 c 引用指向的对象不是Cat 类型。有了 instanceof运算符,向下转型就可以这样写了:

public class Test03{ 
	public static void main(String[] args){ 
		Animal a = new Bird(); 
		if(a instanceof Cat){ 
			Cat c = (Cat)a; 
			c.catchMouse();
		} 
	} 
}

在实际开发中,java 中有这样一条默认的规范需要大家记住:在进行任何向下转型的操作之前,要使用instanceof进行判断, 这是一个很好的编程习惯。
总结一下,其实多态存在的三个必要条件分别是:
① 继承
② 方法覆盖
③ 父类型引用指向子类型对象

方法覆盖主要是说实例方法,静态方法一般不谈方法覆盖,因为静态方法的执行压根和对象无关,既然和对象无关那就表示和多态无关,既然和多态无关,也就是说静态方法的“覆盖”是没有意义的,所以通常我们不谈静态方法的覆盖。

2、多态在开发中的作用

在软件开发过程中,有这样的一个开发原则:开闭原则。开闭原则(OCP)是面向对象设计中“可复用设计”的基石,是面向对象设计中最重要的原则之一,其它很多的设计原则都是 实现开闭原则的一种手段。1988 年,勃兰特·梅耶(Bertrand Meyer)在他的著作《面向对象软件构造(Object Oriented Software Construction)》中提出了开闭原则,它的原文是这样: “Software entities should be open for extension,but closed for modification”。翻译过来就是:“软件实体应当对扩展开放,对修改关闭”。这句话说得略微有点专业,我们把它讲得更通俗一点,也就是:软件系统中包含的各种组件,例如模块(Modules)、类(Classes)以及功能(Functions) 等等,应该在不修改现有代码的基础上,引入新功能。开闭原则中“开”,是指对于组件功能 的扩展是开放的,是允许对其进行功能扩展的;开闭原则中“闭”,是指对于原有代码的修改 是封闭的,即修改原有的代码对外部的使用是透明的。
我们在日常编写代码的过程中应面向抽象编程,尽量不要面向具体编程,面向抽象编程会让你的代码耦合度降低,扩展能力增强,从而符合 OCP 的开发原则。

通过以上内容的学习,我们可以看到多态在开发中联合方法覆盖一起使用,可以降低程序的耦合度,提高程序的扩展力。在开发中尽可能面向抽象编程,不要面向具体编程,好比电脑 主板和内存条的关系一样,主板和内存条件之间有一个抽象的符合某个规范的插槽,不同品牌 的内存条都可以插到主板上使用,2个G的内存条和 4个G的内存条都可以插上,但最终的 表现结果是不同的,2 个G的内存条处理速度慢一些,4 个G的快一些,这就是多态,所谓多态就是同一个行为作用到不同的对象上,最终的表现结果是不同的,主要的要求就是对象是可以进行灵活切换的,灵活切换的前提就是解耦合,解耦合依赖多态机制。

总结

本章我们知道了什么是继承,为什么要继承,继承有什么好处,继承的特性有哪些。另外还需要掌握的是继承的代码如何实现。还有 Java 中任何一个类的定义都会自带Object 类的特征,这是因为Object 是所有类的超类。
需要理解什么时候考虑使用方法覆盖,理解满足什么条件时能够构成方法覆盖,以及掌握怎么进行方法覆盖。
另外还需要掌握多态相关的基础语法,理解多态在开发中起到了解耦合的作用,提倡面向抽象编程,不要面向具体编程。理解 OCP 开发原则等。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值