前情提要: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. [转帖]Redis 使用指南:深度解析 info 命令

    https://www.cnblogs.com/hwpaas/p/9442410.html Redis 是一个使用  ANSI C 编写的开源.基于内存.可选持久性的键值对存储数据库,被广泛应用于大型 ...

  2. 海量数据 vastbase G100 V2.2安装简单总结

    海量数据vastbase G100 V2.2 安装总结 背景说明 最近进行信创四期的数据库兼容性验证, 获取了海量数据的一个信创名录内的安装介质. 一直忙于出差, 今天晚上趁着冬至回家比较早在家里进行 ...

  3. 京音平台-一起玩转SCRM之电销系统

    作者:京东科技 李良文 一.前言 电销是什么?就是坐席拿着电话给客户打电话吗?no no no,让我们一起走进京音平台之电销系统. 京音平台2020年初开始建设,过去的两年多的时间里,经历了跌宕起伏, ...

  4. vue/cli的配置详解

    查看vue/cli的配置 vue的脚手架隐藏了所有的webpack相关的配置,若是想要查看webpack的配置 你可以去执行 vue inspect > output.js 这样就可以查看它的配 ...

  5. BigDecimal详解和精度问题

    JavaGuide :「Java学习+面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识. BigDecimal 是大厂 Java 面试常问的一个知识点. <阿里巴巴 Java 开发 ...

  6. LyScript 实现Hook隐藏调试器

    LyScript 插件集成的内置API函数可灵活的实现绕过各类反调试保护机制,前段时间发布的那一篇文章并没有详细讲解各类反调试机制的绕过措施,本次将补充这方面的知识点,运用LyScript实现绕过大多 ...

  7. 纪念JDBC

    技术总是在不断更新变化的,尤其是在IT编程领域. 有时候我们理所当然的用着现成的框架,以至于用的太过于顺手,更要时不时的骂一句: 什么垃圾框架?我家狗都不会用! 如果那些被拍死在沙滩的"前浪 ...

  8. JavaScript快速入门(一)

    JavaScript快速入门(二) 语句 只需简单地把各条语句放在不同的行上就可以分隔它们 var a = 1 var b = 2 如果想把多条语句放在同一行上,就需要用分号隔开 var a = 1; ...

  9. RabbitMQ高级知识(消息可靠性,死信交换机,惰性队列,MQ集群)

    服务异步通信-高级篇 消息队列在使用过程中,面临着很多实际问题需要思考: 1.消息可靠性 消息从发送,到消费者接收,会经历多个过程: 其中的每一步都可能导致消息丢失,常见的丢失原因包括: 发送时丢失: ...

  10. api接口调用

    api接口调用 CURL 是一个利用URL语法规定来传输文件和数据的工具,支持很多协议,如HTTP.FTP.TELNET等.最爽的是,PHP也支持 CURL 库.使用PHP的CURL 库可以简单和有效 ...