深入探讨多态性及其在Java中的好处
多态是面向对象软件的基本原理之一。该术语通常表示可以具有多种形式的事物。在面向对象的方法中,多态使编写具有后期绑定引用的程序成为可能。尽管在Java中创建多态引用很容易,但其背后的概念对整体编程产生了更深远的影响。本文结合在优锐课学习到的知识点,探讨了有关多态性及其对面向对象编程的影响的一些复杂细节。
多态参考:概述
多态引用是一个变量,可以在不同的时间点引用不同类型的对象。它通常与它所引用的类兼容。 例如,在以下情况下:
Employee employee;
'employee'是一个引用变量,可以引用Employee类的实例。参考变量对参考对象的限定取决于其兼容性。这似乎是唯一可靠的条件,但事实并非如此,特别是在实现多态时。规则太严格了,但是通过结合“具有多种形式”的思想,多态性使得灵活性更高。这意味着多态引用保证了它可以在不同的时间点引用不同类型的对象,而不是为兼容性而完全依赖。因此,如果可以在一个时间点使用引用来调用方法,则可以将其动态更改为指向另一个对象,并在下一次调用其他方法。通过提供引用变量的另一个使用维度,这可以利用灵活性。
当引用变量绑定到无法在运行时更改的对象时,或者换句话说,方法调用与方法定义的绑定是在编译时完成的,称为静态绑定。如果绑定在运行时是可更改的,例如在多态引用的情况下,绑定的决策仅在执行期间做出,则称为动态绑定或后期绑定。两者在面向对象程序设计中都有其用途,并非一个都胜过另一个。但是,在多态引用的情况下,推迟的绑定承诺在灵活性方面使其比编译时绑定更具优势,但另一方面,这会降低性能开销。但是,这在很大程度上是可以接受的,并且在提高效率方面,开销通常具有很小的吸引力。.
创建多态
在Java中,可以通过两种方式创建多态引用:使用继承或使用接口。
继承多态
引用变量引用类的实例。对于继承层次结构,如果引用变量在层次结构树中声明为父类类型,则引用对象可以指向层次结构中任何类的实例。这意味着,在Java中,因为Objectclass是所有类的父类或超类,或者换句话说,Java中的所有类实际上是Object类的子类隐式或显式地是对象类的引用变量。对象类型可以引用Java中的任何类实例。这就是我们的意思。
Employee employee;
Object object;
employee = new Employee();
object = employee; // This is a valid assignment
如果情况相反,则如下所示:
Employee employee;
Object object = new Object();
employee = (Employee)object // Valid, but needs explicit cast
观察到它需要显式强制转换;只有这样,它才能成为有效的声明。可以看出,这种反向分配对于在许多情况下出现问题的边缘来说没有多大用处。这是因为Object实例的功能与Employee引用变量预期的功能几乎没有关系。关系is-a可以从employee-is-an-object派生派生;在这种情况下,相反的关系(例如,对象是雇员)太牵强。
继承多态性:一个例子
让我们尝试借助示例来理解它。
图1:从驱动程序类,公司派生的类
称为Company的驱动程序类创建一个雇员列表,并调用paySalary()方法。薪资类维护公司中不同类型员工的列表。请注意,该数组被声明为派生自Employee类(所有雇员子类的父级或超类)的引用变量的数组。结果,可以用从Employee类的任何子类(例如CommissionEmployee, HourlyEmployee, SalariedEmployee)创建的对象引用填充数组。在paySalary()定义中,根据数组中的对象引用调用适当的salary()方法。因此,对salary()方法的调用是多态的,很明显,每个类都有其自己版本的salary()方法。
Payroll类中的employee数组不代表特定类型的Employee。它用作可以指向任何类型的Employee子类引用的句柄。尽管继承的类共享作为后代继承的一些公共数据,但是它们具有各自的属性集。
通过继承实现多态:Java实现
这是该示例在Java中的快速实现。
package org.mano.example;
public class Company
{
public static void main( String[] args )
{
Payroll payroll = new Payroll();
payroll.paySalary();
}
}
package org.mano.example;
import java.util.ArrayList;
import java.util.List;
public class Payroll {
private List<Employee> employees =
new ArrayList<>();
public Payroll() {
employees.add(new
SalariedEmployee("Harry Potter",
"123-234-345",7800));
employees.add(new
CommissionEmployee("Peter Parker",
"234-345-456",2345.67,0.15));
employees.add(new
HourlyEmployee("Joker Poker",
"456-567-678",562.36,239.88));
}
public void paySalary() {
for (Employee e: employees) {
System.out.println
("----------------------------------------------------");
System.out.println(e.toString());
System.out.printf
("Gross payment: $%,.2f\n",e.salary());
System.out.println
("----------------------------------------------------");
}
}
} package org.mano.example; public abstract class Employee {
protected String name;
protected String ssn;
public Employee(String name, String ssn) {
this.name = name;
this.ssn = ssn;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSsn() {
return ssn;
}
public void setSsn(String ssn) {
this.ssn = ssn;
}
@Override
public String toString() {
return String.format("%s\nSSN: %s",
getName(),getSsn());
}
public abstract double salary();
} package org.mano.example;
public class SalariedEmployee extends Employee {
protected double basicSalary;
public SalariedEmployee(String name, String ssn,
double basicSalary) {
super(name, ssn);
setBasicSalary(basicSalary);
}
public double getBasicSalary() {
return basicSalary;
}
public void setBasicSalary(double basicSalary) {
if(basicSalary>= 0.0)
this.basicSalary = basicSalary;
else
throw new IllegalArgumentException("basic " +
"salary must be greater than 0.0");
}
@Override
public double salary() {
eturn getBasicSalary();
}
@Override
public String toString() {
return String.format("%s\nBasic Salary: $%,.2f",
super.toString(),getBasicSalary());
}
} package org.mano.example;
public class HourlyEmployee extends Employee {
protected double wage;
protected double hours;
public HourlyEmployee(String name, String ssn,
double wage, double hours) {
super (name, ssn);
setWage(wage);
setHours(hours);
}
public double getWage() {
return wage;
}
public void setWage(double wage) {
if(wage >= 0.0)
this.wage = wage;
else
throw new IllegalArgumentException("wage " +
"must be > 0.0");
}
public double getHours() {
return hours;
}
public void setHours(double hours) {
if(hours >= 0.0)
this.hours = hours;
else
throw new IllegalArgumentException("hours " +
"must be > 0.0");
}
@Override
public double salary() {
return getHours() * getWage();
}
@Override
public String toString() {
return String.format("%s\nWage: $%,
.2f\nHours worked: %,.2f",
super.toString(),getWage(),getHours());
}
} package org.mano.example;
public class CommissionEmployee extends Employee {
protected double sales;
protected double commission;
public CommissionEmployee(String name, String ssn,
double sales, double commission) {
super(name, ssn);
setSales(sales);
setCommission(commission);
}
public double getSales() {
return sales;
}
public void setSales(double sales) {
if(sales >=0.0)
this.sales = sales;
else
throw new IllegalArgumentException("Sales " +
"must be >= 0.0");
}
public double getCommission() {
return commission;
}
public void setCommission(double commission) {
if(commission > 0.0 && commission < 1.0)
this.commission = commission;
else
throw new IllegalArgumentException("Commission " +
"must be between 0.0 and 1.0");
}
@Override
public double salary() {
return getCommission() * getSales();
}
@Override
public String toString() {
return String.format("%s\nSales: %,
.2f\nCommission: %,.2f",
super.toString(),getSales(),getCommission());
}
}
接口多态
接口的多态性与前面的示例非常相似,不同之处在于,这里的多态性规则是根据Java接口指定的规范进行的。接口名称可以用作引用变量,就像我们对上面的类名称所做的那样。它引用实现该接口的任何类的任何对象。这是一个例子。
package org.mano.example;
public interface Player {
public enum STATUS{PLAY,PAUSE,STOP};
public void play();
public void stop();
public void pause();
} package org.mano.example;
public class VideoPlayer implements Player {
private STATUS currentStatus = STATUS.STOP;
@Override
public void play() {
if(currentStatus == STATUS.STOP ||
currentStatus == STATUS.PAUSE) {
currentStatus = STATUS.PLAY;
System.out.println("Playing Video...");
}
else
System.out.println("I am ON playing man!");
}
@Override
public voidstop() {
if(currentStatus == STATUS.PLAY ||
currentStatus == STATUS.PAUSE) {
currentStatus = STATUS.STOP;
System.out.println("Video play stopped.");
}
else
System.out.println("Do you want me to go fishing?");
}
@Override
public void pause() {
if(currentStatus == STATUS.PLAY) {
currentStatus = STATUS.PAUSE;
System.out.println("Video play paused.");
}
else
System.out.println("I'm a statue. You froze me
already!");
}
} package org.mano.example;
public class AudioPlayer implements Player {
private STATUS currentStatus = STATUS.STOP;
@Override
public void play() {
if(currentStatus == STATUS.STOP ||
currentStatus == STATUS.PAUSE) {
currentStatus = STATUS.PLAY;
System.out.println("Playing Audio...");
}
else
System.out.println("I am ON playing man!");
}
@Override
public void stop() {
if(currentStatus == STATUS.PLAY ||
currentStatus == STATUS.PAUSE) {
currentStatus = STATUS.STOP;
System.out.println("Audio play stopped.");
}
else
System.out.println("Do you want me to go fishing?");
}
@Override
public void pause() {
if(currentStatus == STATUS.PLAY) {
currentStatus = STATUS.PAUSE;
System.out.println("Audio play paused.");
}
else
System.out.println("I'm a statue. You froze me
already!");
}
} package org.mano.example;
public class PlayerApp {
public static void main(String[] args) {
Player player= new VideoPlayer();
player.play();
player.pause();
player.stop();
player= new AudioPlayer();
player.play();
player.pause();
player.stop();
}
}
请注意,在PlayerApp中,我们已经使用接口Player来声明对象引用变量。引用变量player可以引用实现Player接口的任何类的任何对象。为了证明这一点,我们在这里使用了同一播放器变量来引用VideoPlayer对象和AudioPlayer对象。运行时调用的方法是特定于其引用的类对象的方法。实现接口的类与接口本身之间的关系是父子关系,正如我们在带有继承的多态示例中所看到的。它也是一个is-arelationship,并构成了多态性的基础。
总结
通过类继承或通过接口实现多态性之间的差异是一个选择问题。实际上,区别在于理解类和接口的属性和特性。除了了解其性质外,没有严格的规则来定义何时使用。这超出了本文的范围。但是,在多态中,这个想法既适合并且也有能力完成我们想对它们进行的操作。就这样。抽丝剥茧,细说架构那些事——【优锐课】
深入探讨多态性及其在Java中的好处的更多相关文章
- Java中多态性的实现
class A ...{ public String show(D obj)...{ return ("A and D"); } public String show(A obj) ...
- Java中对域和静态方法的访问不具有多态性
1.将方法调用同方法主体关联起来被称为 2.编译期绑定(静态)是在程序编译阶段就确定了引用对象的类型 3.运行期绑定(动态绑定)是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法 ...
- 转载 - java中接口的向上转型。和多态性
发现一篇对接口总结很精简的文章 1.在java中接口就是一个完全抽象的类,跟抽象类一样不能产生对象,但是可以作为对象的引用,可以由其实现类向上转型,它就跟超类一样, 向上转型了,可以很好的利用接口,可 ...
- 【Java_基础】java中的多态性
方法的重载.重写和动态链接构成了java的多态性. 1.方法的重载 同一个类中多个同名但形参有所差异的方法,在调用时会根据参数的不同做出选择. 2.方法的重写 子类中重新定义了父类的方法,有关方法重写 ...
- 深入探讨Java中的异常与错误处理
Java中的异常处理机制已经比较成熟,我们的Java程序到处充满了异常的可能,如果对这些异常不做预先的处理,那么将来程序崩溃就无从调试,很难找到异常所在的位置.本文将探讨一下Java中异常与错误的处理 ...
- 深入Java核心 Java中多态的实现机制(1)
在疯狂java中,多态是这样解释的: 多态:相同类型的变量,调用同一个方法时,呈现出多中不同的行为特征, 这就是多态. 加上下面的解释:(多态四小类:强制的,重载的,参数的和包含的) 同时, 还用人这 ...
- java中实现多态的机制是什么?
多态性是面向对象程序设计代码重用的一个重要机制,我们曾不只一次的提到Java多态性.在Java运行时多态性:继承和接口的实现一文中,我们曾详细介绍了Java实现运行时多态性的动态方法调度:今天我们再次 ...
- Java中hashcode的理解
Java中hashcode的理解 原文链接http://blog.csdn.net/chinayuan/article/details/3345559 怎样理解hashCode的作用: 以 java. ...
- Java中堆内存和栈内存详解2
Java中堆内存和栈内存详解 Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,ja ...
随机推荐
- 私有git搭建
Git简介(目前世界上最先进的分布式版本控制系统) 那什么是版本控制系统? 你可以把一个版本控制系统(缩写VCS)理解为一个特殊的“数据库”,在需要的时候,它可以帮你完整地保存一个项目的快照.当你需要 ...
- springMVC-MyBatis-Mysql 环境下, 返回时间格式不是指定格式
在数据库中的时间是: 比如: 2018-04-06:12;23:34. 但是mybatis查询出来以后是下面的格式. {"updatedTime":{"date" ...
- python:爬虫0
什么是网页爬虫,也叫网页蜘蛛.把互联网比作一个蜘蛛网,有好多节点,这个蜘蛛在网上爬来爬去,对对网页中的每个关键字进行建立索引,然后建立索引数据库,经过复杂的排序算法后,这些算法的结果将按照相关度的高低 ...
- 建筑行业的新起之秀---BIM
近年来,BIM在国家在建筑行业的推进下逐渐走近人们的视线,而且BIM技术是作为建筑领域的一项新技术行业发展的越来越好,在很多的建筑场景都用到了BIM建模.施工.运维以及BIM+GIS等以BIM为 ...
- requirements.txt的创建及使用
python的包管理 pip方式: 创建 (venv) $ pip freeze >requirements.txt 执行 (venv) $ pip install -r requirement ...
- 达梦"记录超长"警告
出现"记录超长"背景介绍: 导入数据库时,出现数据库记录超长警告,导致数据无法正常导入! 1.重新建库,把页大小改大 这种方式是在建立数据库实例的时候进行的 修改[页大小] 2.把 ...
- 在centos7上进行hadoop-3.1.2的伪分布搭建
第一步:配置网络(静态IP) vi /etc/sysconfig/network-scripts/ifcfg-ens33(网卡名称可能不同) 1. 修改: 将该配置文件中的ONBOOT=no修改为ye ...
- django-migrate一败再败
python3 manage.py makemigrations # 生成数据库迁移文件 python3 manage.py migrate # 迁移数据库 简简单单两条命令就完成了django的数据 ...
- 【开发工具 - MySQL】之不能插入中文的问题
新安装的MySQL数据库,在安装的时候设置了字体为UTF8,但在使用insert语句插入中文的时候还是会报错. 具体解决方法:在MySQL控制台中输入以下设置代码: SET character_set ...
- linux 生成免密登陆步奏
1.大致流程 有时需要从服务器A免密连接到服务器B,这时需要在服务器A生成私钥和公钥,大致过程其实就2步. 1.1 在服务器A生成公钥和私钥 在服务器A上输入如下命令: ssh-keygen -t r ...