《Java编程思想》学习笔记_多态

多态指一个行为产生多种状态,针对父类类型可接收其子类类型,最终执行的状态由具体子类确定,其不同子类可呈现出不同状态。例如人[父类]都会跑步[行为],但小孩[子类]跑步、成年人[子类]跑步、运动员[子类]跑步呈现出来的状态是不一致的。

例如:

创建一个父类People,和对应子类Child,Adult,Athletes.

子类都各自实现了一遍父类的run方法。

class People {
    public void run() {
        System.out.println("run");
    }
}

class Adult extends People {
    public void run() {
        System.out.println("Adult run,1km 4分钟");
    }
}

class Child extends People {
    public void run() {
        System.out.println("Child run, ,1km 3分钟");
    }
}

class Athletes extends People {
    public void run() {
        System.out.println("Athletes run, ,1km 2分钟");
    }
}

创建了一个run方法,接收参数为People类型。

public class Main {
    public static void main(String[] args) {
        Adult adult = new Adult();
        Child child = new Child();
        Athletes athletes  = new Athletes();

        run(adult);
        run(child);
        run(athletes);
    }

    private static void run(People people){
        people.run();
    }
}

Adult run,1km 4分钟
Child run, ,1km 3分钟
Athletes run, ,1km 2分钟

可以看到,接收类型为父类类型,具体传递的参数类型为子类类型。执行run方法时,执行的是具体子类的run方法。上述三个子类都对父类的run方法进行了重写。所有优先执行子类重写的方法,如果子类没有重写对应方法,执行的依然是子类方法。

class Adult extends People {

}
run
Child run, ,1km 3分钟
Athletes run, ,1km 2分钟

Adult修改为不重写父类方法,执行后Adult执行的就是父类的run方法。

既然方法类型为People可接收其所有子类类型。程序可改写为

public class Main {
    public static void main(String[] args) {
        People adult = new Adult();
        People child = new Child();
        People athletes  = new Athletes();

        run(adult);
        run(child);
        run(athletes);
    }
}

使用多态可以确定一个规范,规范具体的实现可能有差异,但使用该规范时,只需要将接收参数设置为该规范,就可接收所有遵循该规范的实现。避免为每一个具体实现编写一个方法。

避免如下情况:

    private static void run(Adult people){
        people.run();
    }
    private static void run(Child people){
        people.run();
    }
    private static void run(Athletes people){
        people.run();
    }

将方法调用桶一个方法主体关联起来成为方法绑定。方法绑定主要有前期绑定,后期绑定(运行时绑定,动态绑定)。

  • 前期绑定 方法在编译时绑定,程序一但编译完成,绑定就完成了,方法调用与主体已经进行了关联,运行过程中始终只调用与之绑定的主体上的方法。
  • 后期绑定 方法绑定是在运行时,根据判断对象类型,调用合适的方法。

上例中接收参数类型均为People,方法执行时根据传递入方法的不同子类执行具体方法。此时就是动态绑定,运行时方法调用会判断对象类型调用对应的方法。Java中private/final/static修饰的方法都是前期绑定,其余方法都是动态绑定。

如下例:

public class Main {
    public static void main(String[] args) {
        A a = new A1();
        System.out.println(a.i);
    }
}

如果按照上面多态的的逻辑分析该代码,输出应该为子类的i即1.但实际输出的是父类的i值。

域的访问操作,都会有编译器解析,因此不是多态的。换而言之,只有方法才具有多态性。

A a = new A1();
a.i;

由于访问变量i不具有多态,同时此时A1向上转型为A。

访问a.i可看做访问A1类型的父类的i,即A1的super.i。

class A {
    public int i = 0;
}

class A1 extends A {
    public int i = 1;

    public int getI(){
        return i;
    }

    public int getAi(){
        return super.i;
    }
}
public class Main {
    public static void main(String[] args) {
        A a = new A1();
        System.out.println(a.i);

        A1 a1 = new A1();
        System.out.println(a1.i);
        System.out.println(a1.getI());
        System.out.println(a1.getAi());
    }
}
0 //A a = new A1(); a.i 实质是A的i
1 //A1 a1 = new A1(); a1.i A1的i
1
0 // A1 a1 = new A1(); a1.getAi(); super.i A的i

继承时,A与A1的域使用到的是两块区域存储对应变量,可看做A区域中有一个i,A1区域中也有一个i。此时由于父类和子类都有对应i,此时访问a.i虽然真实类型时A1,但A1继承自A, A a = new A1()访问 a.i,由于A1被向上转型为A,可看做访问A1的父类A的i。

class F {
    private void fPrivate(){
        System.out.println("F.fPrivate");
    }

    public static void fStatic(){
        System.out.println("F.fStatic");
    }

    public static void main(String[] args) {
        F f = new F1();
        f.fPrivate();

        f.fStatic();
    }
}

class F1 extends F{
    public void fPrivate(){
        System.out.println("F1.fPrivate");
    }

    public static void fStatic(){
        System.out.println("F1.fStatic");
    }
}

其中f调用private方法和static方法,访问的都是父类中的方法,并未实现多态。

实现多态是基于动态绑定,确定具体方法调用的主体,private访问权限就相当于告诉编译器,该方法不需要动态绑定。当方法不要动态绑定时,就会以当前向上转型的类型为准。所以调用的父类的private方法。

静态方法是与类关联,而非与对象关联。所以调用时是以具体类型信息为准。也是以向上转型后的父类类型信息为准,调用父类的静态方法。

假设现在有这样一个问题,有两个类是继承关系。子类和父类都有f()方法,在父类初始化时调用了f()方法,此时f方法究竟是子类的还是父类的方法?。

此处有一个两难的问题:

class A {
    A() {
        System.out.println("A构造器开始执行");
        f();//此处调用的子类的f()方法,但此时子类还未完成初始化。
        System.out.println("A构造器结束执行");
    }

    void f() {
        System.out.println("A.f()");
    }
}

class B extends A {
    private int bi = 1;

    B() {

    }

    B(int bi) {
        this.bi = bi;
    }

    void f() {
        System.out.println("B.f() bi=" + bi);
    }
}

public class Main {

    public static void main(String[] args) {
        B b = new B(5);
    }
}
A构造器开始执行
B.f() bi=0
A构造器结束执行

可以看到A构造器中执行时,执行的是子类B的f方法,但此时B类并未完全初始化,所以执行输出bi时输出的默认的0. 一个动态绑定的方法调用会向外深入到继承层次结构内部,它可以调用导出类(子类)里的方法。(《Java编程思想》原话)暂时还不太理解

协变返回类型是指,但某个方法对父类进行重写时,返回类型不一定需要和父类方法的返回类型完全一致。可以是父类方法返回类型的子类。例如,父类方法 A f(),子类重写父类方法应为 A f(), 但子类重写时返回类型也可为A的子类,如 A1 f().

首先有父类A子类A1作为返回类型

class A {

    public String toString() {
        return "A";
    }
}

class A1 extends A {
    public String toString() {
        return "A1";
    }
}
class F {
    public A f() {
        return new A();
    }
}

class F1 extends F {
    public A1 f() {// 子类重写父类方法,返回类型不一定为父类的A,也可为父类对应返回类型A的子类A1
        return new A1();
    }
}

public class Main {

    public static void main(String[] args) {
        F f = new F();
        System.out.println(f.f());

        f = new F1();
        System.out.println(f.f());
    }
}
A
A1

Original: https://www.cnblogs.com/huang-changfan/p/14471590.html
Author: gcmh
Title: 《Java编程思想》学习笔记_多态

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/588419/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 客观的聊一聊,裁员这件糟心事

    时间在走,环境在变,互联网有点卷不动了; 捋一捋最近互联网上关于职场的热点:裁员,优化,毕业,向社会输送人才,求职;你方唱罢他方登场,持续横跳热搜; 年初到现在五月底,身边已经有好…

    Java 2023年6月15日
    096
  • python 类与对象

    如果需创建一个类,那么可以使用 class 关键字:实例使用名为 a 的属性,创建一个名为 MyClass 的类: class MyClass: a = 6 现在我们可以使用名为 …

    Java 2023年6月9日
    055
  • 深入理解this关键字

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Java 2023年6月7日
    077
  • python 图形验证码的实现

    python 图形验证码的实现 导入pil库和ramdom库 from PIL import Image, ImageDraw, ImageFont, ImageFilter im…

    Java 2023年6月7日
    067
  • Delphi 多线程的例子

    unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Contr…

    Java 2023年5月29日
    087
  • Java随谈(六)我们真的理解 Java 里的整型吗?

    整型是我们日常生活中最常用到的基础数据类型,看这篇文章之前,我想问: 我们真的像自己认为的那么理解 Java 内的整型吗? 也许看完本篇文章你就有自己的答案。 C 语言 提供了如下…

    Java 2023年6月8日
    085
  • rocketmq学习之-基本样例

    1 基本样例 在基本样例中我们提供如下的功能场景: 使用RocketMQ发送三种类型的消息:同步消息、异步消息和单向消息。其中前两种消息是可靠的,因为会有发送是否成功的应答。 使用…

    Java 2023年6月5日
    0112
  • SpringBoot一个服务按多个端口同时启动

    为了模拟集群环境,本地项目一个项目按不同端口多启动,如何配置多个端口同时启动。 1、首先,你得有一个springboot项目,然后点击修改运行配置 2、将你的项目配置的右上角的Al…

    Java 2023年5月30日
    080
  • PhotoShop入门

    字体安装 将xxx.ttf字体文件放在 C:\Windows\Fonts目录下。 PhotoShop新建工作区 默认显示的工作区如下图所示: 图层、通道、路径、调整、库等都是不常用…

    Java 2023年6月15日
    084
  • Java中的final关键字

    字面意思:被final修饰的对象是无法改变的。 一、final数据: (2)同时被static和final修饰的数据占据一段不能被改变的存储空间,同时,由于static的作用,该数…

    Java 2023年5月29日
    0100
  • Java语言编码规范(Java Code Conventions) 转载

    Java语言编码规范(Java Code Conventions) 晨光(Morning) 本文档讲述了Java语言的编码规范,较之陈世忠先生《c++编码规范》的浩繁详尽,此文当属…

    Java 2023年5月29日
    074
  • SpringBoot事件监听器源码分析

    本文涉及到Spring的监听器,如果不太了解请先阅读之前的Spring监听器的文章。 SpringBoot事件监听器初始化 SpringBoot中默认定义了11个事件监听器对象,全…

    Java 2023年6月13日
    083
  • Spring和Springboot相关知识点整理

    简介 本文主要整理一些Spring & SpringBoot应用时和相关原理的知识点,对于源码不做深入的讲解。 思维导图 右键新窗口打开可以放大。 说明 使用@Config…

    Java 2023年5月30日
    089
  • Prohibited package name: java

    一级包名不能叫Java 本文来自博客园,作者:紫英626,转载请注明原文链接:https://www.cnblogs.com/recorderM/p/16037571.html O…

    Java 2023年6月5日
    092
  • 好书推荐之《码出高效》、《阿里巴巴JAVA开发手册》

    好评如潮 《阿里巴巴Java开发手册》 简介 《阿里巴巴Java开发手册》的愿景是码出高效,码出质量。它结合作者的开发经验和架构历程,提炼阿里巴巴集团技术团队的集体编程经验和软件设…

    Java 2023年6月15日
    089
  • rocketMQ 双机房部署

    posted on2020-05-08 21:43 偶尔发呆 阅读(707 ) 评论() 编辑 Original: https://www.cnblogs.com/allenwas…

    Java 2023年5月30日
    088
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球