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. 简化性 我个 ...
随机推荐
- java中class文件与jar文件
1. JAR 文件包 JAR 文件就是 Java Archive File,顾名思意,它的应用是与 Java 息息相关的,是 Java 的一种文档格式.JAR 文件非常类似 ZIP 文件——准确的说, ...
- Linux 结构化命令
if -then 语句 if -then 语句有如下格式 if command then commands f i bash shell 的if语句会先运行if后面的那个命令,如果改命令的退出状态码是 ...
- svn 安装及更新web库
安装: apt-get update //更新apt-get库 apt-get install subversion 安装SVN mkdir /home/svn ...
- ubuntu Nvidia driver install
在图形界面中,有软件和更新,可以使用附加驱动来更新 最上面的驱动是最新版本,英伟达目前Linux最新的版本是375.39 后面的括号,专有意思是代表英伟达自家的驱动,不开源 选择好之后点击应用更改 关 ...
- Java提高篇(一):区分引用变量与对象
我们有代码: New A=new New(); 下面是这个New的类: class New { public New() { System.out.println("这是New类当中的构造方 ...
- #Java学习之路——基础阶段(第五篇)
我的学习阶段是跟着CZBK黑马的双源课程,学习目标以及博客是为了审查自己的学习情况,毕竟看一遍,敲一遍,和自己归纳总结一遍有着很大的区别,在此期间我会参杂Java疯狂讲义(第四版)里面的内容. 前言: ...
- [Swift]LeetCode605. 种花问题 | Can Place Flowers
Suppose you have a long flowerbed in which some of the plots are planted and some are not. However, ...
- [Swift]LeetCode715. Range 模块 | Range Module
A Range Module is a module that tracks ranges of numbers. Your task is to design and implement the f ...
- python 模拟豆瓣登录(豆瓣6.0)
最近在学习python爬虫,看到网上有很多关于模拟豆瓣登录的例子,随意找了一个试了下,发现不能运行,对比了一下代码和豆瓣网站,发现原来是豆瓣网站做了修改,增加了反爬措施. 首先看下要模拟登录的网站: ...
- 【Redis篇】Redis持久化方式AOF和RDB
一.前述 持久化概念:将数据从掉电易失的内存存放到能够永久存储的设备上. Redis持久化方式RDB(Redis DB) hdfs: fsimageAOF(AppendOnlyFile) ...