Java中的继承 与 多态(中)
先导:
我们在《Java中的继承 与 多态(上)》当中讲解了如下几个问题,
1.继承是什么
2.super关键字
3.特殊考点-父子类中不同代码块的实现顺序
所以现在我们对于继承已经有了一些了解。我们需要继承,是为了实现代码的复用,但是继承的效果远不止如此。
在现在有的手机里,来电显示号码,地区,联系人姓名貌似是一件大家都习以为常的事儿,但是在以前的手机里,来电显示只会显示号码,其余信息一概不显示。随着技术的更新换代,显然信息的增多对于人们是有益的,但是由于还有相当一部分人还在使用老的服务(即来电显示只会显示号码),这个时候编程者就不能一棒子打死,让老设备强行运行新标准。而是让新设备继承老标准,再在老标准的基础上进行拓展,最终形成新的标准。而在这其中,继承变显得尤为重要。
关于继承别样的优点我们暂时就说到这,后期你还会在文章里或者是自己的练习当中体会到继承机制的优点。但是,了解了继承可不能说自己就了解了多态,如果要了解多态,至少还要了解以下几个方面的方面的内容。又或者说,多态必须要在以下的几个条件中,才能发生。
1.首先得在继承环境下(继承我们已经讲解了)
2.子类需要发生向上转型
3.子类需要重写父类的方法(发生方法的重写)
4.通过父类引用,调用子类的重写方法
现在的你肯定不理解,为什么一定需要这几种条件,多态才能够发生。别着急,等我们将2-4小点给你挨个解释清楚,虽然离完全理解多态还差一点,但是当我们在具体讲解多态的时候,你就会明白这些条件的意义所在。
所以本文将聚焦于以下三点:
1. 向上转型是什么?如何发生向上转型?
2. 重写方法定义是什么?如何重写方法?
3. 父类引用如何调用子类的重写方法?
(臭不要脸环节)如果看完了这篇文章,能否给作者一个赞呢?如果给作者一个免费的关注那就再好不过啦~
那么,现在本文开始。
1. 向上转型
1.1 什么是向上转型
在前文我们提到了,子类继承父类,父类的代码优于子类执行。所以,父类的层级高于子类。
而要去理解向上转型,那么,是不是就是将我自己创建的子类当成父类来使用,就是向上转型。答案是肯定的。
那么,如何将子类当成父类来用?也就是向上转型中的关键点,所以,大家要记牢,向上转型,最简单的理解就是,将子类对象当成父类来用。
1.2 实现向上转型的三种方法
条目1.1中提到了,向上转型得将子类当成父类来使用。所以,我们new出来的一定是一个子类对象,而不是父类,如果是同级别类那还转什么。但是,既然我们要满足子类变成父类的要求,至少IDEA得知道这个对象是属于父类的吧。所以,用父类类型接收子类变量,就是发生向上转型的关键。(你会发现,向上转型当中的类型并不匹配,我既然要初始化一个Cat类对象,那么理应就是那Cat类来接收,而不是Animal来接收,但是由于向上转型是实现多态的关键,所以Java默认这是可以的,在继承体系下,出现了父类类型接受子类变量,那么Java就会认为你使用了向上转型。)
那么,发生向上转型的第一种方式就出来了,直接赋值。
具体格式如下。
Animal animal = new Cat("元宝",2);
父类类型 子类对象
我们依旧拿我们最熟悉的Animal类来举例子。
class Animal{String name;int age;public void eat() {System.out.println(this.name + "正在吃饭!");}
}class Cat extends Animal{Cat(String name,int age){this.name = name;this.age = age;}public static void main(String[] args) {//方法1 直接赋值法Animal animal = new Cat("初一",10);animal.eat();}
}
那么,除了直接赋值,还有一种方法,叫做方法传参。
既然我通过直接赋值就能实现,那么,只要是将我的对象传递到类当中,也是可以实现对应的功能的。所以,方法传参在Java当中是可行的(Java当中传递参数的特殊性)。所以,用父类类型来接收子类对象,事实上跟直接赋值没有什么大的区别。
代码如下。
class Cat extends Animal{Cat(String name,int age){this.name = name;this.age = age;}public static void change(Animal animal){System.out.println("类型转换成功");}public static void main(String[] args) {//方法2 方法传参Cat animal1 = new Cat("元宝",2);change(animal1);}
}
那么第三种方法,也跟方法有关,如果,我们将方法的返回值设为子类变量,但是用父类类型接收,也可以发生向上转型。
class Cat extends Animal{Cat(String name,int age){this.name = name;this.age = age;}public static Animal change1(){return new Cat("初二",2);}public static void main(String[] args) {//方法3 返回值Animal animal2 = change1();animal2.eat();}
}
这里的返回值需要好好和大家说道说道。首先,你可以发现我们自己定义的change1方法的返回值Animal,而并非是Cat。其中,如果方法的返回值是Cat类型,那么其实我个人认为是最严谨的一种写法,但是这样代码的局限性会很大,因为如果你将change1的返回值更改为Cat类,那么后续return的值就必须是Cat类的对象,而不能是Animal当中的其他子类。但是,肯定还有人有疑问,为什么change1当中的返回了Cat类IDEA却没有报错。其实这里Java已经知道了你要向上转型了。在方法2-函数传参当中,你会发现形参的类型就是Animal,也就是父类,你会发现无论你传哪一种子类,父类形参都能够接收,IDEA不会报错。那么作为函数返回值也可以,既然是函数的返回值是父类,那么其实也能代表这个函数可以返回父类当中的任意子类类型的变量,但是,这些都发生在继承体系下,如果不是,你看看他给不给加点下划线。
向上转型到这里其实也就差不多了,至于为什么会有向上转型,先别着急,我们再来看看方法的重写。
2.方法的重写
2.1 何为重写
重写想必是大家学生时代最不想听到的东西,因为重写意味着重头再来,但是几千个字的文章你说重写就重写那不是写得腱鞘炎都出来了。为什么要重写,大家可以看看先导内容中关于手机的例子。事实上手机的例子更多的涉及到重写。
但是在Java中,重写很没有那么难。(毕竟用键盘写)
要介绍重写,我们得先看看一段代码。
class Animal{String name;int age;void eat() {System.out.println(this.name + "正在吃饭!");}
}class Cat extends Animal{Cat(String name,int age){this.name = name;this.age = age;}public void eat() {System.out.println(this.name+"正在吃猫粮!");}public static void main(String[] args) {Animal animal = new Cat("初一",10);animal.eat();}
现在我们既在父类里写了eat方法,同时在子类里也写了一个eat方法。我现在在main方法里调用了eat方法,那么现在会调用谁呢?
我们执行代码来看看结果。
代码结果是初一正在吃猫粮。
那么,其实我们在子类里面是不是重新写了一份eat方法,这个重新写eat方法的操作,我们就叫做方法的重写!!!
2.2 方法重写的要求
但是方法的重写,我们有很多的限制,具体限制如下。
1.重写操作只能在子类里进行
2.重写父类的方法时,必须与父类方法原型一致(但是也有例外): 返回值类型 方法名 参数列表 要完全一致 被重写的方法返回值类型可以不同,但是必须是具有父子关系的
3.被重写的方法访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被public修饰,则子类中重写该方法就不能声明为 protected
4.父类被static、private、final修饰的方法均不能被重写,所有构造方法都不能被重写。
重写的方法可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验. 例如不小心将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法, 就会编译报错, 提示无法构成重写.
(有了override注解会极大的增强代码的可读性)
到这里其实重写也讲的差不多了,有一点大家需要注意,那就是动态绑定,这个我们放到第三点讲。
3.通过父类引用调用子类重写方法
调用方法其实很简单,通过对象调用就完了,我们已经知道了向上转型,重写方法,既然我们完成了向上转型和方法的重写,自然调用肯定是不在话下。但是接下来我们需要有一点需要弄清楚,为什么我们调用的会是重写的方法,而不是父类方法,这其中就发生了动态绑定!!!
我来给各位梳理一下这个过程。
首先,虽然我们new的对象是归属于Cat类,但是我们是拿Animal类进行接收的,所以,在IDEA调用方法的时候,我们应该是要用Animal类的方法。
随后,IDEA检测到了向上转型,同时eat方法被重写,此时就需要调用子类的重写的eat方法。这个时候就发生了动态绑定!!!可以理解为,在编译过程中调用重写方法的操作即为动态绑定!!!
那你说有没有静态绑定???有的!在编译之前,通过参数列表就能确定要调用那个方法,这种的就叫做静态绑定。
4.彩蛋环节-浅尝多态
你会发现,到现在我们还是没有讲到多态,我们充其量也就知道了什么是动态绑定而已,接下来的一段代码,会让你知道多态有多爽。
现在,我想画方块,圆形,花朵这三个图案,并希望他们按照一定的顺序输出。这个如何通过代码解决???
如果我们不介绍多态,不了解多态前需要的三个知识,你会这么写代码。
)
public static void drawShapes() {Rect rect = new Rect();Cycle cycle = new Cycle();Flower flower = new Flower();String[] shapes = {"cycle", "rect", "cycle", "rect", "flower"};for (String shape : shapes) {if (shape.equals("cycle")) {cycle.draw();} else if (shape.equals("rect")) {rect.draw();} else if (shape.equals("flower")) {flower.draw();}}}
你会发现这也太难读懂了吧,但是,如果我们从继承开始考虑,我们会这么写。
class Shape {public void draw() {System.out.println("画图形!");}
}
class Rect extends Shape{@Overridepublic void draw() {System.out.println("♦");}
}
class Cycle extends Shape{@Overridepublic void draw() {System.out.println("●");}
}
class Flower extends Shape{@Overridepublic void draw() {System.out.println("❀");}
}class test{public static void drawShapes() {
// 我们创建了一个 Shape 对象的数组.Shape[] shapes = {new Cycle(), new Rect(), new Cycle(),new Rect(), new Flower()};for (Shape shape : shapes) {shape.draw();}}public static void main(String[] args) {drawShapes();}
}
这个代码,就称得上是神来之笔!也是我们能够理解多态的非常好的一串代码。
首先,我们既然要画图形,就得定义我们想要画的图形类,shape作为大类,其余子类分别继承。既然子类是具体的图形,那么Shape类当中的draw方法也得重写,得打印我们需要的图形。
然后,我们在test类当中定义了drawShapes这样的方法来帮助我们打印。
在drawShapes这个方法中,我们先定义了Shape类型的数组(这个很关键),然后成员分别是Shape当中的子类。这一个定义,既规定了打印的次序,同时也发生了向上转型!!!!
然后,通过for-each循环,开始挨个调用draw方法,但是,由于每一个子类都重写了draw方法,所以最后执行的都是子类当中的draw方法!!!这也打印了我们想要的图形。
不难发现,我们传递的对象不同,每个对象都实现了“打印图形”这一个相同的操作,但是打印的图形却不一样,这种去完成某一种特定的行为,不用对象产生不同结果的现象,我们就叫做多态!!!
多态暂时我们就涉及到这里,等到之后我们会单独开专栏,深入理解!!!
那么本篇文章到这里就结束了!
(再一次臭不要脸)如果看完了这篇文章,能否给作者一个赞呢?如果给作者一个免费的关注那就再好不过啦~
Java中的继承 与 多态(中)相关推荐
- C++ 中的继承和多态
C++ 中的继承和多态 一.继承 二.函数重载.隐藏.覆盖.重写 1.函数重载(Function Overload) 2.函数隐藏(Function Hiding) 3.函数重写与函数覆盖(Funct ...
- Python中的继承和多态
本文以生活中的基础现象为切入点,主要介绍了Python基础中继承和多态,包括单继承.多继承的语法.多态常见的 "鸭子类型". 以及如何重写父类的方法都做了详细的讲解. 一.继承的介 ...
- python中的继承与多态_Python3 与 C# 面向对象之~继承与多态
2.继承 ¶ 2.1.单继承 ¶ 在OOP中,当我们定义一个Class的时候,可以从某个现有的Class继承 新的Class称为子类,而被继承的 ...
- C#中的继承与多态还有接口
简单继承 多态 接口 参考 简单继承 最简单的三个类 [csharp] view plaincopy print? public class Animal { public Animal() { De ...
- java中抽象类继承抽象类_Java中的抽象类用示例解释
java中抽象类继承抽象类 Abstract classes are classes declared with abstract. They can be subclassed or extende ...
- Java基础:继承、多态、抽象、接口
第一讲 继承 一.继承概述 1.多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可. 2.通过extends关键字可以实现类与类的 ...
- Java实战之继承与多态
类的继承以及抽象类的定义和使用 任务描述 我们都知道,生活中的继承无处不在,在数学王国的领域也是如此.最近数学王国中一位名叫Shape的父亲有一个烦心事,他有两个儿子Circle和Rectangle, ...
- Java基础篇--继承(inherit),多态(Polymorphism)
Java基础篇--继承(inherit),多态(Polymorphism) 1. 继承概述 1.1 什么是继承 1.2 为什么要使用继承 1.3 继承的特点 1.4 继承的优点 2. 组合设计模式 2 ...
- java封装继承多态的理解_深入理解Java封装、继承、多态
一:封装 将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问,常见的实现方式就是:getter.setter. 封装遵循了"开闭原则&qu ...
最新文章
- ikbc键盘自动打字_键盘按斤卖,一斤一百块?IKBC W200机械键盘简晒
- 《民国枭雄杜月笙》—— 听后总结
- 计划将项目中使用entity framework的要点记录到改栏目下
- Failed to install UTRUST.apk on device 'ZTE_SP920': Too many open files
- 所谓中央空调VRV指的是什么
- GCC的内嵌汇编语法 ATT汇编语言语法
- 在Lotus Notes设置邮件转发
- java sqlserver数据库连接_JAVA连接SQLserver数据库
- The Bits(找规律)
- intouch的报警怎么发到邮件上
- KindEditor实现上传图片与回显
- 【PTA】 统计素数并求和
- Adobe Flash Player 不是最新版本
- 如何操作电脑压缩包解压文件?干货技巧!电脑压缩包怎样进行文件解压?
- RabbitMq报错 Execution of Rabbit message listener failed
- 湿度传感器 DHT11
- Ubuntu 16.04 安装php的拓展yac
- share mouse键盘不能在再另一个屏幕使用
- Java I/O系统
- 详细讲解C语言经典例题:有n个人围成一圈,顺序排号。从第1个人开始报数(从1到3报数),凡报到3的人退出圈子, 问最后留下的是原来第几号的那位
热门文章
- 2022年Redis最新面试题第8篇 - Redis缓存问题
- redis主从复制面试题
- php的uniqid函数,PHP之uniqid()函数用法,phpuniqid函数用法_PHP教程
- C++字符串数组 | 字符串数组输出
- 五险一金后5千的月均工资,在长沙处于什么水平?
- 项目实战:《智慧线上购物商城》:基于vue3+vite+vant4组件(一)
- pandas中的isalnum、isdecimal、isdigit、isnumeric、isalpha
- 怎么把PDF转换成图片?来看看这几个方法吧!
- java中判断数组为空
- Unity_Shader_卡通动画效果