菜鸡的Java笔记 第二十二 - java 对象多态性
本次只是围绕着多态性的概念来进行讲解,但是所讲解的代码与实际的开发几乎没有关系,而且多态一定是在继承性的基础上才可以操作的,
而本次将使用类继承的关系来描述多态的性质,实际的开发中不会出现普通类的继承关系(一个已经完善的类不应该再被继承),开发中都要求继承抽象类和接口
多态性要想实现有两个前提:继承,覆写
范例:引出代码
class A {
public void print(){
System.out.println("hello");
}
}
class B extends A{
public void print(){
System.out.println("你好!");
}
}
public class polymorphism{
public static void main(String args[]){
B b = new B();
b.print();
}
}
覆写调用的前提:看new 的是那个类的对象,而后看方法是否被子类所覆写
在java中多态性主要由两个方面组成:
方法的多态性:
方法重载:方法调用时根据不同的参数个数及类型可以实现不同的功能
方法覆写:不同的子类针对于同样的一个方法可以有不同的实现
对象的多态性:父类与子类对象间的转换操作;发生在继承关系之中
【自动】对象的向上转型:子类对象变为父类对象 父类 父类对象 = 子类实例,自动完成的
【强制】对象的向下转型;父类对象变为子类对象 子类 子类对象 = (子类)父类实例,强制转换
除了转型之外,还有一些操作是不转型的,例如:String
对象多态性基础实现
观察一道程序
范例:
class Member{
public String getInfo(){
return "Member:我是一个会员";
}
}
class VIPMember extends Member{ // 产生继承
public String getInfo(){
return "VIPMember:我是一个贵宾会员";
}
}
public class polymorphism{
public static void main(String args[]){
VIPMember mam = new VIPMember();
System.out.println(mem.getInfo());
}
}
方法覆写观察:
观察现在 new 的是那个子类
观察这个子类调用的方法是否被覆写,如果被覆写了则调用的就是被覆写过的方法
范例:对象的向上转型
class A {
public void print(){
System.out.println("hello");
}
}
class B extends A{
public void print(){
System.out.println("你好!");
}
}
public class polymorphism{
public static void main(String args[]){
A a = new B(); // 向上转型
a.print();
}
}
可以发现向上转型是自动完成的,除了向上转型之外,也可以实现对象的向下转型操作
向上转型最大的特点在于:所有的子类对象按照统一的父类类型进行接收,但是由于实例化子类的不同,有可能同一个父类的方法会有不同的调用实现
为了更好的理解对象的向上转型问题就做一个简单的分析:要求设计一个方法,这个方法可以接收用户的参数信息
如果这个时候采用原始的技术:方法重载
范例:
class Member{
public String getInfo(){
return "Member:我是一个会员";
}
}
class VIPMember extends Member{ // 产生继承
public String getInfo(){
return "VIPMember:我是一个贵宾会员";
}
}
class CIPMember extends Member{ // 产生继承
public String getInfo(){
return "CIPMember:我是一个商务会员";
}
}
public class polymorphism{
public static void main(String args[]){
income(new Member());
income(new VIPMember());
income(new CIPMember());
}
public static void income(Member mem){
System.out.println(mem.getInfo());
}
public static void income(VIPMember mem){
System.out.println(mem.getInfo());
}
public static void income(CIPMember mem){
System.out.println(mem.getInfo());
}
}
缺点:
如果突然有一天你的用户类型分为了十万种,那么就需要有十万个 Member 子类,这个方法会重载十万次
所有的方法体执行的功能都一样,那么这样是一个明显重复
当子类众多又需要考虑参数统一的时候,按恶魔最好用的做法就是进行对象的向上转型(自动,调用被覆写过的方法)
class Member{
public String getInfo(){
return "Member:我是一个会员";
}
}
class VIPMember extends Member{ // 产生继承
public String getInfo(){
return "VIPMember:我是一个贵宾会员";
}
}
class CIPMember extends Member{ // 产生继承
public String getInfo(){
return "CIPMember:我是一个商务会员";
}
}
public class polymorphism{
public static void main(String args[]){
income(new Member());
income(new VIPMember());
income(new CIPMember());
}
public static void income(Member mem){
System.out.println(mem.getInfo());
} }
发现利用对象的向上转型可以有效的实现参数的统一处理。这一点在程序中至关重要
对象向下转型
范例:对象的向下转型
class A {
public void print(){
System.out.println("hello");
}
}
class B extends A{
public void print(){
System.out.println("你好!");
}
}
public class polymorphism{
public static void main(String args[]){
A a = new B(); // 向上转型
B b = (B)a; // 向下转型
b.print();
}
}
具体的转型的概念没有什么难理解,那么程序的执行结果也很好理解,但是这样做有什么意义呢?
分析:向上转型的意义
现在要求定义一个 fun()方法,这个方法要求可以接收A以及A类所有子类的实例化对象
于是根据这样的描述可以有两种实现方案
方案一:使用方法重载的概念来完成
class A {
public void print(){
System.out.println("hello");
}
}
class B extends A{
public void print(){
System.out.println("你好!");
}
}
class C extends A{
public void print(){
System.out.println("不好!");
}
}
public class polymorphism{
public static void main(String args[]){
fun(new A());
fun(new B());
fun(new C());
}
public static void fun(A a){
a.print();
}
public static void fun(B b){
b.print();
}
public static void fun(C c){
c.print();
}
}
/*
结果:
Hello
你好!
不好
*/
如果说这个时候A类有3000W个子类,并且这个子类还在以倍数的方式增长
那么就表示:在设计之初此方法就需要重载3000W次,并且随着子类的追加,此方法继续重载,继续不断的修改类,于是你就疯了......
方案二:发现所有的子类调用的方法实际上都只是println()一个,那么现在可以利用对象自动向上转型的概念就直接使用A类接收
class A {
public void print(){
System.out.println("hello");
}
}
class B extends A{
public void print(){
System.out.println("你好!");
}
}
class C extends A{
public void print(){
System.out.println("不好!");
}
}
public class polymorphism{
public static void main(String args[]){
fun(new A());
fun(new B());
fun(new C());
}
public static void fun(A a){
a.print();
}
}
/*
结果:
Hello
你好!
不好
*/
所以对象的向上转型给开发者最大的帮助在于其数据操作的统一性上
分析:向下转型的意义
范例:观察问题
class A {
public void print(){
System.out.println("hello");
}
}
class B extends A{
public void print(){
System.out.println("你好!");
}
public void funB(){ // 子类自己扩充的新方法
System.out.println("********************");
}
}
public class polymorphism{
public static void main(String args[]){
A a = new B(); // 向上转型
a.print();
a.funB();// 不能够调用
}
}
//结果:出错
一旦发生了向上转型之后,父类对象是不可能调用子类中新建的方法的,只能够调用父类自己本身所定义的方法名称,也就是说向上转型之后牺牲的是子类的个性化特征
但是如果说现在要想调用子类定义的特殊方法,那么就必须采用向下转型
class A {
public void print(){
System.out.println("hello");
}
}
class B extends A{
public void print(){
System.out.println("你好!");
}
public void funB(){ // 子类自己扩充的新方法
System.out.println("********************");
}
}
public class polymorphism{
public static void main(String args[]){
A a = new B(); // 向上转型 (把A改为B??)
a.print();
B b = (B)a;
b.funB();
}
}
/*
结果:
你好!
******************
*/
解释:为什么现在你的代码里面需要先向上转型,在进行向下转型,啰嗦
class A {
public void print(){
System.out.println("hello");
}
}
class B extends A{
public void print(){
System.out.println("你好!");
}
public void funB(){ // 子类自己扩充的新方法
System.out.println("********************");
}
}
class C extends A{
public void print(){
System.out.println("不好!");
}
public void funC(){ // 子类自己扩充的新方法
System.out.println("###################");
}
}
public class polymorphism{
public static void main(String args[]){
fun(new B());
fun(new C());
}
public static void fun(A a){
a.print()
// 由于某种特殊需求必须调用B类中的funB()方法,所以要进行向下转型
B b = (B)a;
b.funB();
}
}
/*
结果:
你好!
不好
*/
现在如果使用了向下转型,那么在之前好不容易建立起来的参数统一的局面就被打破了,所以这样的操作就属于不合理的操作形式
但是转型的意义却可以更加明确了:为了调用子类自己的特殊支持
但是在进行对象向下转型前也需要有一个注意点:
范例:错误的向下转型
class A {
public void print(){
System.out.println("hello");
}
}
class B extends A{
public void print(){
System.out.println("你好!");
}
public void funB(){ // 子类自己扩充的新方法
System.out.println("********************");
}
}
public class polymorphism{
public static void main(String args[]){
A a == new A(); // 父类对象
B b = (B)a;// 强制转换
b.print()
}
}
//结果:出错
此时出现了“java.langClassException”异常信息,,表示的是类转换异常,本质指的是两个没有关系的类对象发生了强制转换所带来的问题
所以要想进行向下转型操作之前一定要首先保证发生了向上转型,这样才可以建立父子对象的关系
但是可以发现这样的转型本身是会存在有安全隐患的,所以正在java中提供有一个关键字:instanceof,利用此关键字可以判断某一个对象是否是指定类的实例
对象 instanceof 类 返回boolean类型
范例:观察instanceof关键字的使用
class A {
public void print(){
System.out.println("hello");
}
}
class B extends A{
public void print(){
System.out.println("你好!");
}
public void funB(){ // 子类自己扩充的新方法
System.out.println("********************");
}
}
public class polymorphism{
public static void main(String args[]){
A a == new A(); // 父类对象
System.out.println(a instanceof A);
System.out.println(a instanceof B);
//B b = (B)a;// 强制转换
//b.print()
}
}
/*
结果:
true
false
*/
范例:如果发生了向上转型之后的判断
class A {
public void print(){
System.out.println("hello");
}
}
class B extends A{
public void print(){
System.out.println("你好!");
}
public void funB(){ // 子类自己扩充的新方法
System.out.println("********************");
}
}
public class polymorphism{
public static void main(String args[]){
A a == new B(); // 父类对象
System.out.println(a instanceof A);
System.out.println(a instanceof B);
//B b = (B)a;// 强制转换
//b.print()
}
}
/*
结果:
true
true
*/
范例:利用 instanceof 保证转型的正确性
class A {
public void print(){
System.out.println("hello");
}
}
class B extends A{
public void print(){
System.out.println("你好!");
}
public void funB(){ // 子类自己扩充的新方法
System.out.println("********************");
}
}
public class polymorphism{
public static void main(String args[]){
A a == new B(); // 父类对象
System.out.println(a instanceof A);
System.out.println(a instanceof B);
if(a instanceof B){
B b = (B)a;// 强制转换
b.print()
}
}
}
/*
结果:
true
true
你好
*/
总结
向上转型(90%):为了实现参数类型的统一,但是向上转型一定要与方法覆写产生关联
向上转型(1%):为了调用子类特殊的方法实现,但是向下转型前必须要首先发生向上转型,会存在操作的安全性隐患,可以使用 instanceof 进行判断,但是不推荐这样使用
不转型(9%):为了方便操作直接使用系统类或者是一些功能类,例如String 简单java类
菜鸡的Java笔记 第二十二 - java 对象多态性的更多相关文章
- 菜鸡的Java笔记 第二十八 - java 包的定义
包的主要作用以及定义 包的导入操作 系统常见的开发包 jar 程序命令 包的定义 在任何的操作系统之中都有一个统一的共识:同一个目录下不能够存在有相同的文 ...
- 菜鸡的Java笔记 第二十九 - java 单例设计模式
SingleCase 单例设计模式 1.单例设计模式的特点 2.多例设计模式的特点 内容 单例设计模式 现在如果说有这么一个程序类 class S ...
- 菜鸡的Java笔记 第二十六 - java 内部类
/* innerClass 从实际的开发来看,真正写到内部类的时候是在很久以后了,短期内如果是自己编写代码,几乎是见不到内部类出现的 讲解它的目的第一个是为了解释概念 ...
- 菜鸡的Java笔记 第二十四 - java 接口的基本定义
1.接口的基本定义以及使用形式 2.与接口有关的设计模式的初步认识 3.接口与抽象类的区别 接口与抽象类相比,接口的使用几率是最高的,所有的 ...
- 菜鸡的Java笔记 第十二 - java 构造方法与匿名对象
1.点 构造方法的作用以及定义要求 匿名对象的使用 构造方法: 只要出现()的都表示方法 构造方法就是类构造对象时调用的方法,主要用来实例化对象.> ...
- “全栈2019”Java多线程第二十二章:饥饿线程(Starvation)详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- “全栈2019”Java异常第二十二章:try-with-resources语句详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java异 ...
- 菜鸡的Java笔记 第二十五 wrapperClass 包装类
wrapperClass 包装类 1.包装类的特点 2.装箱与拆箱操作 3.数据转型处理 内容 Object 类可以接收 ...
- 菜鸡的Java笔记 第二十 - java 方法的覆写
1.方法的覆写 当子类定义了与父类中的完全一样的方法时(方法名称,参数类型以及个数,返回值类型)这样的操作就称为方法的覆写 范例:观察方法的覆写 class A{ public void ...
随机推荐
- C# 显示、隐藏窗口对应的任务栏
WPF中全屏窗口,会自动隐藏任务栏. 那非全屏窗口如何隐藏任务栏?甚至有没有一种场景,隐藏任务后自定义一套系统任务栏来显示? 以下会分阶段讲述一些概念 1. 主屏任务栏 任务栏,其实也是一个窗口,主屏 ...
- Java - 你的 Java 代码有这些坏味道吗?
列举一些 Java 开发中常见的"不良实践",来源于代码扫描(https://github.com/pmd/pmd),和诸君一起学习参考: 1 - 关闭资源 CloseResour ...
- Billu_b0x内网渗透-vulnhub
个人博客:点我 本次来试玩一下vulnhub上的Billu_b0x,只有一个flag,下载地址. 下载下来后是 .ova 格式,建议使用vitualbox进行搭建,vmware可能存在兼容性问题.靶场 ...
- flask 之 请求钩子
请求钩子 什么是请求钩子? 在客户端和服务器交互的过程中,有些准备工作或扫尾工作需要统一处理,为了让每个视图函数避免编写重复功能的代码,flask提供了统一的接口可以添加这些处理函数,即请求钩子. f ...
- ZK(ZooKeeper)分布式锁实现
点赞再看,养成习惯,微信搜索[牧小农]关注我获取更多资讯,风里雨里,小农等你. 本文中案例都会在上传到git上,请放心浏览 git地址:https://github.com/muxiaonong/Zo ...
- DM8数据库单机安装
一.系统概要 表1 部署情况一览表 操作系统 Windows10 数据库版本 DM8(开发版) 数据库类型 单机 磁盘挂载 无 Key信息 无 二.操作系统信息检查 2.1 操作系统版本 [root@ ...
- Noip模拟35 2021.8.10
考试题目变成四道了,貌似确实根本改不完... 不过给了两个小时颓废时间确实很爽(芜湖--) 但是前几天三道题改着不是很费劲的时候为什么不给放松时间, 非要在改不完题的时候颓?? 算了算了不碎碎念了.. ...
- 零基础要怎么样学习嵌入式Linux--走进嵌入式
零基础要怎么样学习嵌入式希望可以通过这一篇帖子让大家走进嵌入式,对嵌入式的学习不再那么陌生. 嵌入式Linux工程师的学习需要具备一定的C语言基础,因此面对许多朋友只是在大一或者大二学习过C(还不一定 ...
- SVN查看项目修改记录及修改内容
工具/原料 svn 一,查看修改记录 1 选择要查看的文件夹,打开之后在空白的地方右键. 2 选择svn里面的"查看日志".show_Log 3 在弹出的日志框里,可以看到,你可以 ...
- MySQL报错汇总[10/29更新]