我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Java编程 多态

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Java编程 多态

前言:

封装,是合并属性和行为创建一种新的数据类型,继承是建立数据类型之间的某种关系(is-a),而多态就是这种关系在实际场景的运用。

多态就是把做什么和怎么做分开了;其中,做什么是指调用的哪个方法[play 乐器],怎么做是指实现方案[使用A乐器 使用B乐器],''分开了''指两件事不在同一时间确定。

一、向上转型

对象既可以作为它本身的类型使用,也可以作为它的基类型使用,而这种把对某个对象的引用视为对其基类型的引用的做法就是向上转型。

example:


public enum Note {
 // 演奏乐符
    MIDDLE_C, C_SHARP, B_FLAT;

}
public class Instrument {

 // 乐器基类
    public void play(Note n) {
        print("Instrument.play()");
    }

}
public class Wind extends Instrument{

 // Wind是一个具体的乐器
    // Redefine interface method:
    public void play(Note n) {
        System.out.println("Wind.play() " + n);
    }

}
public class Music {

 // 乐器进行演奏
    public static void tune(Instrument i) {
        i.play(Note.MIDDLE_C);
    }

    public static void main(String[] args) {
        Wind flute = new Wind();
        tune(flute); // 向上转型
    }
}

好处:

在上面例子中,如果让tune方法接受一个Wind引用作为自己的参数,似乎看起来更为直观,但是会引发一个问题:这个时候你就需要为系统中Instrument的每种类型都编写一个新的tune方法。所以我们只写一个简单的方法,仅仅接收基类作为参数,而不是特殊的导出类,这么做情况不是变得更好吗。

example:


class Stringed extends Instrument {
    public void play(Note n) {
        print("Stringed.play() " + n);
    }
}


class Brass extends Instrument {
    public void play(Note n) {
        print("Brass.play() " + n);
    }
}


public class Music2 {
    public static void tune(Wind i) {
        i.play(Note.MIDDLE_C);
    }

    public static void tune(Stringed i) {
        i.play(Note.MIDDLE_C);
    }


    public static void tune(Brass i) {
        i.play(Note.MIDDLE_C);

    }
    public static void main(String[] args) {
        Wind flute = new Wind();
        Stringed violin = new Stringed();
        Brass frenchHorn = new Brass();
        tune(flute); // No upcasting
        tune(violin);
        tune(frenchHorn);
    }
}

二、转机


public static void tune(Instrument i) {
    // ...
    i.play(Note.MIDDLE_C);

}

在上面这个方法中,它接收一个Instrument引用,那么在这种情况下,编译器怎么样才能知道这个instrument引用指向的是Wind对象呢? ——通过后期绑定

1、绑定

将一个方法调用同一个方法主体关联起来称为绑定。

在程序执行前进行绑定,就是前期绑定,比如C语言就只有一种方法调用,就是前期绑定。

在运行时根据对象的类型进行绑定就是后期绑定,也叫做动态绑定或者运行时绑定。

Java中除了static方法和final方法之外,其它所有方法都是后期绑定,这意味着通常情况下,我们不必判定是否应该进行后期绑定——它会自动发生。

2、扩展性

由于有多态机制,所以可根据自己的需要向系统里加入任意多的新类型,同时毋需更改 true()方法。在一个设计良好的 OOP 程序中,我们的大多数或者所有方法都会遵从 tune()的模型,而且只与基础类接口通信。我们说这样的程序具有“扩展性”,因为可以从通用的基础类继承新的数据类型,从而新添一些功能。如果是为了适应新类的要求,那么对基础类接口进行操纵的方法根本不需要改变,
对于乐器例子,假设我们在基础类里加入更多的方法[what/adjust],以及一系列新类[Woodwind/Brass],

例子:


class Instrument {
    void play(Note n) { print("Instrument.play() " + n); }
    String what() { return "Instrument"; }
    void adjust() { print("Adjusting Instrument"); }
}


class Wind extends Instrument {
    void play(Note n) { print("Wind.play() " + n); }
    String what() { return "Wind"; }
    void adjust() { print("Adjusting Wind"); }
}


class Percussion extends Instrument {
    void play(Note n) { print("Percussion.play() " + n); }
    String what() { return "Percussion"; }
    void adjust() { print("Adjusting Percussion"); }
}

class Stringed extends Instrument {
    void play(Note n) { print("Stringed.play() " + n); }
    String what() { return "Stringed"; }
    void adjust() { print("Adjusting Stringed"); }
}


class Brass extends Wind {
    void play(Note n) { print("Brass.play() " + n); }
    void adjust() { print("Adjusting Brass"); }
}


class Woodwind extends Wind {
    void play(Note n) { print("Woodwind.play() " + n); }
    String what() { return "Woodwind"; }
}


public class Music3 {
    // Doesn't care about type, so new types
    // added to the system still work right:
    public static void tune(Instrument i) {
        // ...
        i.play(Note.MIDDLE_C);
    }

    public static void tuneAll(Instrument[] e) {
        for(Instrument i : e)
            tune(i);
    }
    public static void main(String[] args) {
        // Upcasting during addition to the array:
        Instrument[] orchestra = {
            new Wind(),
            new Percussion(),
            new Stringed(),
            new Brass(),
            new Woodwind()
        };
        tuneAll(orchestra);
    }
}

​ 为乐器系统添加更多的类型,而不用改动tune方法。tune方法完全可以忽略它周围代码所发生的全部变化,依旧正常运行。

3、缺陷

私有方法

private方法被自动修饰为final,而且对导出类是屏蔽的,所以在子类Derived类中的f方法是一个全新的方法。既然基类中的f方法在在子类Derived中不可见,那么也不能被重载。

域与静态方法

任何域(field)的访问操作都是由编译器解析的,因此不是多态的。

如果某个方法是静态的,那么它就不具有多态性

三、构造器与多态

通常,构造器不同于其它方法,涉及到多态时也是如此。构造器是不具有多态性的

1、构造器的调用顺序

基类的构造器总是在导出类的构造过程中被调用,而且按照继承层次逐渐向上链接。使得每个基类的构造器都能得到调用。

2、构造器内部的多态方法的行为

构造器调用的层次结构带来一个问题:如果在一个构造器内部调用正在构造的对象的某个动态绑定方法,会发生什么?


public class Glyph {

    void draw() { print("Glyph.draw()"); }
    Glyph() {
        print("Glyph() before draw()");
        draw(); // 调用正在构造的对象的某个动态绑定方法,对象的字段radius被初始化为0
        print("Glyph() after draw()");
    }

}


public class RoundGlyph extends Glyph{

    private int radius = 1;

    RoundGlyph(int r) {
        radius = r;
        print("RoundGlyph.RoundGlyph(), radius = " + radius);
    }
    void draw() {
        print("RoundGlyph.draw(), radius = " + radius);
    }


}


public class PolyConstructors {
    
    public static void main(String[] args) {
        new RoundGlyph(5);
    }
    
}

//:~

Glyph的构造器中,我们调用了draw方法,因为这个是动态绑定方法的缘故,我们就会调用导出类RoundGlyph中的draw方法,但是这个方法操纵的成员radius还没初始化,所以就体现出问题了,结果中第一次输出radius为0。

所以初始化的实际过程是:

  • 1 在其他任何事物之前,将分配给对象的存储空间初始化成二进制的零
  • 2 如前所述调用基类构造器
  • 3 按照声明的顺序调用成员的初始化方法
  • 4 调用导出类的构造器主体

四、协变返回类型

在面向对象程序设计中,协变返回类型指的是子类中的成员函数的返回值类型不必严格等同于父类中被重写的成员函数的返回值类型,而可以是更 "狭窄" 的类型。

​ Java 5.0添加了对协变返回类型的支持,即子类覆盖(即重写)基类方法时,返回的类型可以是基类方法返回类型的子类。协变返回类型允许返回更为具体的类型。

例子:


import java.io.ByteArrayInputStream;
import java.io.InputStream;

class Base
{
    //子类Derive将重写此方法,将返回类型设置为InputStream的子类
   public InputStream getInput()
   {
      return System.in;
   }
}
public  class Derive extends Base
{

    @Override
    public ByteArrayInputStream getInput()
    {

        return new ByteArrayInputStream(new byte[1024]);
    }
    public static void main(String[] args)
    {
        Derive d=new Derive();
        System.out.println(d.getInput().getClass());
    }
}


五、继承进行设计


class Actor {
 public void act() {
 }
}
 
class HappyActor extends Actor {
 public void act() {
  System.out.println("HappyActor");
 }
}
 
class SadActor extends Actor {
 public void act() {
  System.out.println("SadActor");
 }
}
 
class Stage {
 private Actor actor = new HappyActor();
 
 public void change() {
  actor = new SadActor();
 }
 
 public void performPlay() {
  actor.act();
 }
}
 
public class Transmogrify {
 public static void main(String[] args) {
  Stage stage = new Stage();
  stage.performPlay();
  stage.change();
  stage.performPlay();
 }
 
}

输出:

  HappyActor
    SadActor

一条通用的准则是:“用继承表达行为间的差异,并用字段表达状态上的变化”。在上述例子中,两者都用到了:通过继承得到了两个不同的类,用于表达 act()方法的差异:而 Stage通过运用组合使自己的状态发生了变化。在这种情况下,这种状态的改变也就产生了行为的改变。

总结:

多态意味着 不同的形式。在面向对象的设计中,我们持有从基类继承而来的相同接口,以及使用该接口的不同形式不同版本的多态绑定方法。 运用数据的抽象和继承,能更好的类型和创造多态的例子。

到此这篇关于Java编程 多态的文章就介绍到这了,更多相关Java多态内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Java编程 多态

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Java编程—在测试中考虑多态

面向对象编程有三大特性:封装、继承、多态。封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据。对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法。继承是为了重用父类代码。两个类若存在IS-A的关
2023-05-30

Java面向对象编程的多态怎么实现

本文小编为大家详细介绍“Java面向对象编程的多态怎么实现”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java面向对象编程的多态怎么实现”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。Java面向对象编程之多态
2023-06-26
2023-08-31

java多线程编程之InheritableThreadLocal

InheritableThreadLocal的作用: 当我们需要在子线程中使用父线程中的值得时候我们就可以像使用ThreadLocal那样来使用InheritableThreadLocal了。 首先我们来看一下InheritableThre
2023-05-31

java多线程编程实例

以下是一个简单的Java多线程编程实例:```javapublic class MultiThreadExample implements Runnable {private String threadName;public MultiTh
2023-08-16

Java之多态

多态 多态的实现条件重写重写的定义重写的例子方法重写的条件 多态思想动态绑定与静态绑定 作者简介: zoro-1,目前大一,正在学习Java,数据结构等 作者主页:zoro-1的主页 欢迎大家点赞 👍
2023-08-16

Java面向对象编程(封装/继承/多态)实例解析

本文主要介绍了面向对象的三大特征实例解析,下面看看具体内容。封装封装一个Teacher和Student类package com.hz.test;public class Teacher { private String name; pr
2023-05-30

Java多线程编程的概念

这篇文章主要讲解了“Java多线程编程的概念”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java多线程编程的概念”吧!Java多线程编程是什么?一个程序或进程能够包含多个线程,这些线程可以
2023-06-17

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录