太原理工大学机器人团队20天学习打卡day17
今天主要学习面向对象的其他两个特征,继承和多态,以及他们的具体应用
1、继承
- 集成是面向对象三大特征之一【封装,继承,多态】
①继承的作用
- 基本作用:代码复用。
- 但是继承最重要的作用是:有了继承才有了以后方法的覆盖和多态机制
②继承的语法格式
[修饰符列表] class 类名 extends 父类名{
类体
}
③继承的语法规则
-
java语言当中的继承只支持单继承,一个类不能同时继承很多类,只能继承一个类。
-
虽然java语言当中只支持单继承,但是一个类也可以间接继承其他类,例如:
C extends B{
}B extends A{
}A extends T{
}
C直接继承B类,但是C类间接继承T、A类
-
java语言中假设一个类没有显示的继承任何类,该类默认继承JavaSE库当中提供的java.lang.Object类
-
java语言中任何一个类中都有Object类的特征
④继承中的术语
- B类继承A类,其中:
- A类称为:父类、基类、超类、superclass
- B类称为:子类、派生类、subclass
⑤在java语言当中子类继承父类都继承哪些数据呢
- 私有的不支持继承
- 构造方法不支持继承
- 其他数据都可以被继承
⑥实例
public class Account {
private String actno;
private double balance;
public Account(String actno, double balance) {
this.actno = actno;
this.balance = balance;
}
public Account() {
}
public String getActno() {
return actno;
}
public void setActno(String actno) {
this.actno = actno;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
public class CreditAccount extends Account {
private double credit;
public CreditAccount(String actno, double balance, double credit) {
this.credit = credit;
}
public CreditAccount() {
}
public double getCredit() {
return credit;
}
public void setCredit(double credit) {
this.credit = credit;
}
}
public class ExtendsTest {
public static void main(String[] args) {
CreditAccount act = new CreditAccount();
//本来CreditAccount类中没有set/get方法
act.setActno("act-001");
act.setBalance(-1000.0);
act.setCredit(0.99);
System.out.println(act.getActno()+","+act.getBalance()+","+act.getCredit());
}
}
2、方法的覆盖
①先回顾一下方法的重载
- 什么时候使用
- 方法完成的功能相似
- 同一个类当中方法名相同,参数列表不同
②方法覆盖
- 方法覆盖又被称为方法重写,英语单词override【官方】/overwrite
③什么时候使用方法重写
- 当父类中的方法已经无法满足当前子类的业务需求,子类有必要将父类中继承过来的方法进行重新编写,这个重新编写的过程叫做方法重写或者方法覆盖
④什么条件满足后方法会发生重写呢
- 方法重写发生在具有继承关系的父子类之间
- 方法重写的时候:方法名相同,返回值类型相同,形参列表相同
- 方法重写的时候:访问权限不能更低,只能更高
⑤注意
- 私有方法不能继承,所以不能覆盖。
- 构造方法不能继承,所以不能覆盖。
- 静态方法不存在覆盖。
- 覆盖只针对方法,不谈属性
3、多态
①关于多态中涉及到的几个概念
- 向上转型(upcasting)
- 子类型转换成父类型,又被称为自动类型转换
- 向下转型(downcasting)
- 父类型转换成子类型,又被称为强制类型转换【需要强制类型转换符】
- 无论是向上转型还是向下转型,两种类型之间必须要有继承关系
②语法机制
引例
//动物类
public class Animal {
public void move(){
System.out.println("动物在移动");
}
}
//猫类
public class Cat extends Animal {
//重写父类中继承过来的方法
public void move(){
System.out.println("猫在走猫步");
}
//不是从父类中继承过来的,是子类中特有的【不是所有动物都能抓老鼠】
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
public class Test {
public static void main(String[] args) {
//使用多态语法机制
Animal a2 = new Cat();
a2.move();//猫在走猫步
//以下程序为什么编译报错
/*
*因为编译阶段编译器检测到a2类型是Animal类型,从Animal.class字节码文件当中查找catchMouse()
*最终没有找到该方法,导致静态绑定失败,也就是编译失败
*/
//a2.catchMouse();
}
}
详解
- Animal和Cat之间存在继承关系,Animal是父类,Cat是子类
- new Cat()创建的对象的类型是Ca,a2这个引用的数据类型是Animal,可见他们进行了类型转换,子类型转换成父类型,向上转型,或者称为自动类型转换
- java中允许这种语法,父类型引用指向子类型对象
方法的调用,运行结果
- java程序分为编译阶段和运行阶段,编译不通过是无法运行的
- 编译阶段编译器检查a2这个引用的数据类型为Animal,由于Animal.class字节码当中有move()方法,所以编译通过了。这个过程我们称为静态绑定,编译阶段绑定。
只有静态绑定成功之后才有后续的运行 - 运行阶段JVM堆内存当中真是创建的是Cat对象,那么以下程序在运行阶段一定会调用Cat对象的move()方法,此时发生了程序的动态绑定,运行阶段绑定。
- 无论Cat类有没有重写move()方法,运行阶段一定调用的是Cat对象的move方法,因为底层真实对象就是Cat对象
- 父类型引用指向子类型对象这种机制导致程序存在编译阶段的绑定和运行阶段的绑定两种不同的形态,这种机制称为多态语法机制
按照以上代码,想让以上对象执行catchMouse()方法怎么办
-
我们可以将a2强制类型转换为Cat类型。a2的类型是Animal(父类型),转换成Cat(子类型),被称为向下转型/强制类型转换,加强制类型转换符。
-
向下转型也必须要两种类型之间必须有继承关系,不然编译报错。
什么时候使用向下转型
- 当调用的方法是子类型中特有的,父类型当中不存在
public class Bird extends Animal {
//重写父类中继承过来的方法
public void move(){
System.out.println("鸟儿在飞翔");
}
public void fly(){
System.out.println("Bird fly!");
}
}
public class Test {
public static void main(String[] args) {
//使用多态语法机制
Animal a2 = new Cat();
a2.move();//猫在走猫步
//以下程序为什么编译报错
/*
*因为编译阶段编译器检测到a2类型是Animal类型,从Animal.class字节码文件当中查找catchMouse()
*最终没有找到该方法,导致静态绑定失败,也就是编译失败
*/
//a2.catchMouse();
Cat c2 = (Cat)a2;
//父类型引用指向子类型对象【多态】
Animal a3 = new Bird();
/*
以下编译是没有问题的,因为编译器检查到a3数据类型是Aniaml
Animal和Cat之间存在继承关系,并且Animal是父类型,Cat是子类型
父类型转换成子类型叫做向下转型,语法合格
但是程序编译虽然通过,但是运行会出现异常,因为JVM堆内存当中真实存在的对象是Bird类型,Bird类型 无法转换成Cat类型,因为两种对象之间不存在任何继承关系,此时出现著名的异常:
java.lang.ClassCastException
类型转换异常,这种异常总是在“向下转型”的时候发生
*/
Cat c3 = (Cat)a3;
}
}
如何避免以上异常
- 向下转型存在安全隐患(编译过了,但是运行错了)
- 向上转型只要编译通过,运行一定不会出问题
- 向下转型编译通过,运行可能错误:java.lang.ClassCastException
- 使用instanceof运算符可以避免出现以上异常
③instanceof运算符
语法格式
(引用 instanceof 数据类型名)
- 以上运算符的执行结果类型是布尔类型,结果可能是true/false
关于运算结果true/false
假设:(a instanceof Animal)
true表示:a这个引用指向的对象是一个Animal类型
false表示:a这个引用指向的对象不是一个Animal类型
上面的代码升级为【改动的地方在代码最后】
public class Test {
public static void main(String[] args) {
//使用多态语法机制
Animal a2 = new Cat();
a2.move();//猫在走猫步
//以下程序为什么编译报错
/*
*因为编译阶段编译器检测到a2类型是Animal类型,从Animal.class字节码文件当中查找catchMouse()
*最终没有找到该方法,导致静态绑定失败,也就是编译失败
*/
//a2.catchMouse();
Cat c2 = (Cat)a2;
//父类型引用指向子类型对象【多态】
Animal a3 = new Bird();
/*
以下编译是没有问题的,因为编译器检查到a3数据类型是Aniaml
Animal和Cat之间存在继承关系,并且Animal是父类型,Cat是子类型
父类型转换成子类型叫做向下转型,语法合格
但是程序编译虽然通过,但是运行会出现异常,因为JVM堆内存当中真实存在的对象是Bird类型,Bird类型 无法转换成Cat类型,因为两种对象之间不存在任何继承关系,此时出现著名的异常:
java.lang.ClassCastException
类型转换异常,这种异常总是在“向下转型”的时候发生
*/
//Cat c3 = (Cat)a3;
if(a3 instanceof Cat){
Cat c3 = (Cat)a3;
c3.catchMouse();
}else if(a3 instanceof Bird){
Bird b2 = (Bird)a3;
b2.fly();
}
}
java规范中要求:在进行强制类型转换之前,建议采用instanceof运算符进行判断,避免异常的发生