知识框架
方法覆盖 Override
概念
- 子类出现了和父类中一模一样的方法声明(方法名一样,参数列表也必须一样)就发生了方法覆盖。
方法重写的应用场景
- 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
方法覆盖的条件
- 方法覆盖发生在具有继承关系的父子类之间,这是首要条件
- 覆盖之后的方法与原方法具有相同的返回值类型、相同的方法名、相同的形式参数列表
代码示例
package com.wrg; public class Test { public static void main(String[] args) { // 创建子类对象 NewPhone np = new NewPhone(); // 调用父类继承而来的方法 np.call("张三"); // 调用子类重写的方法 np.showNum(); } } //父类 class Phone { //父类的方法 public void sendMessage() { System.out.println("发短信"); } public void call(String name) { System.out.println("给" + name + "打电话"); } public void showNum() { System.out.println("来电显示号码"); } } //智能手机类,子类 class NewPhone extends Phone { //重写父类的来电显示号码功能,并增加自己的显示姓名和图片功能 public void showNum() { //调用父类已经存在的功能使用super关键字 super.showNum(); //增加自己特有显示姓名和图片功能 System.out.println("显示来电姓名"); System.out.println("显示头像"); } }
另外,在使用方法覆盖的时候,需要有哪些注意事项呢?
- 由于覆盖之后的方法与原方法一模一样,建议在开发的时候采用复制粘贴的方式,不建议手写
- 私有的方法不能被继承,所以不能被覆盖
- 构造方法不能被继承,所以也不能被覆盖
- 覆盖之后的方法不能比原方法拥有更低的访问权限,可以更高
- 覆盖之后的方法不能比原方法抛出更多的异常,可以相同或更少
- 方法覆盖只是和方法有关,和属性无关
- 静态方法不存在覆盖,方法覆盖只能针对实例方法。
多态
什么是多态?
多态(Polymorphism)属于面向对象三大特征之一,它的前提是封装形成独立体,独立体之间存在继承关系,从而产生多态机制。多态是同一个行为具有多个不同表现形式或形态的能力。
前提
- 继承或者实现【二选一】
- 方法的重写【意义体现:不重写,无意义】
- 父类引用指向子类对象【格式体现】
多态就是“同一个行为”发生在“不同的对象上”会产生不同的效果。
在 java 中允许这样的两种语法出现,一种是向上转型(Upcasting),一种是向下转型(Downcasting),向上转型是指子类型转换为父类型,又被称为自动类型转换,向下转型是指父类型转换为子类型,又被称为强制类型转换。请看下图:
在 java 语言中有这样的一个规定,无论是向上转型还是向下转型,两种类型之间必须要有继承关系,没有继承关系情况下进行向上转型或向下转型的时候编译器都会报错,
向上转型
- 向上转型:多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。 当父类引用指向一个子类对象时,便是向上转型。
使用格式:
向下转型
向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。
使用格式:
多态的好处
- 实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展性与便利。
多态的弊端
- 一个引用类型变量如果声明为父类的类型,但实际引用的是子类 对象,那么该变量就不能再访问子类中特有的属性和方法
如何解决多态的弊端
- 我们可以使用向下转型来调用访问子类中特有的属性和方法
注意:
- Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。简称:编译时,看左边;运行时,看右边。
多态中的成员访问特点
- 不管是成员变量还是成员方法,编译的时候,父类中必须存在,否则编译都不会通过,会报错。
总结:
- 当使用多态方式调用成员变量时,首先检查父类中是否有该变量,如果没有,则编译错误;如果有,执行的是父类中的变量
- 当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写后方法
为什么成员方法和成员变量不同?
- 因为成员方法有重写,而成员变量没有
-
成员变量:不具备多态性,只看引用变量所声明的类
代码举例
//定义父类 public class Animal { public int age = 40; public void eat() { System.out.println("动物吃东西"); } }
定义子类
public class Cat extends Animal { public int age = 20; public int weight = 10; @Override public void eat() { System.out.println("猫吃鱼"); } public void playGame() { System.out.println("猫捉迷藏"); } }
定义测试类
public class AnimalDemo { public static void main(String[] args) { //有父类引用指向子类对象 Animal a = new Cat(); // 当使用多态方式调用成员变量时,首先检查父类中是否有该变量,如果没有,则编译错误;如果有,执行的是父类中的变量 System.out.println(a.age);//40 //当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写后方法 a.eat();//猫吃鱼 } }
转型的异常
ClassCastException异常,翻译为类型转换异常,这种异常通常出现在向下转型的操作过程当中,当类型不兼容的情况下进行转型出现的异常,之所以出现此异常是因为在程序运行阶段 a 引用指向的对象是一只小鸟,然后我们要将一只小鸟转换成一只猫,这显然是不合理的,因为小鸟和猫之间是没有继承关系的。为了避免这种异常的发生,建议在进行向下转型之前进行运行期类型判断,这就需要我们学习一个运算符了,它就是 instanceof。instanceof 运算符的语法格式是这样的:
- (引用 instanceof 类型)
instanceof 运算符的运算结果是布尔类型,可能是 true,也可能是 false,假设(c instanceof Cat)结果是 true 则表示在运行阶段 c 引用指向的对象是 Cat 类型,如果结果是 false 则表示在运行阶段 c 引用指向的对象不是 Cat 类型。有了 instanceof 运算符,向下转型就可以这样写了:
public class AnimalDemo { public static void main(String[] args) { // 向上转型 Animal a = new Cat(); a.eat(); // 调用的是 Cat 的 eat // 向下转型 if (a instanceof Cat) { Cat c = (Cat) a; c.catchMouse(); // 调用的是 Cat 的 catchMouse } else if (a instanceof Dog) { Dog d = (Dog) a; d.watchHouse(); // 调用的是 Dog 的 watchHouse } } }
总结一下类型转换