菜鸡的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 ...
随机推荐
- Markdown 编写技巧汇总(一)
编写文档,有很多格式选择,也有不同平台选择.下面就自己接触到的MarkDown编写文档的各种技巧做简单梳理,供自己参阅,也希望帮到网友. [1]添加空格 ① 这种写法比较老土,但是,很实用!注意都 ...
- vue-混入( mixin 更方便的组件功能复用方法)的使用
前言 vue 中组件完成了样式和功能的综合复用,通过自定义指令完成了一部分功能的复用,本文总结一下混入在vue项目开发中提供的非常便利的功能复用. 正文 1.混入的分类 (1)全局混入 <div ...
- SpringCloud升级之路2020.0.x版-25.OpenFeign简介与使用
本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent OpenFeign 的由来和实现思路 在微服务系统中,我们经常会进行 RPC 调用.在 S ...
- Django整理(五) - 请求与响应 - request对象
请求对象 一.客户端传参的几种方式 1. 通过URL路径(path)传递,例如:http://127.0.0.1:8000/news/1/2,两个参数:id和page 2. 通过 query stri ...
- mysql数据备份及恢复详细操作
一.数据库数据备份 1.全备 BakDir=/backup/full #创建全备目录 LogFile=/backup/full/bak.log #创建备份日志 Date=`date +%Y%m%d` ...
- hexo访问优化之--------gulp压缩
hexo访问优化之--------gulp压缩 hexo生成的博客是静态html页面,当有很多静态资源时,加载速度会非常慢,且github服务器在国外,导致网页加载速度非常差 gulp压缩 gulp是 ...
- WSL (Windows Subsystem for Linux)
WSL (Windows Subsystem for Linux) :适用于 Linux 的 Windows 子系统. References Install WSL with a single com ...
- Kubernetes client-go 源码分析 - Reflector
概述入口 - Reflector.Run()核心 - Reflector.ListAndWatch()Reflector.watchHandler()NewReflector()小结 概述 源码版本: ...
- 【数据结构】c语言实现集合的交并差运算
待改写:存储数据类型int-->char 重复的元素可存储 功能上不完善 #include <stdio.h> #include <stdlib.h> typedef s ...
- STM32串口USART的使用方法和程序
通用同步异步收发器(USART)提供了一种灵活的方法来与使用工业标准NR 异步串行数据格式的外部设备之间进行全双工数据交换. USART利用分数波特率发生器提供宽范围的波特率选择,支持同步单向通信和半 ...