Java——多态浅析
前言
在面向对象程序设计语言中,多态是继数据抽象和继承之后的第三种基本特性。多态的含义是什么,有什么作用以及在Java中是怎么实现的?下面将做介绍。
什么是多态
简单点说就是“一个接口,多种实现”,不同类对同一操作体现出不同效果。设想有一个性质,一个引用变量所指向的确切类型和该引用变量调用的方法是哪个类中的,这个两个问题在编译期间是不确定的,在程序运行期间才可确定。于是,一份代码就可以适用于多个不同的类,只要这份代码中有一个引用变量可以指向这些不同的类的对象。在程序运行期间,就可以动态选择多个不同的对象或者多个不同的方法运行,这就是多态性。
也可以简单的使用Java核心卷中的一句话说:一个引用变量可以指示多种实际类型的现象被称为多态。
要什么样的引用变量才可以指向多种不同类的对象呢?那就需要是基类引用变量。这就涉及到向上转型
。
向上转型
简单一句话就是:基类(父类)引用指向子类对象。取这个术语属于也是有历史原因的,以传统的类继承图的绘制方法为基础:将根置于页面的顶端,然后逐渐向下。
为什么基类引用就可以指向子类对象呢?
这可以以生活中的一个例子来说,狗是对所有品种狗的统称,具体的品种又有哈士奇,拉布拉多犬等。假设我们在路上遇见了一只不知道名字的狗(比如牧羊犬),我们也许会说:那儿有一只狗。此时,我们就做了向上转型,以狗指向了具体的牧羊犬。以范围较大的基类引用去指向范围小的子类。这是以生活中的例子解释,在语言层面其实也可以说明:子类是继承基类而来,所以基类中的所有方法子类也有,可以发送给基类的消息同样也可以发送给子类。使用基类引用也就可以指向子类对象调用这些方法。
可以使用基类引用指向子类对象,那么可不可以使用子类引用指向基类对象呢?
是可以的,这就叫做向下转型,但是这存在风险。因为子类中可能会有新增方法,而基类中是没有这些方法的,若是调用这些方法就会抛出ClassCastException
异常。
介绍了什么是多态,那么就了解它的作用有什么。
多态的作用
多态的一个好处就是可以实现统一管理,需要注意父类不能调用子类特有的方法即父类中没有的方法子类有的方法,若要调用需要向下转型。
多态如何实现
Java中实现多态的三个要求为:
要有继承关系
方法要被重写
基类引用指向子类对象
可以看一个简单的多态例子
class Animal{
public void eat() { System.out.println("吃东西"); }
}
class Dog extends Animal{
public void eat() { System.out.println("吃狗粮");}
}
class Cat extends Animal{
public void eat() { System.out.println("吃小鱼干");}
}
public class PloyTest {
//使用父类引用指向子类对象 调用子类中被重写的方法
public static void printEatingFood(Animal a) { a.eat();}
public static void main(String[] args) {
printEatingFood(new Dog());
printEatingFood(new Cat());
}
}
/*
output:
吃狗粮
吃小鱼干
*/
当我们传入Dog对象时,a.eat()调用的是Dog类中被重写的eat方法;传入Cat对象时,a.eat()调用的是Cat类中被重写的方法。程序在运行过程中,依据我们传入的对象自动地为我们寻找到正确的方法调用。这就是多态技术的实现依据:动态绑定。
动态绑定
《Java编程思想》上这样说:运行时父类引用根据其指向的对象,绑定到相应的对象方法上。
那么这个过程的具体实现是怎样的呢?《Java核心技术卷1》上是这样解释的解释:
先是要清楚调用对象方法的过程:
- (搜索过程)编译器查看对象的声明类型和方法名。可能会有同名的重载方法。
若是调用x.f(param)
获得当前类和超类中为public的名为f的方法,即获可能被调用的候选方法
- (匹配过程)然后,编译器查看调用方法时提供的参数类型,与候选方法进行匹配,此过程也就是重载解析。
完全匹配,则选择调用该方法,这其中还会存在类型转换,所以过程会比较复杂。最后若是没有找到匹配的,编译器则会报错。
-------------------------------------------------------------------------------------------
静态绑定:
若方法是private, static, final或者构造器,那么编译器会知道调用哪个方法,这种方式为静态绑定。
动态绑定:
依赖于隐式参数的实际类型,在运行时才可以确定调用方法,则为动态绑定。
当程序运行,并且采用动态绑定调用方法时,虚拟机会调用与x所引用对象的实际类型最合适的那个类的方法。并且为了避免每次搜索浪费时间,虚拟机会为每个类创建一个方法表。其中包含所有方法的签名和实际调用的方法(包括继承来方法)。
一些陷阱和建议
域和静态方法
我们需要注意只有普通方法调用才可以是多态的,对域的访问将在编译时期进行解析。如下面这个例子:
class Super{
public int field = 0;
public int getField() { return field;}
}
class Sub extends Super{
public int field = 1;
public int getField() { return field;}
public int getSuperField() { return super.getField();}
}
public class FieldAccess {
public static void main(String[] args) {
Super sup = new Sub();
System.out.println("sup.field="+sup.field+" sup.getField()="+sup.getField());
Sub sub = new Sub();
System.out.println("sub.getField="+sub.field+" sub.getField()="+sub.getField()+
" sub.getSuperField()="+sub.getSuperField());
}
}
/*
output:
sup.field=0 sup.getField()=1
sub.getField=1 sub.getField()=1 sub.getSuperField()=0
*/
当使用父类Super的引用sup指向子类Sub类对象,输出域,发现是父类的值。因此,域的访问是编译器解析,不是多态的。
如果某个方法是静态的,它的行为也不具有多态性。
class StaticSuper{
public static String staticGet() {
return "Base staticGet()";
}
public String dynamicGet(){
return "Base dynamicGet()";
}
}
class StaticSub extends StaticSuper{
public static String staticGet() {
return "Derived staticGet()";
}
public String dynamicGet() {
return "Derived dynamicGet()";
}
}
public class OverloadingTest {
public static void main(String[] args) {
StaticSuper sup = new StaticSub(); //向上转型
System.out.println(sup.staticGet());
System.out.println(sup.dynamicGet());
}
}
/*
output:
Base staticGet()
Derived dynamicGet()
*/
由输出可以看出,静态方法是不具有多态性的。静态方法是与类,而非与单个的对象相关联的。
小结
简要介绍了对于多态的理解,其中存在的不足,希望各位看官不吝赐教。
参考:
《Java编程思想》第四版
《Java核心技术卷1》第九版
Java多态性理解,好处及精典实例:https://blog.csdn.net/Jian_Yun_Rui/article/details/52937791
Java——多态浅析的更多相关文章
- Java 多态——与C++的比较
学习了Java和C++之后,由于长期不使用C++,而java的基础知识掌握不牢,现在已经搞不清java多态了.现在先来谈谈java多态,稍后有时间再更新C++的多态,并进行比较~ 一. Java的多态 ...
- C++和java多态的区别
C++和java多态的区别 分类: Java2015-06-04 21:38 2人阅读 评论(0) 收藏 举报 转载自:http://www.cnblogs.com/plmnko/archive ...
- 深入理解Java多态机制
从字节码层面来看,Java中的所有方法调用,最终无外乎转换为如下几条调用指令. invokestatic: 调用静态方法. invokespecial: 调用实例构造器<init>方法,私 ...
- Java 多态 父类和子类方法的访问控制权限
Java 多态 父类和子类方法的访问控制权限 @author ixenos 父类和子类方法的访问控制权限 继承是为了扩展类的功能,而这种扩展显然就是对一个原始类的扩展,目的还是向上转型来调用,所以这就 ...
- Java多态(二)
public class ExtendsTest { public static void main(String[] args) { A a1 = new A(); A a2 = new B(); ...
- 从JVM角度看Java多态
首先,明确一下,Java多态的三个必要条件: 1. 继承 2. 子类重写父类方法 3. 父类引用指向子类对象 然后看一个例子 package test.xing; class Father{ prot ...
- 关于java多态的理解
要理解多态,就必须有一个大的理解方向,不然很容易绕进去. 首先知道多态的释义:多态性是指一个名词可以有多种语义. 对于java的多态性学习者来说,就是必须要知道多个同名方法在不同情况下的使用规则. j ...
- Java经验杂谈(2.对Java多态的理解)
多态是面向对象的重要特性之一,我试着用最简单的方式解释Java多态: 要正确理解多态,我们需要明确如下概念:・定义类型和实际类型・重载和重写・编译和运行 其中实际类型为new关键字后面的类型. 重载发 ...
- 学JAVA第十六 天,JAVA多态
今天老师讲了多态的使用 多态是同一个行为具有多个不同表现形式或形态的能力. 多态的优点: 1. 消除类型之间的耦合关系 2. 可替换性 3. 可扩充性 4. 接口性 5. 灵活性 6. 简化性 我个 ...
随机推荐
- Hibernte
什么是CRM?(了解) CRM(customer relationship management)即客户关系管理,是指企业用CRM技术来管理与客户之间的关系.在不同场合下,CRM可能是一个管理学术语, ...
- iOS 开发中单元格cell高度自适应
高度自适应分下面两种情况 1.用代码自定义的cell 用代码自定义的cell,cell高度自定义需要我们手动的去计算每个cell的字符串高度.然后返回对应的高度即可. 2.用XIB 或者 StoreB ...
- H5测试点总结-UI测试、功能测试、兼容性测试、体验相关(弱网、资源、手机操作等)、安全性测试、性能测试
一.概述 1.1 什么是H5 H5 即 HTML5,是最新的 Web 端开发语言版本,现如今,大多数手机 APP 页面会用 H5 实现,包括 PC Web 站点也会用它开发实现.所以 Web 的通用测 ...
- webpack2入门概念
webpack是一种JavaScript应用模块化打包工具,它配置起来简单易上手,因此很多企业工程化代码都使用它来打包.在具体介绍如何使用webpack之前,先来介绍下webpack的四个核心概念. ...
- 微信小程序开发---视图层(View)
WXML WXML能力: 数据绑定 列表渲染 条件渲染 模板 事件 数据绑定 数据绑定使用 Mustache 语法(双大括号)将变量包起来,可作用于内容,组件属性(需要在双引号之内),控制属性(需要在 ...
- 压力测试工具 ab
ab 是Apache 自带的一个压力测试工具,命令行,是 ApacheBench 命令的缩写. ab的原理:ab命令会创建多个并发访问线程,模拟多个访问者同时对某一URL地址进行访问.它的测试目标是基 ...
- Oracle报错ORA-12516 TNS:listener could not find available handler with matching protocol stack
解决办法定位原因-- 以sysdba身份登陆PL/SQL sqlplus / as sysdba;-- 查看当前连接进程数SQL>select count(*) from v$process;- ...
- nodejs操作mysql常见错误
1.Cannot enqueue Handshake after already enqueuing a Hand shake.这个错误提示意思是某个数据库连接已经执行了,不能进行多次连接了.遇到此类 ...
- VsCode 使用专用编程字体FiraCode
FiraCode资料:https://github.com/tonsky/FiraCode PHP代码效果如下: VsCode 配置中添加: "editor.fontFamily" ...
- Pycharm中配置鼠标悬停快速提示方法参数
第一步: 第二步: 演示: