前情提要:https://www.cnblogs.com/DAYceng/category/2227185.html

重写

注意事项和使用细节

方法重写也叫方法覆法,需要满足下面的条件

1.子类的方法的参数,方法名称,要和父类方法的参数,方法名称完全一样。

2.子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类,比如父类返回类型是Object ,子类方法返回类型是String

public Object getInfo(){}
...
public String getInfo(){}

3.子类方法不能缩小父类方法的访问权限

重载overload与重写override的区别

练习

1.编写一个Person类,包括属性/private (name.age),构造器、方法say(返回自我介绍的字符串)。

2.编写一个Student类,继承Person类,增加id、score属性/private,以及构造器,定义say方法(返回自我介绍的信息)。

3.在main中,分别创建Person和Student对象,调用say方法输出自我介绍。

public class OverrideExercise {
public static void main(String[] args) {
Person person = new Person("jk", 16);
System.out.println(person.say());
Student student = new Student(1,88.6);
System.out.println(student.say());
}
} class Person{
private String name;
private int age; public Person(){
} public Person(String name, int age) {
this.name = name;
this.age = age;
} public String say(){
return "名字: " + name + " 年龄: " + age;
}
} class Student extends Person{
private int id;
private double score; public Student() {
} public Student(int id, double score) {
this.id = id;
this.score = score;
} public String say(){
return "id: " + id + " 成绩: " + score;
}
}

多态

多态其实是一种思想,它可以有多种体现方式

多态的具体体现

1.方法的多态

方法的重写和重载就体现多态思想。

2.对象的多态(核心)

(1)一个对象的编译类型和运行类型可以不一致

(2)编译类型在定义对象时,就确定了,不能改变

(3)运行类型是可以变化的.

(4)编译类型看定义时=号的左边,运行类型看=号的右边

例如:

Animal animal = new Dog();

【animal编译类型是Animal,运行类型Dog】

animal = new Cat();

【animal的运行类型变成了Cat,编译类型仍然是Animal】

动物类

public class Animal {
public void cry(){
System.out.println("Animal cry()动物在叫");
}
}

狗类

public class Dog extends Animal{
// @Override
public void cry() {
System.out.println("Dog cry()狗叫");
}
}

猫类

public class Cat extends Animal{
//@Override语法校验,写不写都行
public void cry() {
System.out.println("Cat cry()猫叫");
}
}

演示

public class PolyObject {
public static void main(String[] args) {
// animal编译类型是Animal,运行类型Dog
Animal animal = new Dog();
//因为执行到该行是,animal的运行类型是Dog
//所以cry是Dog的cry
animal.cry();//Dog cry()狗叫 // animal编译类型是Animal,运行类型Cat
animal = new Cat();
animal.cry();//Cat cry()猫叫
}
}

多态的细节

多态的前提:两个对象(类)存在继承关系

多态的向上转型

​ 1)本质:父类的引用指向了子类的对象

​ 2)语法:父类类型引用名=new子类类型();

​ 3)特点:

​ 编译类型看左边,运行类型看右边;

​ 可以调用父类中的所有成员(需遵守访问权 限),不能调用子类中特有成员;

​ 最终运行效果看子类的具体实现;

动物类

public class Animal {
String name ="动物";int age = 10;
public void sleep(){
System.out.println("睡");
}
public void run(){
System.out.println("跑");
}
public void eat(){
System.out.println("吃");
}
public void show(){
System.out.println("hello,你好");
} }

猫类

public class Cat extends Animal{
public void eat(){//方法重写
System.out.println("猫吃鱼");
}
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}

演示

public class PolyDetail {
public static void main(String[] args) {
//向上转型:父类的引用指向了子类的对象
// 语法:父类类型引用名= new子类类型();
Animal animal = new Cat();//向上转型
Object obj = new Cat();//可以吗?可以 Object也是 Cat的父类
//可以调用父类中的所有成员(需遵守访问权限)
//但是不能调用子类的特有的成员
//因为在编译阶段,能调用哪些成员,是由编译类型来决定的
// animal.catchMouse();错误,因为该方法没有在父类中存在,是Cat的特有方法
//最终运行效果看子类(运行类型)的具体实现,即调用方法时,按照从子类(运行类型)开始查找方法
// ,然后调用,规则我前面我们讲的方法调用规则一致。
animal.eat();//从猫类找,猫吃鱼
animal.run();//跑
animal.show();//hello,你好
animal.sleep();//睡
}
}
多态的向下转型

目的是调用子类特有的方法

1)语法:子类类型引用名=(子类类型)父类引用;

2)只能强转父类的引用,不能强转父类的对象

3)要求父类的引用必须指向的是当前目标类型的对象;

4)可以调用子类类型中所有的成员;

接着上面的演示例子

我们可以通过强转来实现对猫类特定方法的访问

public class PolyDetail {
public static void main(String[] args) {
//向上转型:父类的引用指向了子类的对象
// 语法:父类类型引用名= new子类类型();
Animal animal = new Cat(); animal.eat();//从猫类找,猫吃鱼 //希望调用Cat的 catchMouse方法
// 多态的向下转型
//语法:子类类型引用名=(子类类型)父类引用;
//编译类型Cat,运行类型Cat
//相当于生成了一个新的引用cat也指向cat对象
//简单理解就是"披着Cat皮的Animal"
Cat cat = (Cat) animal;//猫抓老鼠
//(2)要求父类的引用必须指向的是当前目标类型的对象
//可以吗?错误
//因为父类Animal的引用名animal指向的是子类类型Cat
//Dog也是子类类型,两者间不能转换(因为不存在关系,是平级的)
Dog dog = (Dog) animal; System.out.println("ok~~");
}
}
属性没有重写这种说法

属性的值直接看编译类型

public class PolyDetail02 {
public static void main(String[] args) {
//属性没有重写之说!属性的值看编译类型
Base base = new Sub();//向上转型
//看编译类型,Base,所以结果是10
System.out.println(base.count);
}
}
class Base{
int count = 10;
} class Sub extends Base{
int count = 20;
}
instanceof

instanceof用于判断对象运行类型是否为XX类型或其子类

public class PolyDetail03 {
public static void main(String[] args) {
BB bb = new BB();
System.out.println(bb instanceof BB);// true
System.out.println(bb instanceof AA);// true
//aa编译类型AA,运行类型是BB
AA aa = new BB();
System.out.println(aa instanceof AA);
System.out.println(aa instanceof BB);
}
}
class AA{}//父类
class BB extends AA{}//子类

练习

1.
public class PolyExercise01{
public static void main(Stringlargs){
double d = 13.4;//ok
long l = (long)d; //ok
System.out.println(l): //13
int in = 5; //ok
boolean b =(boolean)in;//不对,boolean -> int
Object obj="Hello";//可以,向上转型
String objStr = (String)obj;//可以向下转型
System.out.println(objStr); // hello Object objPri = new Integer(5);//可以,向上转型
String str = (String)objPri;//错误ClassCastExcetpion,指向Integer的父类引用,转成String
Integer str1 = (Integer)objPri;//可以,向下转型
}
}
2.
public class PolyExercise02 {
public static void main(String[] args) {
SubEx s = new SubEx();
System.out.println(s.count);//20
s.display();//20
BaseEx b = s;//相当于BaseEx b = new SubEx();
System.out.println(b == s);//T
System.out.println(b.count);//因为属性只看编译类型,故这里为10
b.display();//方法调用,先看运行类型,是SubEx,所以输出20
} } class BaseEx {//父类
int count = 10; public void display() {
System.out.println(this.count);
}
} class SubEx extends BaseEx{//子类
int count = 20;
public void display(){
System.out.println(this.count);
}
}

总结

1、多态是是由java的独特设计所产生的一种精简代码的思想【因为java需要将编译与运行过程分开】

2、向上转型指父类引用子类对象【 Animal animal = new Cat(),Cat需要先继承Animal】

具体使用场景:

​ 当我们想使用父类的方法,并且希望这些方法如果有被子类重写,应该获取子类重写的值的情况

例如:父类是Animal,子类是Cat,Cat重写了父类的eat()方法,我想在使用父类方法eat()时,返回子Cat重写的eat()所获取到的值,这时需要使用向上转型

3、向下转型指父类在向上转型时,若想使用子类的特定方法,需要再建立一个新的子类引用并强制转换到父类的引用名上

Animal animal = new Cat();
Cat cat = (Cat) animal;//"披着Cat皮的Animal"

4、属性调用只看编译类型,方法调用先看运行类型

动态绑定机制

java的动态绑定机制

1.当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定

2.当调用对象属性时,没有动态绑定机制,哪里声明,那里使用【按作用域走】

举例说明

public class DynamicBinding {
public static void main(String[] args) {
//a 的编译类型A,运行类型B
A a = new B();//向上转型
/*
* 由于a绑定的运行类型是B,将B中sum()方法注释后,由于继承机制的影响,子类B会去找父类A的sum()方法
* 而父类A的sum()方法又调了getI(),此时触发动态绑定机制,java去判断a绑定的的运行类型
* 查到是B,于是去调B内的getI(),返回B中的属性i,即20
* 给到A的sum()方法,故20+10 = 30
*/
System.out.println(a.sum());//?40->30
/*
* 同理,在注释B中sum1()方法后,子类B会去找父类A的sum1()方法
* 其返回i + 10,i没有动态绑定机制,在A中声明,就在A中使用,故返回10+10=20
* */
System.out.println(a.sum1());//?30->20 }
} class A {//父类
public int i = 10;
public int sum() {
return getI() +10;
}
public int sum1() {
return i + 10;
}
public int getI() {
return i;
}
} class B extends A {//子类
public int i = 20;
// public int sum() {
// return i + 20;
// }
public int getI(){
return i;
}
// public int sum1(){
// return i + 10;
// }
}

多态数组

定义

数组的定义类型为父类类型,里面保存的实际元素类型为子类类型

演示

需要依次定义Person(父类)、Student和Teacher(子类)三个类

public class Person {//父类
private String name;
private int age; public Person(String name, int age) {
this.name = name;
this.age = age;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public String say() {//返回名字和年龄
return name + "\t" +age;
} }
//==================================================
public class Student extends Person{
private double score; public Student(String name, int age, double score) {
super(name, age);
this.score = score;
} public double getScore() {
return score;
} public void setScore(double score) {
this.score = score;
} //重写父类say()
@Override
public String say() {
return super.say() + " score = " + score;
} //特有的方法
public void study() {
System.out.println("学生" +getName() + " 正在学java.. . ");
} }
//==================================================
public class Teacher extends Person{
private double sal; public Teacher(String name, int age, double sal) {
super(name, age);
this.sal = sal;
} public double getSal() {
return sal;
} public void setSal(double sal) {
this.sal = sal;
} //重写父类say()
@Override
public String say() {
return super.say() + " salary = " + sal;
} //特有方法
public void teach(){
System.out.println("老师" +getName() +"正在讲java课程...");
} }

测试类PloyArray

public class PloyArray {
public static void main(String[] args) {
//应用实例:现有一个继承结构如下:要求创建1个Person对象、
//2个Student对象和2个Teacher对象,统一放在数组中,并调用每个对象say方法 //数组的定义中有:数组中的元素可以是任何数据类型,包括基本类型和引用类型
//这里其实是用引用类型的数据(即对象persons)组成了数组,因此多态数组也是一个数组
Person[] persons = new Person[5];
persons[0] = new Person("jack",20);
persons[1] = new Student("jk",18, 100);
persons[2] = new Student( "smith",19, 30.1);
persons[3] = new Teacher( "scott",30,20000);
persons[4] = new Teacher( "king" ,50,25000); //循环遍历多态数组,调用say
for (int i = 0; i < persons.length; i++) {
//persons[i]的编译类型是Person
//但是运行类型根据所存放的对象动态变化,由JVM去判断
System.out.println(persons[i].say()); //动态绑定
//错误,因为这些方法是写在子类里面的,需要进行向下转型才能使用
//首先先需要判断persons[i]的运行类型(方法看运行类型)
// persons[i].teach();
// persons[i].study();
if(persons[i] instanceof Student){
Student student = (Student)persons[i];//向下转型
student.study();
//或((Student)persons[i]).study();
} else if (persons[i] instanceof Teacher) {
Teacher teacher = (Teacher)persons[i];
teacher.teach();
//或((Teacher)persons[i]).teach();
}else if(persons[i] instanceof Person) { }else {
System.out.println("类型有误,请检查");
}
}
}
}
/*
jack 20
jk 18 score = 100.0
学生jk 正在学java.. .
smith 19 score = 30.1
学生smith 正在学java.. .
scott 30 salary = 20000.0
老师scott正在讲java课程...
king 50 salary = 25000.0
老师king正在讲java课程...
*/

总结

数组中的元素可以由任何数据类型构成,包括基本类型引用类型。【ps:数组本身也是引用类型】

而所谓的"多态数组"就是由对象构成的一种特殊数组

其中的对象可以遵循多态的思想来使用,使得这个特殊的数组本身也具有多态的性质

多态参数

方法定义的形参类型为父类类型,实参类型允许为子类类型

应用实例

定义员工类Employee,包含姓名和月工资[private],以及计算年工资getAnnual的方法。

普通员工和经理继承了员工,经理类多了奖金bonus属性和管理manage方法,普通员工类多了work方法,普通员工和经理类要求分别重写getAnnual方法

测试类中添加一个方法showEmpAnnal(Employee e),实现获取任何员工对象的年工资,并在main方法中调用该方法[e.getAnnual0]

测试类中添加一个方法,testWork,如果是普通员工,则调用work方法,如果是经理,则调用manage方法

先分别定义Employee(父类)、Worker和Manager(子类)

public class Employee {
private String name;
private double salary; public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
//得到年工资的方法
public double getAnnual() {
return 12 * salary;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public double getSalary() {
return salary;
} public void setSalary(double salary) {
this.salary = salary;
}
}
//===========================================
public class Worker extends Employee{
public Worker(String name, double salary) {
super(name, salary);
} public void work(){
System.out.println("普通员工" +getName() + " is working");
} @Override
public double getAnnual() {//没有额外收入直接调用就行
return super.getAnnual();
}
}
//===========================================
public class Manager extends Employee{
private double bonus; public Manager(String name, double salary, double bonus) {
super(name, salary);
this.bonus = bonus;
} public double getBonus() {
return bonus;
} public void setBonus(double bonus) {
this.bonus = bonus;
} public void manage() {
System.out.println("经理" +getName() +" is managing");
}
//重写获取年薪方法
@Override
public double getAnnual(){
return super.getAnnual() + bonus;
}
}

测试类PolyParameter

public class PolyParameter {
public static void main(String[] args) {
Worker tom = new Worker( "tom",2500);
Manager milan = new Manager("milan",5000,200000);
PolyParameter polyParameter = new PolyParameter();
// 传进去的tom与Worker绑定,因此调的是Worker中重写的getAnnual()
polyParameter.showEmpAnnual(tom);
//传进去的milan与Manager绑定,因此调的是Manager中重写的getAnnual()
polyParameter.showEmpAnnual(milan); polyParameter.testWork(tom);
} // showEmpAnnual(Employee e)
//实现获取任何员工对象的年工资,并在main方法中调用该方法[e.getAnnual()]
public void showEmpAnnual(Employee e) {
System.out.println(e.getAnnual());//动态绑定机制
} //添加一个方法,testWork ,如果是普通员工,则调用work方法,如果是经理,则调用manage方法
public void testWork(Employee e){
if(e instanceof Worker){
((Worker) e).work();//向下转型操作
} else if (e instanceof Manager) {
((Manager) e).manage();
}else {
System.out.println("nothing");
}
}
}

总结

和多态数组的概念类似,所谓的"多态参数"也就是将对象作为输入形参时的特殊情况

参数中的对象可以遵循多态的思想来使用

【Java复健指南07】OOP中级02-重写与多态思想的更多相关文章

  1. 【Java复健指南09】项目练习全解--房屋出租系统

    一个基于文本界面的综合练习,主要用于串联和回忆知识点,比较简单 各个界面的设计样式 主菜单 =============房屋出租系统菜单============ 1 新 增 房 源 2 查 找 房 屋 ...

  2. 【Java复健指南15】链表LinkedList及其说明

    链表LinkedList by Java 之前有写过一些记录(引用),但是忘了乱了,现在重新梳理一遍 链表是Java中List接口的一种实现 定义(引用) 链表(linked list)是一种物理存储 ...

  3. Java工程师学习指南 中级篇

    Java工程师学习指南 中级篇 最近有很多小伙伴来问我,Java小白如何入门,如何安排学习路线,每一步应该怎么走比较好.原本我以为之前的几篇文章已经可以解决大家的问题了,其实不然,因为我写的文章都是站 ...

  4. Java工程师学习指南(中级篇)

    Java工程师学习指南 中级篇 最近有很多小伙伴来问我,Java小白如何入门,如何安排学习路线,每一步应该怎么走比较好.原本我以为之前的几篇文章已经可以解决大家的问题了,其实不然,因为我写的文章都是站 ...

  5. 013 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 07 基本数据类型变量的存储

    013 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 07 基本数据类型变量的存储 变量和它的值如何在内存中进行存储的? 前面学习过:Java中的数据类型分为基本 ...

  6. Java工程师学习指南 完结篇

    Java工程师学习指南 完结篇 先声明一点,文章里面不会详细到每一步怎么操作,只会提供大致的思路和方向,给大家以启发,如果真的要一步一步指导操作的话,那至少需要一本书的厚度啦. 因为笔者还只是一名在校 ...

  7. Java工程师学习指南

    java学习指南-四个部分:分别是入门篇,初级篇,中级篇,高级篇 第一步是打好Java基础,掌握Java核心技术,                                            ...

  8. Java工程师学习指南 初级篇

    Java工程师学习指南 初级篇 最近有很多小伙伴来问我,Java小白如何入门,如何安排学习路线,每一步应该怎么走比较好.原本我以为之前的几篇文章已经可以解决大家的问题了,其实不然,因为我之前写的文章都 ...

  9. Java工程师学习指南 入门篇

    Java工程师学习指南 入门篇 最近有很多小伙伴来问我,Java小白如何入门,如何安排学习路线,每一步应该怎么走比较好.原本我以为之前的几篇文章已经可以解决大家的问题了,其实不然,因为我之前写的文章都 ...

  10. Java工程师学习指南(初级篇)

    Java工程师学习指南 初级篇 最近有很多小伙伴来问我,Java小白如何入门,如何安排学习路线,每一步应该怎么走比较好.原本我以为之前的几篇文章已经可以解决大家的问题了,其实不然,因为我之前写的文章都 ...

随机推荐

  1. [转帖]dd命令中dsync和fsync区别

    在Linux系统中经常会使用dd命令来测试硬盘的写入速度,命令会涉及几个常用参数:sync.dsync.fsync与fdatasync # dd if=/dev/zero of=/tmp/1G bs= ...

  2. [转帖]JAVA⽣态的微服务⽆侵⼊链路追踪

    https://v5.6-docs.rainbond.com/docs/v5.3/advanced-scenarios/devops/pinpoint/#pinpoint%E7%AE%80%E4%BB ...

  3. scss常用语法

    在线编译 https://wow.techbrood.com/fiddle/11143 群组选择器的嵌套[编译前] .container { h1, h2, h3 {margin-bottom: .8 ...

  4. echarts定义饼状图的指向线内容

    定义饼状图的指向线内容 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&qu ...

  5. Pytest 源码解读 [1] - [pluggy] 核心设计理念浅读

    背景: Pytest 是一个功能强大的 Python 测试框架,它使用了一个名为 "pluggy" 的插件系统来扩展其功能.在 Pytest 的源码中,pluggy 模块负责实现插 ...

  6. C/C++ 通用ShellCode的编写与调用

    首先,我们的ShellCode代码需要自定位,因为我们的代码并不是一个完整的EXE可执行程序,他没有导入表无法定位到当前系统中每个函数的虚拟地址,所以我们直接获取到Kernel32.dll的基地址,里 ...

  7. 从嘉手札<2023-11-13>

    1. 很多时候 成功并不等同于成长 成功是很多因素复合形成的一种结果 而并不等同于一个人阅历的丰富.认知的提高 2. 我一直认为 世界不属于投机者 也不属于堕落者 信念感在这个大数据泛滥.碎片化汹涌的 ...

  8. centos7.9离线升级openssl和openssh9.2

    前言 最近有几台服务器漏扫出了关于openssh的漏洞,升级完后顺便记录一下. 环境 CentOS Linux release 7.9.2009 (Core) 开始升级 准备工作 下载安装包: zli ...

  9. 多进程|基于非阻塞调用的轮询检测方案|进程等待|重新理解挂起|Linux OS

    说在前面 今天给大家带来操作系统中进程等待的概念,我们学习的操作系统是Linux操作系统. 我们今天主要的目标就是认识wait和waitpid这两个系统调用. 前言 那么这里博主先安利一下一些干货满满 ...

  10. CF1921F Sum of Progression 题解

    题目链接:CF 或者 洛谷 一道经典的类型题,把这种类型的题拿出来单独说一下. 注意到问题中涉及到需要维护 \(a_{x+k\times step}\) 这样的信息,这样的信息很难用树型结构维护,比较 ...