超详细Java基础-多态
茫茫人海千千万万,感谢这一秒你看到这里。希望我的能对你的有所帮助!共勉!
愿你在未来的日子,保持热爱,奔赴山海!
Java基础知识(多态)
多态
多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。
因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
多态的定义和存在的必要条件
多态的定义:
- 多态是指同一个行为具有多个不同表现形式或形态的能力。
- 多态就是同一个接口,使用不同的实例而执行不同操作。
就举动物类的例子吧,cat和dog都是属于动物这一类,而动物呢,都有一个共同的行为就是吃吧,而不同的动物所吃的食物都大不相同吧!
猫呢,它喜欢吃鱼!
而对于狗呢,它就比较喜欢啃骨头!
所以多态就是对于吃这一行为来说,每种动物对吃这一行为所表现的行为都不尽相同。
多态存在的三个必要条件
继承或者实现
在多态中必须存在有继承或者实现关系的子类和父类。
方法的重写
子类对父类中某些方法进行重新定义(重写),在调用这些方法时就会调用子类的方法。
基类引用指向派生类对象,即父类引用指向子类对象
父类类型:指子类对象继承的父类类型,或者实现的父接口类型。
多态的格式:
父类类型 变量名 = new 子类类型();
变量名.方法名();
多态格式可以充分体现了同一个接口,使用不同的实例而执行不同操作。
接下来我们具体来进行案例体会体会吧!
多态的案例
多态我们首先要知道的一点:
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写后方法。如果子类没有重写该方法,就会调用父类的该方法。
总结起来就是:编译看左边,运行看右边。
首先我们先定义一个父类动物类,动物有吃的行为!
接着定义一个猫类和狗类去继承动物类,重写里面的吃行为!
具体代码如下:
定义动物父类:
package com.nz.pojo;
/**
* 先定义一个父类 --> 动物类
* 动物都有一个吃的行为属性
*/
public class Animal {
public void eat() {
System.out.println("动物它们都会吃东西!!!");
}
}
定义猫咪子类:
package com.nz.pojo;
/**
* 定义猫类继承动物类,
* 随之重写里面的吃行为,因为猫也有吃的行为,但是猫喜欢吃罐头
*/
public class Cat extends Animal{
public void eat() {
System.out.println("小喵咪都喜欢吃鱼罐头!");
}
}
定义小狗子类:
package com.nz.pojo;
/**
* 定义狗类继承动物类,
* 随之重写里面的吃行为,因为狗也有吃的行为,但是狗喜欢啃骨头
*/
public class Dog extends Animal{
public void eat() {
System.out.println("小狗狗都爱啃骨头!");
}
}
定义测试类,测试多态的形式:
package com.nz;
import com.nz.pojo.Animal;
import com.nz.pojo.Cat;
import com.nz.pojo.Dog;
/**
* 测试多态的形式
*/
public class Demo {
public static void main(String[] args) {
// 多态形式,创建猫类对象
Animal animal = new Cat();
// 调用的是Cat的 eat
animal.eat();
// 多态形式,创建狗类对象
Animal animal2 = new Dog();
// 调用的是Dog的eat
animal2.eat();
}
}
得到的结果:
小喵咪都喜欢吃鱼罐头!
小狗狗都爱啃骨头!
类的大致结构:
可以看出我们可以使用多态的属性得到不同的动物的一个吃的行为属性!
多态的好处
提高了代码的拓展性,使用父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用。
具体我们来看看吧:
继续使用上述的动物类、猫类、狗类吧。
定义测试类:
package com.nz;
import com.nz.pojo.Animal;
import com.nz.pojo.Cat;
import com.nz.pojo.Dog;
/**
* 测试多态的好处
*/
public class Demo2 {
public static void main(String[] args) {
// 创建猫和狗对象
Cat cat = new Cat();
Dog dog = new Dog();
// 调用catEat
catEat(cat);
// 调用dogEat
dogEat(dog);
/*
多态的好处:
以上各个动物的吃的方法, 我们都可以使用animalEat(Animal a)方法来代替。
并且执行效果都一样, 所以我们可以使用animalEat直接替代了不同动物的吃方法。
*/
animalEat(cat);
animalEat(dog);
}
/*
定义几个不同吃的方法,看看具体调用后的结果是什么吧!
*/
public static void catEat (Cat cat){
cat.eat();
}
public static void dogEat (Dog dog){
dog.eat();
}
public static void animalEat (Animal animal){
animal.eat();
}
}
执行结果:
小喵咪都喜欢吃鱼罐头!
小狗狗都爱啃骨头!
小喵咪都喜欢吃鱼罐头!
小狗狗都爱啃骨头!
可以看出,由于多态的特性,我们的animalEat()方法传入的Animal类型参数,并且它是我们的Cat和Dog的父类类型,父类类型接收子类对象,所以我们可以将Cat对象和Dog对象,传递给animalEat()方法。
所以我们可以完全使用animalEat()方法来替代catEat()方法和dogEat()方法,达到同样的效果!以至于我们可以不必再单独写xxxEat()方法来传入指定的动物参数了,从而实现了实现类的自动切换。
所以多态的好处体现在:可以使我们的程序编写的更简单,并有良好的扩展性。
多态的弊端
从上面的多态的好处,可以看到我们可以使用父类的参数代替了某个子类的参数,从而达到程序的扩展!
但是对于某个子类有些独有的功能方法时,此时我们的多态的写法就无法访问子类独有功能了。
具体来瞧瞧?
代码如下:
重新定义下猫的子类:
package com.nz.pojo;
/**
* 定义猫类继承动物类,
* 随之重写里面的吃行为,因为猫也有吃的行为,但是猫喜欢吃罐头
*/
public class Cat extends Animal{
public void eat() {
System.out.println("小喵咪都喜欢吃鱼罐头!");
}
/**
* 增加一哥猫咪特有的玩球方法()
*/
public void playBall() {
System.out.println("小喵咪都喜欢小球!");
}
}
定义测试类:
package com.nz;
import com.nz.pojo.Animal;
import com.nz.pojo.Cat;
/**
* 测试多态的弊端!
*/
public class Demo3 {
public static void main(String[] args) {
Animal animal = new Cat();
animal.eat();
animal.playBall();//编译报错,编译看左边,Animal没有这个方法
}
}
可以看到动物类和猫类有个共同的eat吃方法,但是呢,猫咪多了个玩球球的方法。而对于动物对象来说,它本身动物类没有玩球球的方法,所以它的编译就直接没有通过了!
那有什么方法解决呢?且看下一章节吧!
引用类型转换
1. 引用类型转换是什么,为什么需要它?
从上面的多态的弊端的案例中,我们可以看到,我们使用动物对象时无法直接访问到猫类中的玩球球方法,这也就是我们之前说的编译看左边,运行看右边。
而在我们使用多态方式调用方法时,首先检查会左边的父类中是否有该方法,如果没有,则编译错误。也就代表着,父类无法调用子类独有的方法。、
所以说,如果编译都错误,更别说运行了。这也是多态给我们带来的一点小困扰,而我们如果想要调用子类特有的方法,必须做向下转型。
2. 向上转型(自动转换)
对于向下转型,我们先来讲解下向上转型的概念吧。
向上转型:
多态本身是子类向父类向上转换(自动转换)的过程,这个过程是默认的。当父类引用指向一个子类对象时,便是向上转型。
对于父类和子类的关系来说,具体来看图说话:
父类相对与子类来说是大范围的类型,Animal是动物类,是父类。而Cat是猫咪类,是子类。
所以对于父类Animal来说,它的范围是比较大的,它包含一切动物,包括猫咪类和小狗类。
所以对于子类类型这种范围小的,我们可以直接自动转型给父类类型的变量。
使用格式:
父类类型 变量名 = new 子类类型();
如:Animal animal = new Cat();
相当于有:
Animal animal = (Animal) new Cat();
相当于自动帮我们了一个隐形的转换为动物类的一个过程,因为动物本身就包含了猫咪。
3. 向下转型(强制转换)
向上转型可以知道它是子类自动转换为父类的一个过程,所以我们现在再来看看向下转型的定义:
向下转型:
向下转型就是由父类向子类向下转换的过程,这个过程是强制的。一个需要将父类对象转为子类对象,可以使用强制类型转换的格式,这便是向下转型。
为什么这种就必须自己强制加上一个类型转换过程呢?
对于父类和子类的关系来说,我们接着看图说话:
对于猫咪类的话,它在动物类中只是其中的一部分吧,而对于动物类来说,它有许多其他子类动物如狗,牛,猪等等。
所以对于动物父类想要向下转型的时候, 它此时不知道指向那个子类,因为不确定呀,所以就必须自己加上强制的类型转换的一个过程。
使用格式:
子类类型 变量名 = (子类类型) 父类变量名;
如:
Animal animal = new Cat();
Cat cat = (Cat) animal;
cat.playBall();// 此时我们就可以使用猫咪的特有方法啦
所以对于多态的弊端,无法使用子类特有的参数,我们也解决啦,可以通过向下转型的方法,从而将类型强制转换为某个子类对象后,再去调用子类的特有方法!
4. 向下转型的问题
虽然我们可以使用向下转型使得我们可以使用子类的独有方法,但是转型的过程中,一不小心就会遇到这样的问题了,来,我们来看看下面的代码:
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse 【运行报错】
}
}
这段代码可以通过编译,但是运行时,却报出了 ClassCastException
,类型转换异常!这是因为,明明创建了Cat类型对象,运行时,当然不能转换成Dog对象的。
5. 转型的异常
转型的过程中,一不小心就会遇到这样的问题,请看如下代码:
定义狗类中额外的独有遛狗方法:
package com.nz.pojo;
/**
* 定义狗类继承动物类,
* 随之重写里面的吃行为,因为狗也有吃的行为,但是狗喜欢啃骨头
*/
public class Dog extends Animal{
public void eat() {
System.out.println("小狗狗都爱啃骨头!");
}
public void walk() {
System.out.println("小狗在被我溜着!");
}
}
定义测试类
package com.nz;
import com.nz.pojo.Animal;
import com.nz.pojo.Cat;
import com.nz.pojo.Dog;
/**
* 测试多态的向下转型的问题
*/
public class Demo4 {
public static void main(String[] args) {
// 向上转型的过程
Animal animal = new Cat();
// 调用了猫咪的吃方法
animal.eat();
// 向下转型
Dog dog = (Dog) animal;
dog.walk(); // 调用的是 Dog 的 walk 可以通过,但是会运行报错
}
}
得到结果:
小喵咪都喜欢吃鱼罐头!
Exception in thread "main" java.lang.ClassCastException: com.nz.pojo.Cat cannot be cast to com.nz.pojo.Dog
at com.nz.Demo4.main(Demo4.java:20)
我们可以看到,虽然我们的代码通过编译,但是终究在运行时,还是出错了,抛出了 ClassCastException
类型转换的异常。
其实我们可以知道,我们在上面的时候,创建了Cat类型对象,而在向下转型时,将其强行转换为了Dog类型,所以程序在运行时,就会抛出类型转换的异常!
那我们如何可以避免这种异常发生呢?且看下一节分析!
6. instanceof关键字
Java为我们提供一个关键字instanceof
,它可以帮助我们避免了ClassCastException
类型转换异常的发生。
那如何做呢?
格式:
变量名 instanceof 数据类型
解释:
- 如果变量属于该数据类型或者其子类类型,返回true。
- 如果变量不属于该数据类型或者其子类类型,返回false。
代码实现:
package com.nz;
import com.nz.pojo.Animal;
import com.nz.pojo.Cat;
import com.nz.pojo.Dog;
/**
* 使用instanceof解决类型转换异常!
*/
public class Demo5 {
public static void main(String[] args) {
// 向上转型的过程
Animal animal = new Cat();
// 调用了猫咪的吃方法
animal.eat();
// 向下转型
if (animal instanceof Cat){
Cat cat = (Cat) animal;
cat.playBall(); // 调用的是 Cat 的 playBall
} else if (animal instanceof Dog){
Dog dog = (Dog) animal;
dog.walk(); // 调用的是 Dog 的 walk
}
}
}
结果:
小喵咪都喜欢吃鱼罐头!
小喵咪都喜欢小球!
可以发现,它可以帮助我们在做类型转换前,判断该类型是否属于该类型或者子类类型,如果是,我们就可以强转啦!
总结
相信各位看官都对Java中的特性之一多态的知识和使用有了一定了解,等待下一次更多Java基础的学习吧!
学到这里,今天的世界打烊了,晚安!虽然这篇文章完结了,但是我还在,永不完结。我会努力保持写文章。来日方长,何惧车遥马慢!
感谢各位看到这里!愿你韶华不负,青春无悔!
注: 如果文章有任何错误和建议,请各位大佬尽情评论留言!如果这篇文章对你也有所帮助,希望可爱亲切的您给个关注点赞收藏下,非常感谢啦!
超详细Java基础-多态的更多相关文章
- 一看你就懂,超详细java中的ClassLoader详解
本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 ClassLoader翻译过来就是类加载器,普通的Java开发者其实用到的不多,但对于某些框架开发者来说却非常常见.理解ClassL ...
- 【转】一看你就懂,超详细java中的ClassLoader详解
http://blog.csdn.net/briblue/article/details/54973413 ClassLoader翻译过来就是类加载器,普通的java开发者其实用到的不多,但对于某些框 ...
- 一看你就懂,超详细java中的ClassLoader详解(转)
转载地址 http://blog.csdn.net/briblue/article/details/54973413 目录(?)[-] Class文件的认识 你还记得java环境变量吗 J ...
- (转)超详细java中的ClassLoader详解
转:https://blog.csdn.net/briblue/article/details/54973413 ClassLoader翻译过来就是类加载器,普通的java开发者其实用到的不多,但对于 ...
- JAVA基础-多态
多态 polymophism: 动态绑定, 迟绑定, 指在执行期间(java), 而不是编译期间(javac), 判断所引用对象的实际类型, 根据实际类型调用响应的方法. 3个条件: 1. 继承 2. ...
- Java基础——多态
多态性是指允许不同类型的对象对同一消息做出相应.具有灵活性.抽象.行为共享.代码共享的优势,共享就意味着最大化利用和简洁,还有就是加载速度. 一.多态的作用 消除类型之间的耦合关系.即同一事件发生在不 ...
- Java基础---多态、内部类、异常、包
第一讲 多态 多态可以理解为事物存在的多种体现形态. 例:动物中猫,狗.猫这个对象对应的类型是猫类型,如:猫 x = new猫(); 同时猫也是动物中的一种,也可以把猫称为动物.动物 y = ...
- 超详细“零”基础kafka入门篇
1.认识kafka 1.1 kafka简介 Kafka 是一个分布式流媒体平台 kafka官网:http://kafka.apache.org/ (1)流媒体平台有三个关键功能: 发布和订阅记录流,类 ...
- Java基础—多态
1.什么是多态 多态是同一个行为具有多个不同表现形式或形态的能力. 多态性是对象多种表现形式的体现. 现实中,比如我们按下 F1 键这个动作: 如果当前在 Flash 界面下弹出的就是 AS 3 的帮 ...
随机推荐
- flex mx:TabNavigator进行选项卡切换,需要进行交互时。发生Error #1009错误
当需要进行 mx:TabNavigator选项卡进行切换时,需要进行交互,然后却报了"TypeError: Error #1009: 无法访问空对象引用的属性或方法."错误,产生这 ...
- Linux:VMware配置NAT网络IP
设置虚拟机网络配置 在目标虚拟机下右键, 选择"设置", 打开"虚拟机设置"对话框, 再选择"网络适配器"使用NAT模式的, 如下图所示: ...
- Android java层常见加密算法的hook自吐以及栈信息的打印
杂谈:其实原理并没有很难,本质就是hook Android的框架层中的api将我们想要的key和iv(也可以没有,就打个比方),但是目前的话,很多厂家已经不在直接调用java层的这些加密算法的api了 ...
- TCP/UDP/HTTP的区别和联系(转载)
一.TPC/IP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据. 关于TCP/IP和HTTP协议的关系,网络有一段比较容易理解的介绍:"我们在传 ...
- QT. 学习之路 一
初识QT 一: hello-world: #include "mainwindow.h" #include <QApplication> #include < ...
- 「CF576D」 Flights for Regular Customers
「CF576D」 Flights for Regular Customers 对不起我又想网络流去了 你看这长得多像啊,走过至少多少条边就是流量下界,然后没上界 但是这个题求的最少走多少条边啊...完 ...
- 个人博客开发之blog-api 项目全局日志拦截记录
前言 大型完善项目中肯定是需要一个全局日志拦截,记录每次接口访问相关信息,包括: 访问ip,访问设备,请求参数,响应结果,响应时间,开始请求时间,访问接口描述,访问的用户,接口地址,请求类型,便于项目 ...
- IDEA工具-自动导包去除星号(import xx.xx.*)
打开设置>Editor>Code Style>Java>Scheme Default>Imports 设置导入类数值阈值,默认同包类是超过5个变成*,静态导入超过3个变成 ...
- 关于Xpath定位方法知道这些基本够用
一.写在前面 之前写过一些关于元素定位的文章,但是感觉都是很碎片,现在想做个整合,便有了这篇文章. 二.xpath的定位方法 关于xpath定位方法,网上写的已经很成熟了,现已百度首页为例,如下图: ...
- Redis学习——常用小功能
一.慢查询分析(查询日志:所谓慢查询日志就是系统在命令执行前后计算每条命令的执行时间,当超过预设阀值,就将这条命令的相关信息(例如:发生时间,耗时,命令的详细信息)记录下来,Redis也提供了类似的功 ...