前情提要: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. [转帖]find排除一个或多个目录的方法

    find排除一个或多个目录的方法 百度就是垃圾,搜索结果千篇一律,错抄错.google一下,总结find排除某个目录的方法: How to exclude a directory in find . ...

  2. 京东哥伦布即时设计平台ChatGPT落地实践

    一.平台介绍 即时设计平台是一个即时搭建c端楼层的开发平台,支持通过导入relay设计稿url完成Ui2Code,在此基础上完成前端可视化搭建,同时支持通过ChatGPT完成一句话需求,搭建后的楼层自 ...

  3. 【JS 逆向百例】网洛者反爬练习平台第二题:JJEncode 加密

    关注微信公众号:K哥爬虫,持续分享爬虫进阶.JS/安卓逆向等技术干货! 声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后 ...

  4. Python 潮流周刊#12:Python 中如何调试死锁问题?

    查看全文: https://pythoncat.top/posts/2023-07-22-weekly 文章&教程 1.使用 PyStack 调试 Python 中的崩溃和死锁 (英) 2.介 ...

  5. NET Core 通过扩展方法实现密码字符串加密(Sha256和Sha512)

    using System; using System.Security.Cryptography; using System.Text; namespace SPACore.Extensions { ...

  6. 顺颂秋冬<一>

    起名字真难. 原来想给这个合集起个积极的名字,记录鄙人浅薄的认知和内心的荒芜. 以及所遇见的温暖. 想来想去,不过是 浮生旧茶  西楼残月之类的 难堪大用. 后来想起来一句, 即,顺颂时祺,秋绥冬禧, ...

  7. 【C语言进阶】【小项目】实现一个通讯录【C语言知识点汇总项目】通过这个项目,掌握C语言重要知识点

    [C语言进阶][小项目]实现一个通讯录[C语言知识点汇总项目]通过这个项目,掌握C语言重要知识点 欢迎来到#西城s的博客,今天,博主带着大家用C实现一个通讯录!干货满满不要错过噢! 作者: #西城s ...

  8. PHP 编程技巧与优化

    PHP 编程技巧与优化 1. 尽量采用大量的PHP内置函数. 2. 使用echo代替print,并且使用echo的多重参数(译注:指用逗号而不是句点)代替字符串连接 3. 用单引号代替双引号来包含字符 ...

  9. MarkDown书写语法(常用格式)

    实际上每个 Markdown 应用程序都实现了稍有不同的 Markdown 语法,熟悉MarkDown书写语法常用格式,满足日常文字编辑需求 1.标题 请在单词或短语前面添加井号 (#) .# 的数量 ...

  10. ASP.NET Core分布式项目实战(详解oauth2授权码流程)--学习笔记

    最近公司产品上线,通宵加班了一个月,一直没有更新,今天开始恢复,每日一更,冲冲冲 任务13:详解oauth2授权码流程 我们即将开发的产品有一个用户 API,一个项目服务 API,每个服务都需要认证授 ...