继承


// Animal.java
public class Animal {public String name;public Animal(String name) {this.name = name;}public void eat(String food) {System.out.println(this.name + "is eating" + food);}
}
// Cat.java
class Cat {public String name;public Cat(String name) {this.name = name;}public void eat(String food) {System.out.println(this.name + "is eating" + food);}public void jump() {System.out.println(this.name + "is jumping");}
}
// Bird.java
class Bird {public String name;public Bird(String name) {this.name = name;}public void eat(String food) {System.out.println(this.name + "is eating" + food);}public void fly() {System.out.println(this.name + "is flying");}
}

观察可见:

  • Animal,Cat,Bird三个类都有相同的方法eat(),且实现的功能一样
  • 三个类都具有同样的属性name
  • 从逻辑上讲,Cat,Bird都是Animal的一种(他们之间是is-a关系)

所以我们可以让Cat,Bird继承Animal类,实现代码复用。本质上来讲继承就是为了代码的复用

继承的语法规则

extends

用关键字extends来实现继承,

class 子类/派生类 extends 父类/基类/超类{}

(像Cat,Bird这种类就叫做子类/派生类,而Animal这种被继承的类叫做父类/基类/超类)

注意:

  • 使用 extends 指定父类.
  • Java 是单继承,也就是说一个子类只能继承一个父类 (而C++/Python等语言支持多继承).
  • 子类会继承父类的所有 public 的字段和方法.
  • 对于父类的 private 的字段和方法, 子类中是无法访问的.
  • 子类的实例中, 也包含着父类的实例. 可以使用 super 关键字得到父类实例的引用.

super

super();//调用父类的构造方法 必须放在第一行   因为要构造子类要先构造父类
super.func();//调用父类的方法func()
super.data;  //调用父类的数据成员data

父类只能访问自己的成员 或者是方法
但子类可以通过关键字super访问父类的成员和方法
所以我们可以将Animal,Cat,Bird的代码优化如下:

// Animal.java
public class Animal {public String name;public Animal(String name) {this.name = name;}public void eat(String food) {System.out.println(this.name + "is eating" + food);}
}
// Cat.java
class Cat extends Animal{public Cat(String name) {super(name);}public void jump() {System.out.println(this.name + "is jumping");}
}
// Bird.java
class Bird extends Animal{public Bird(String name) {super(name);}public void fly() {System.out.println(this.name + "is flying");}
}

此时只需将Cat,Bird类中Animal有的字段和方法删除即可
注意:
在子类的构造方法中一定要先用super()构造父类,构造完后再构造子类自己

但是此时如果想要实现封装,将父类的name属性变为private,那么编译就会出错,因为子类无法访问private修饰的方法和字段,此时应该如何实现封装呢?

protected关键字

使用protected关键字就很好的解决了这个问题:

  • 对于类的调用者来说,并不能访问protected修饰的字段和方法
  • 但对于子类来说,protected所修饰的字段和方法是可以访问的

此处拓展几个权限修饰关键字的修饰范围:(default是什么关键字都不加)

范围 private default protected public
同一包中同一类
同一包中不同类 ×
不同包中的子类 × ×
不同包中非子类 × × ×

注意:
final所修饰的类不可被继承

注意区分继承和组合

  • 继承是一种is-a关系
  • 组合是一种has-a关系,是一种包含关系

组合并没有涉及到特殊的语法(诸如 extends 这样的关键字), 仅仅是将一个类的实例作为另外一个类的字段.比如:

class Student{...
}
class Teacher{...
}
class School{public Student[] students;public Teacher[] teacher;...
}

多态


向上转型

即将子类的值赋值给父类 /父类引用子类对象,比如:

Cat cat = new Cat("大不妞");

可以写成

Animal cat = new Cat("大不妞");

此时 cat 是父类 (Animal) 的引用, 指向子类 (Cat) 的实例. 这种写法称为 向上转型

向上转型发生的时机 :

  • 直接赋值
  • 方法传参
  • 方法返回

上面列举的是直接赋值,方法传参和方法返回见下:

方法传参的形式()

public class Test {public static void main(String[] args) {Cat cat = new Cat("大不妞");feed(cat);}public static void feed(Animal animal) {animal.eat(" fish");}
}

方法返回

public class Test {public static void main(String[] args) {Animal animal = findMyAnimal();}public static Animal findMyAnimal() {Cat cat = new Cat("大不妞");return cat;}
}

动态绑定

将前面示例的代码稍作改动,让子类Cat和Animal有一个同名但实现功能不同的方法eat();

// Animal.java
public class Animal {public String name;public Animal(String name) {this.name = name;}public void eat(String food) {System.out.println("我是一只小动物");System.out.println(this.name + "is eating" + food);}
}
// Cat.java
class Cat extends Animal{public Cat(String name) {super(name);}public void eat(String food) {System.out.println("我是一只小猫咪");System.out.println(this.name + "is eating" + food);}
}class Demo0223 {public static void main(String[] args) {Animal animal1 = new Animal("大不妞");animal1.eat(" fish");Animal animal2 = new Cat("大不妞");animal2.eat(" fish");}
}

执行结果:

此时, 我们发现:

  • animal1 和 animal2 虽然都是 Animal 类型的引用, 但是 animal1 指向 Animal 类型的实例, 而animal2 指向的是Cat 类型的实例.
  • animal1 和 animal2 分别调用 eat 方法, 发现 animal1.eat() 实际调用了父类的方法, 而animal2.eat() 实际调用了子类的方法.

由此可得:
在 Java 中, 子类和父类拥有同名方法,此时调用该方法时究竟执行的是子类的方法还是父类的方法 , 要看究竟这个引用指向的是子类对象还是父类对象. 这个过程是程序运行时决定的(而不是编译期), 因此称为 动态绑定

反汇编发现程序编译时确实调用的是父类的方法 但是运行时却调用的子类的方法 这就是运行时绑定(也叫动态绑定),这就是所谓的 编译看左,运行看右

方法重写(override)

像是上述代码当中的eat():
子类实现父类的同名方法, 并且参数的类型和个数完全相同, 这种情况称为 方法覆写/重写/覆盖(Override)

方法重写的注意事项:

  1. 普通方法可以重写, static 修饰的静态方法不能重写
  2. 重写中子类的方法的访问权限不能低于父类的方法访问权限(也就是说如果父类方法是用protected修饰,那么子类方法肯定不能是public修饰)

对于重写的方法可以显示的给一个注解@override

class Cat extends Animal{public Cat(String name) {super(name);}@overridepublic void eat(String food) {System.out.println("我是一只小猫咪");System.out.println(this.name + "is eating" + food);}
}

这样做的好处在于这个注解能帮我们进行一些合法性校验.
例如不小心将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法, 就会编译报错, 提示无法构成重写.

重写和重载的区别:

方法重写 方法重载
方法名 相同 相同
参数列表 相同 不同
返回值 相同 不做要求
范围 继承 同一个类
限制 被重写的方法不能拥有比父类更严格的访问控制权限 没有访问控制权限要求

发生多态要满足两个条件:(这个多态叫做运行时多态)

  1. 父类需要引用子类对象(即向上转型)
  2. 通过父类的引用调用子类和父类同名的覆盖方法

class对象存储位置在方法区
反射: 获取class对象(用三种方法会发现class对象地址一样==》class对象只有一个)

向下转型

向下转型是将子类对象转给父类,一般不太常见,下面将介绍他的作用

还是刚刚这段代码

// Animal.java
public class Animal {public String name;public Animal(String name) {this.name = name;}public void eat(String food) {System.out.println("我是一只小动物");System.out.println(this.name + "is eating" + food);}
}
// Cat.java
class Cat extends Animal{public Cat(String name) {super(name);}@overridepublic void eat(String food) {System.out.println("我是一只小猫咪");System.out.println(this.name + "is eating" + food);}public void jump() {System.out.println(this.name + "is jumping");}
}

让猫咪吃东西

        Animal animal = new Cat("大不妞");animal.eat(" fish");
//执行结果
//大不妞 is eating fish

如果我们想让猫咪跑起来

animal.jump();

此时编译出错,找不到jump();方法
因为编译看左,运行看右,编译时期编译器先在Animal类中看有没有jump方法,没有所以直接编译出现错误
那如果想要让猫咪跑起来就只能

     Animal animal = new Cat("大不妞");Cat cat = (Cat)animal;animal.jump();

这种就是向下转型,但是向下转型存在风险,比如:

     Animal animal = new Bird("啾啾");Cat cat = (Cat)animal;animal.jump();
//此时执行会抛出类型转换异常 java.lang.ClassCastException

因为本质上animal是一个Bird类型的,和Cat直接没有关系,所以就会出现类型转换异常
==》要发生向下转型最好先判断是否是一个实例

instanseof

instanseof可以判定一个引用是否是某个类的实例

if(Animal instanseof Cat){Cat cat=(Cat)animal;cat.jump();
}

构造方法内是否可以发生运行时绑定?

答案是可以,例子见下

class A {public A() {func();}public void func() {System.out.println("A.func()");}
}class B extends A {private int num = 1;@Overridepublic void func() {System.out.println("B.func() " + num);}
}
public class Test {public static void main(String[] args) {B b = new B(); }
}
// 执行结果
B.func() 0

为什么执行出来的num会是0?
构造子类对象前要先构造父类
所以构造 B 对象的同时, 会调用 A 的构造方法.
A 的构造方法中调用了 func 方法, 此时会触发动态绑定, 会调用到 B 中的 func
此时 B 对象自身还没有构造, 此时 num 处在未初始化的状态, 值为 0.

使用多态的好处是什么?

  1. 类调用者对类的使用成本进一步降低.
    封装 是让类的调用者不需要知道类的实现细节.
    多态 能让类的调用者连这个类的类型是什么都不必知道, 只需要知道这个对象具有某个方法即可.
    因此, 多态可以理解成是封装的更进一步, 让类调用者对类的使用成本进一步降低.

  2. 能够降低代码的 “圈复杂度”(一段代码中的分支和循环语句越多,圈复杂度越高), 避免使用大量的 if - else

  3. 可扩展能力更强,使用多态的方式代码改动成本也比较低.

Java入门part6--继承和多态相关推荐

  1. JAVA入门_继承与重载_饲养员喂养动物

    JAVA入门_继承与重载_饲养员喂养动物 实验要求 Tiger类 Feeder类 MainClass 运行结果 实验要求 本实验要求:本实验以饲养员喂养老虎为业务背景,体验"函数重载&quo ...

  2. Java实验3继承、多态

    继承.多态(接口和包) 实验目的 (1) 掌握Java语言中继承和多态的相关概念 (2) 掌握Java程序设计中继承和多态机制的使用 (3) 掌握Java语言中接口和包的概念及使用 实验内容及要求 仿 ...

  3. Java中的继承 与 多态(中)

    先导: 我们在<Java中的继承 与 多态(上)>当中讲解了如下几个问题,  1.继承是什么   2.super关键字   3.特殊考点-父子类中不同代码块的实现顺序 所以现在我们对于继承 ...

  4. Java基础:继承、多态、抽象、接口

    第一讲    继承 一.继承概述 1.多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可. 2.通过extends关键字可以实现类与类的 ...

  5. Java基础篇--继承(inherit),多态(Polymorphism)

    Java基础篇--继承(inherit),多态(Polymorphism) 1. 继承概述 1.1 什么是继承 1.2 为什么要使用继承 1.3 继承的特点 1.4 继承的优点 2. 组合设计模式 2 ...

  6. Java回顾 封装 继承和多态

    封装 什么是封装 封装:就是隐藏对象的属性和实现细节,仅对外提供公共访问方式. 封装时的权限控制符区别如下: 封装的意义 对于封装而言,一个对象它所封装的是自己的属性和方法,所以它是不需要依赖其他对象 ...

  7. 【java笔记】继承与多态

    多态性的前提:extends继承或者implements实现 继承与多态: 定义:同一个操作被不同类型对象调用时可能产生不同 的行为 解释:如果一个类有很多子类,并且这些子类都重写了父类中的某个方法, ...

  8. Java分别采用继承、多态、抽象类、接口实现猫和狗的入门案例

    目录 采用继承的思想实现猫和狗的案例 采用多态的思想实现猫和狗的案例 采用抽象类的思想实现猫和狗的案例 采用接口的思想实现猫和狗的案例 采用继承的思想实现猫和狗的案例 分析: ①猫: 成员变量:姓名, ...

  9. java中的多态与继承_【Java学习笔记之十六】浅谈Java中的继承与多态

    1.  什么是继承,继承的特点? 子类继承父类的特征和行为,使得子类具有父类的各种属性和方法.或子类从父类继承方法,使得子类具有父类相同的行为. 特点:在继承关系中,父类更通用.子类更具体.父类具有更 ...

最新文章

  1. 使用netty实现一个http挡板,轻量又实用。收藏起来吧
  2. ipad xcode连接不了iPad的。
  3. SqoopFlume、Flume、HDFS之间比较
  4. 【新手教程】CE找武林外传基址方法
  5. Python is同一性运算符和==相等运算符区别
  6. Docker上部署GitLab服务器
  7. ios 画带有箭头的线_ios纯色箭头与渐变色箭头的实现
  8. eclipse android 服务端,Eclipse搭建服务器,实现与Android的简单通信
  9. ERP实施过程中的沟通管理研究
  10. ThinkPHP学生管理系统
  11. vs2010断点调试详细教程
  12. 手工编译Flex SDK 多国语言包
  13. 国产低代码开发平台,这5个值得一试
  14. 第3章【思考与练习4】数据清洗,从studentsInfo.xlsx 文件的“Group1”表单中读取数据。数据填充,使用习题1的数据,使用列的平均值填充“体重”和“成绩”列的NaN数据。
  15. @Autowired和@Resource的区别
  16. 如何在outlook里面撤回邮件?邮件撤回成功后对方还能不能看到?
  17. Linux这些年经历了什么?
  18. css 将标签固定在底部
  19. cadworx可以画设备流程图吗,CADWORX中心线法绘制管道
  20. 【中医百科app——中药、方剂、针灸】养生必备,随时随地查看症状

热门文章

  1. python 仪表盘监控_Python 全栈开发 -- 监控篇
  2. 概率与数理统计——大数定律
  3. 常用眼底图像数据集简介及下载--糖尿病视网膜病变 EyePacs,APTOS2019,STARE数据集
  4. ros实验操作——订阅者Subscriber的编程实现
  5. 小米商城抢购脚本_小米10系列MiCare保障服务上线:免费2次换屏 549元起
  6. JMockit 指南 翻译
  7. ESP8266(基于arduino平台)每篇一个知识点—1.wifi STA模式连接指定wifi
  8. Python软件编程等级考试三级——20210905
  9. python爬虫网易云_Python爬虫网易云音乐Top50热门歌单
  10. 【python爬虫】爬取Bing词典的单词存到SQLite数据库(加了pyqt5界面显示)