当前位置: 首页 > news >正文

多态

为什么须要多态?

从设计方面考虑:因为现实世界中存在很多泛化的概念,比方经理说员工们去工作。而不是说技术部门的员工開始做技术类工作,销售部门的员工開始做销售的工作。为了使得计算机可以非常好的表达这样的概念而且可以体现oop的思想(以问题来解决这个问题),引入多态以及继承的概念,通过方法重载和重写以及向上转型来表达这样的现实世界中的问题。

从开发方面考虑:假设让计算机真的去具体的表述技术部门的员工開始做技术类工作,销售部门的员工開始做销售的工作。

必定类型之间耦合度(依赖性)大,这将导致代码的组织方式过于繁琐。维护成本高。

而且当得到扩展时也极其不方便。

 

什么是多态?

多态是一种设计代码的思想,一般与继承结合使用。

多态描写叙述了一种核心概念的多种不同的表现形态。又分为编译时多态和执行时多态,对于编译时多态变现有重载。是对一种行为的多种不同表现方式。执行时多态变如今“新类是现有类的一种类型”这层关系上,通过将基类的引用指向导出类的对象以重写的方式来实现。

 

深入理解多态:

一、编译时多态(重载):在编译的时候编译器可以将函数名和參数名不同的重载方法在编译后通过不同的函数名区分开来。在调用时依据限制类型实现前期绑定还是后期绑定。

二、执行时多态(向上类型转型):在编译的时候,编译器并不知道也不去检查引用指向的那个类型,他只知道这个引用是什么类型。所以检查使用的方法是否存在以及调用是否正确不过通过这个基类中的方法来检查。假设这种方法能够是导出类接口的一部分。那么就会执行后期绑定,假设这种方法是private 或者static 那么运行前期绑定。特别的,对于这个引用訪问基类中某些特别的域那一定是前期绑定。

当方法运行后期绑定时,jvm会依据对象中安置的“类型信息”通过方法调用机制找到正确的方法并绑定调用。

三、在基类方法中隐式的使用多态:

class Parent{
	Parent(){ }
	public void eat(){	
		play();
	}
	public void play(){
		System.out.println("Parent.play()");
	}
}
class Child extends Parent{
	public void play(){
		System.out.println("Child.play()");
	}
}
public class Demo{
	public static void main(String[] args) {
		Parent c = new Child();
		c.eat();
	}
}

c.eat();依照二方式确定调用那个方法之后,事实上编译器为eat方法隐士的传入了一个this指针指向了new Child(),可是this类型为Parent(由于调用了Parenteat()),所以这里有一个隐式 的向上类型转化。在eat()内部有一个play()调用,编译器知道这是this.paly()的形式 ,要去推断这种方法该调用谁的play()。

 

多态有哪些优点?

1、多态消除了类型之间的耦合关系。使得只与核心概念基类相关联。

2、多态具有非常强的可扩展性。能够随时加入新的导出类。

3、多态改善代码的组织结构和可读性使得改变事物与不改变事物分离。

多态有什么缺点或者缺陷?

1、导出类中接口的扩展部分不能被基类訪问。向上转型导致某些方法的丢失。

2、基类私有方法得不到重写,所以仅仅能调用基类的这种方法。

3、域与静态方法不支持多态。

4、继承与多态使用,将类的调用卷入到继承的层次结构(耦合度)中去。在基类中使用了导出类的方法,可是这个导出类域没有初始值。

(具体见下一篇。继承与多态的初始化和清除)



实例:



class SuperParent {
	SuperParent(){
		System.out.println("SuperParent:---> ");
		this.f();
	}
	 void f(){
		System.out.println("SuperParent");
	}
}

class Parent extends SuperParent{
	Parent(){
		System.out.println("Parent:--->");
		this.f();
		super.f();
	}
	void f(){
		System.out.println("Parent");
	}
}

class Child extends Parent{
	public Child() {
		System.out.println("Child:--->");
		this.f();
		super.f();
	}
	void f(){
		System.out.println("Child");
	}
}

public class Demo{

	public static void main(String[] args) {
		Child c = new Child();
	}

	
}

/*Output:

SuperParent:--->

Child

Parent:--->

Child

SuperParent

Child:--->

Child

Parent

*/




 编译器编译SuperParent时,遇到this.f()调用。此时f函数是一个一般的方法,所以y这个this应该是invokevirtual,所以应该是在后期绑定的时候由jvm推断究竟调用那个f方法。

  接着编译Parent方法。同理处理构造器中this.f( )。

可是对于super来说。这个对于编译器是确定的就是其基类,不会待执行的时候有什么后期绑定,所以应该是invokespecial的。同理分析Child。

 

多态的应用:

1. 多态用于形參类型的时候,能够接收很多其它类型的数据 。

  避免了因基类导出的多个子类在调用各自的某一方法时要多写代码。更能体现出继承的关系,同一时候利用向上转型能够使得多个子类在调用方法功能同样行为不同的方法出现冗余,同一时候再加入新类。也变得灵活。提高的可扩展性。

 

2. 多态用于返回值类型的时候,能够返回很多其它类型的数据。

//图形类
abstract class MyShape{

	public abstract void getArea();

	public abstract void getLength();	
}



class Circle extends MyShape{

	public static final double PI = 3.14;

	double r;

	public Circle(double r){
		this.r =r ;	
	}

	public  void getArea(){
		System.out.println("圆形的面积:"+ PI*r*r);
	}

	public  void getLength(){
		System.out.println("圆形的周长:"+ 2*PI*r);
	}
}


class Rect  extends MyShape{

	int width;

	int height;

	public Rect(int width , int height){
		this.width = width;
		this.height = height;
	}

	public  void getArea(){
		System.out.println("矩形的面积:"+ width*height);
	}

	public  void getLength(){
		System.out.println("矩形的周长:"+ 2*(width+height));
	}
}



class Demo12 {

	public static void main(String[] args) 
	{
		/*
		//System.out.println("Hello World!");
		Circle c = new Circle(4.0);
		print(c);

		Rect r = new Rect(3,4);
		print(r);
		*/

		MyShape m = getShape(0); //调用了使用多态的方法。定义的变量类型要与返回值类型一致。
		m.getArea();
		m.getLength();
		

	}

	//需求1: 定义一个函数能够接收随意类型的图形对象,而且打印图形面积与周长。

public static void print(MyShape s){ // MyShpe s = new Circle(4.0); s.getArea(); s.getLength(); } // 需求2: 定义一个函数能够返回随意类型的图形对象。

public static MyShape getShape(int i){ if (i==0){ return new Circle(4.0); }else{ return new Rect(3,4); } } }



相关文章:

  • 个人站点的日期查询
  • 2017-2018年度Scrum现状报告发布
  • 我们的春节--2019
  • BZOJ 1412 狼和羊的故事
  • LeetCode29.两数相除 JavaScript
  • vim命令模式下光标移动+查找
  • Fastjson的基本使用方法大全
  • 面孔相册按脸给照片分类 这是靠小米人脸检测技术实现的
  • 数据结构java版之冒泡排序及优化
  • 洛谷1474货币系统——小心重复的完全背包
  • 博弈论入门之斐波那契博弈
  • 工程优化暨babel升级小记
  • poj 3280【区间dp】
  • iOS 9以上系统 信任的企业级开发者证书
  • RxJS 实现摩斯密码(Morse) 【内附脑图】
  • 5、React组件事件详解
  • CAP理论的例子讲解
  • css属性的继承、初识值、计算值、当前值、应用值
  • flutter的key在widget list的作用以及必要性
  • leetcode讲解--894. All Possible Full Binary Trees
  • Markdown 语法简单说明
  • mongo索引构建
  • npx命令介绍
  • PHP面试之三:MySQL数据库
  • REST架构的思考
  • Service Worker
  • vue数据传递--我有特殊的实现技巧
  • 道格拉斯-普克 抽稀算法 附javascript实现
  • 给自己的博客网站加上酷炫的初音未来音乐游戏?
  • 每天10道Java面试题,跟我走,offer有!
  • 前端工程化(Gulp、Webpack)-webpack
  • 前端之Sass/Scss实战笔记
  • 如何合理的规划jvm性能调优
  • 使用 Docker 部署 Spring Boot项目
  • 它承受着该等级不该有的简单, leetcode 564 寻找最近的回文数
  • 微服务核心架构梳理
  • 项目管理碎碎念系列之一:干系人管理
  •  一套莫尔斯电报听写、翻译系统
  • SAP CRM里Lead通过工作流自动创建Opportunity的原理讲解 ...
  • 关于Kubernetes Dashboard漏洞CVE-2018-18264的修复公告
  • $.extend({},旧的,新的);合并对象,后面的覆盖前面的
  • (3)(3.2) MAVLink2数据包签名(安全)
  • (cos^2 X)的定积分,求积分 ∫sin^2(x) dx
  • (C语言)编写程序将一个4×4的数组进行顺时针旋转90度后输出。
  • (八)Docker网络跨主机通讯vxlan和vlan
  • (十)c52学习之旅-定时器实验
  • (十五)使用Nexus创建Maven私服
  • (一)appium-desktop定位元素原理
  • (一)pytest自动化测试框架之生成测试报告(mac系统)
  • (转)EOS中账户、钱包和密钥的关系
  • (转)winform之ListView
  • (转)大型网站架构演变和知识体系
  • .bat批处理(五):遍历指定目录下资源文件并更新
  • .naturalWidth 和naturalHeight属性,
  • .NET 8 编写 LiteDB vs SQLite 数据库 CRUD 接口性能测试(准备篇)