Java时如何实现多态的(重载和重写)_方法重现体现多态-程序员宅基地

回顾一下Java的四大特性抽象,封装,继承,多态
其中封装是依靠访问修饰符(public,default,protected,private)实现的,继承是依靠关键字extends,那么多态又是依靠什么实现的呢?

什么是多态

多态的概念并不难,并且在实际编码中可以说是最最高频使用率。多态就是使得同一个行为具有多个不同表现形式或形态的能力。举个形象点的例子:对于 “打印” 这个行为,使用彩色打印机 “打印” 出来的效果就是彩色的,而使用黑白打印机 “打印” 出来的效果就是黑白的。我们就称 “打印” 这个行为是多态的,彩色打印效果和黑白打印效果就是 “打印” 这个行为的两个不同的表现形式。
在这里插入图片描述

同一个行为在不同的对象上会产生不同的结果。

多态发生的三个必要条件
看下面这段代码,首先,我们有一个基类 Shape,三个子类,并且都重写了基类的 draw 方法:

class Shape {
    
    void draw() {
    }
}
 
class Circle extends Shape {
    
    void draw() {
    
        System.out.println("Circle.draw()");
    }
}
 
class Square extends Shape {
    
    void draw() {
    
        System.out.println("Square.draw()");
    }
}
 
class Triangle extends Shape {
    
    void draw() {
    
        System.out.println("Triangle.draw()");
    }
}

多态的体现就在下面这几行代码里

Shape circle = new Circle();//画一个圆形
Shape square = new Square();//画一个正方形
Shape triangle = new Triangle();//画一个三角形

是否有些眼熟,上面这三行代码不就是向上转型吗!继承中提到的向上转型,它就是多态的体现。同样的一个 draw 方法,在这三个不同的对象上产生了三种不同的行为,多态在此体现的淋漓尽致。
注意点
这里需要注意的是,当使用多态方式调用方法时,编译器会首先检查父类中是否有该方法,如果没有,则编译错误;如果父类中有该方法,并且被子类重写,就会调用子类的这个方法;如果父类的方法没有被子类重写,就会调用父类的方法。

Shape circle = new Circle();
circle.draw(); // 调用的是 Circle 的 draw,不是Shape的,只有当Circle没有重写draw的时候才会调用Shape

简单来说:当父类引用变量指向子类对象后(多态),只能使用父类已声明的方法,但方法如果被重写会执行子类的方法,如果方法未被重写那么将执行父类的方法。

实现多态的三要素(必要条件)
1)继承
2)重写
3)父类引用指向子类对象:Parent p = new Child();
在这里插入图片描述
多态是如何发生的
那么,多态到底是如何发生的?编译器是如何知道父类 Shape 引用指向的是 Circle 而不是 Triangle 或 Square 呢?
若绑定发生在程序运行前,叫做静态绑定,也称前期绑定。你可能从来没有听说这个术语,因为它是面向过程语言不需选择默认的绑定方式,例如在 C 语言中就只有前期绑定这一种方法调用。

Shape circle = new Circle();
Shape square = new Square();
circle.draw(); 

对于上面的代码,Shape 即引用类型在编译期可知,不会被改变,而 Circle 作为实例对象类型在运行期才可知,可能会发生变化。所以如果使用前期绑定,在运行之前,编译器只知道有一个 Shape 引用,它无法得知究竟会调用哪个方法。
解决方法就是动态绑定 Dynamic Binding,在运行时根据对象的类型自动的进行绑定(JVM类加载),所以动态绑定也称运行时绑定。动态绑定是多态的基础。(自动绑定实例化对象对应的类)
注意:Java 中除了 static 和 final方法(private 方法属于 final 方法)之外,其他所有方法都是动态绑定。这意味着通常情况下,我们不需要判断动态绑定是否会发生,它是自动发生的。

final 不允许方法重写,而多态发生的条件之一就是重写,所以 final 方法会在编译期间就进行绑定,即静态绑定
static 方法是类直接拥有的的,与该类的任何一个对象都无关(该类的所有对象共同维护),所以也是静态绑定

重载和重写
方法的重写 Overriding 和重载 Overloading 都是是 Java 多态性的表现。
1.方法的重写是多态的必要条件,也是父类和子类多态性的表现
其中子类方法和父类方法名字相同,参数类型相同,访问权限子类必须大于等于父类,总结来说,子类方法和父类方法除了方法体不一样,访问权限可以有限制的修改之外,其余都是一样的。

class Shape {
    
    public void draw() {
    }
}
 
class Circle extends Shape {
    
    public void draw() {
    
        System.out.println("Circle.draw()");
    }
}

但是重写方法的返回类型一定要和父类方法一模一样吗?
答案其实是否定的,首先,我们需要知道方法的名字参数列表称为方法的签名。例如,draw() 和 draw(String) 是两个具有相同名字, 不同签名的方法。如果在子类中定义了一个与超类签名相同的方法, 那么子类中的这个方法就覆盖/重写了超类中的这个相同签名的方法。
但是返回类型不是签名的一部分, 因此,在覆盖/重写方法时, 一定要保证返回类型的兼容性。 允许子类将覆盖方法的返回类型定义为原返回类型的子类型。
假设shape类为

class Shape {
    
    public Shape draw() {
    
    	......
    }
}

子类重写draw方法

class Circle extends Shape {
    
    public Circle draw() {
    
        ......
    }
}

此时重写的方法返回类型其实和父类方法不同,专业术语来说,这两个 draw 方法具有可协变的返回类型。

2.方法重载并非多态的必要条件,不过可以理解成某个类的多态性的表现
所谓方法重载,就是一个类中定义了多个方法名相同,但是参数的数量或者类型不同。方法的返回类型和访问权限可以任意修改,不以它俩作为方法重载的标志。

class Circle extends Shape {
    
    public void draw() {
    
        System.out.println("Circle.draw()");
    }
    
    public void draw(int i) {
    
        System.out.println("Circle.draw()" + i);
    }
}

在这里插入图片描述
总结一下方法重载和方法重写的区别,其实最简单就是看是否在同一个类

在这里插入图片描述
在这里插入图片描述
既然方法可以重载,那么main方法可以重载吗?
答:只要是方法,都可以重载,但是,如果是作为程序的入口,那么 main 函数只有一种写法,Java 虚拟机在运行的时候只会调用带有参数是 String 数组的那个 main() 方法,而其他重载的写法虚拟机是不认的,只能人为的调用

class Test {
    
	public static void main(String[] args) {
    
		main(1);
	}
	public static void main(int i) {
    
		System.out.println("重载的 main 方法");
	}
}

运行的结果也很简单——“重载的main方法”
可以看出第一个main方法调用了重载后的main方法,打印语句,这也证明了main方法也可以重载,但是程序的入口仍然是String数组的那个main方法
在这里插入图片描述

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_45725126/article/details/119043028

智能推荐

网安学习记录1 ms17010漏洞-程序员宅基地

文章浏览阅读56次。使用nmap对win7进行端口扫描。

python实现软件自动点击_鼠标自动点击、键盘自动输入?几行Python代码搞定-程序员宅基地

文章浏览阅读7.3k次,点赞2次,收藏30次。第一步:安装并导入相关模块只需要安装PyUserInput,如下:pip install PyUserInput如果你是Win10系统,肯定会报错,具体怎么解决,我放在文章结尾讲,先把主要思路讲完。安装完成导入:from pymouse import PyMousefrom pykeyboard import PyKeyboard这里不要奇怪,为什么明明安装的是PyUserInput,却要从pym..._怎么自动控制软件并输入内容

factor graph,potential function,Template models_factor graph potentials-程序员宅基地

文章浏览阅读1.7k次。factor是对于variables的某种combination的fitness。在BN中factor就是conditional probability distribution(CPD);但factor并不总对应着某种概率(当然也不一定取0~1),比如说在MRF中。和数据库table的操作类似,factor上的基本操作有factor product ,factor marginalization_factor graph potentials

vscode如何找letax模板_配置 VSCode 作为LaTex 编辑器-程序员宅基地

文章浏览阅读92次。1.安装texliveWindows系统下latex:texlive2016和texstudiotexstudio 相当于编程时的IDE,可以使用这个经验贴安装texstudio,然后在texstudio中编写tex文档。所以如果你准备用VSCode来编写tex文档的话,可以不用安装texstudio。2.安装并配置VSCode2.1 安装VSCodeVSCode下载next,next就安装好了。..._nonstopmode ctexart.tex

Jackson 配置支持解析JDK8 时间类型_jackson2.15-程序员宅基地

文章浏览阅读966次。配置类的代码转自这里配置类@Configuration@ConditionalOnClass(ObjectMapper.class)@AutoConfigureBefore(JacksonAutoConfiguration.class)public class JacksonConfig { @Bean public Jackson2ObjectMapperBuilderCustom..._jackson2.15

Python做接口自动化时报错:json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)报错可能原因_python 通过接口传递数据完成后,报错为interface exception:expectio-程序员宅基地

文章浏览阅读4k次。接口自动化中出现报错,导致后面的接口数据无法继续进行json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)接口返回的数据可能不是json数据,可能包含html格式,可以考虑在执行接口自动化测试的那段代码中加入可以在执行失败时跳过这条数据,继续后面用例的执行try: ....... 运行接口自动化那..._python 通过接口传递数据完成后,报错为interface exception:expectiong value:lin

随便推点

Dart —— 基础数据类型 Number String Boolean List Map Set Rune Symbol_dart number-程序员宅基地

文章浏览阅读714次。内建类型文章目录Dart 语言支持以下内建类型:NumberStringBooleanListSetMapRuneSymbolDart 语言支持以下内建类型:NumberStringBooleanList (也被称为 Array)MapSetRune (用于在字符串中表示 Unicode 字符)Symbol这些类型都可以被初始化为字面量。 例如, 'this is a s..._dart number

PAT甲级刷题记录——1089 Insert or Merge (25分)_pat甲级1089最后一个测试点-程序员宅基地

文章浏览阅读193次。According to Wikipedia:Insertion sort iterates, consuming one input element each repetition, and growing a sorted output list. Each iteration, insertion sort removes one element from the input data, ..._pat甲级1089最后一个测试点

Lombok中的@Accessors(chain = true)_lombok @accessors(chain = true)-程序员宅基地

文章浏览阅读329次,点赞12次,收藏5次。@Accessors是 Lombok 库中的一个注解属性,它应用于类级别,用于修改生成 getter 和 setter 方法的行为。当 chain = true 时,setter 方法会返回 this,使得可以进行链式调用(fluent API)。在上述代码中,setName 方法返回 person 对象的引用,允许我们在同一行代码中继续调用 setAge 方法。如果没有 chain = true,则每个 setter 方法调用都会返回 void,并且链式调用将不可能实现。_lombok @accessors(chain = true)

wireshark 找不到网卡的解决办法_wireshark 2012 无本地链接-程序员宅基地

文章浏览阅读7.5k次。1. 以管理员身份打开CMD命令行输入命令:net start npf2. 启动wireshark_wireshark 2012 无本地链接

JavaScript 数据结构与算法之美 - 栈内存与堆内存 、浅拷贝与深拷贝-程序员宅基地

文章浏览阅读144次。前言想写好前端,先练好内功。栈内存与堆内存 、浅拷贝与深拷贝,可以说是前端程序员的内功,要知其然,知其所以然。笔者写的 JavaScript 数据结构与算法之美 系列用的语言是 JavaScript ,旨在入门数据结构与算法和方便以后复习。栈定义后进者先出,先进者后出,简..._深拷贝与堆内存

centos下使用nginx+uwsgi 部署django_centos8 uwsgi you are running uwsgi as root-程序员宅基地

文章浏览阅读289次。开发十年,就只剩下这套Java开发体系了>>> ..._centos8 uwsgi you are running uwsgi as root