转自:http://android.blog.51cto.com/268543/53181

什么是多态?它的实现机制是什么呢?重载和重写的区别在那里?这就是这一次我们要回顾的四个十分重要的概念:继承、多态、重载和重写。

继承(inheritance)

简单的说,继承就是在一个现有类型的基础上,通过增加新的方法或者重定义已有方法(下面会讲到,这种方式叫重写)的方式,产生一个新的类型。继承是面向对象的三个基本特征--封装、继承、多态的其中之一,我们在使用JAVA时编写的每一个类都是在继承,因为在JAVA语言中,java.lang.Object类是所有类最根本的基类(或者叫父类、超类),如果我们新定义的一个类没有明确地指定继承自哪个基类,那么JAVA就会默认为它是继承自Object类的。

我们可以把JAVA中的类分为以下三种:

  1. 类:使用class定义且不含有抽象方法的类。
  2. 抽象类:使用abstract class定义的类,它可以含有,也可以不含有抽象方法。
  3. 接口:使用interface定义的类。

在这三种类型之间存在下面的继承规律:

  • 类可以继承(extends)类,可以继承(extends)抽象类,可以继承(implements)接口。
  • 抽象类可以继承(extends)类,可以继承(extends)抽象类,可以继承(implements)接口。
  • 接口只能继承(extends)接口。

请注意上面三条规律中每种继承情况下使用的不同的关键字extends和implements,它们是不可以随意替换的。大家知道,一个普通类继承一个接口后,必须实现这个接口中定义的所有方法,否则就只能被定义为抽象类。我在这里之所以没有对implements关键字使用“实现”这种说法是因为从概念上来说它也是表示一种继承关系,而且对于抽象类implements接口的情况下,它并不是一定要实现这个接口定义的任何方法,因此使用继承的说法更为合理一些。

以上三条规律同时遵守下面这些约束:

  1. 类和抽象类都只能最多继承一个类,或者最多继承一个抽象类,并且这两种情况是互斥的,也就是说它们要么继承一个类,要么继承一个抽象类。
  2. 类、抽象类和接口在继承接口时,不受数量的约束,理论上可以继承无限多个接口。当然,对于类来说,它必须实现它所继承的所有接口中定义的全部方法。
  3. 抽象类继承抽象类,或者实现接口时,可以部分、全部或者完全不实现父类抽象类的抽象(abstract)方法,或者父类接口中定义的接口。
  4. 类继承抽象类,或者实现接口时,必须全部实现父类抽象类的全部抽象(abstract)方法,或者父类接口中定义的全部接口。

继承给我们的编程带来的好处就是对原有类的复用(重用)。就像模块的复用一样,类的复用可以提高我们的开发效率,实际上,模块的复用是大量类的复用叠加后的效果。除了继承之外,我们还可以使用组合的方式来复用类。所谓组合就是把原有类定义为新类的一个属性,通过在新类中调用原有类的方法来实现复用。如果新定义的类型与原有类型之间不存在被包含的关系,也就是说,从抽象概念上来讲,新定义类型所代表的事物并不是原有类型所代表事物的一种,比如黄种人是人类的一种,它们之间存在包含与被包含的关系,那么这时组合就是实现复用更好的选择。下面这个例子就是组合方式的一个简单示例:

  1. public class Sub {
  2. private Parent p = new Parent();
  3. public void doSomething() {
  4. // 复用Parent类的方法
  5. p.method();
  6. // other code
  7. }
  8. }
  9. class Parent {
  10. public void method() {
  11. // do something here
  12. }
  13. }

当然,为了使代码更加有效,我们也可以在需要使用到原有类型(比如Parent p)时,才对它进行初始化。

使用继承和组合复用原有的类,都是一种增量式的开发模式,这种方式带来的好处是不需要修改原有的代码,因此不会给原有代码带来新的BUG,也不用因为对原有代码的修改而重新进行测试,这对我们的开发显然是有益的。因此,如果我们是在维护或者改造一个原有的系统或模块,尤其是对它们的了解不是很透彻的时候,就可以选择增量开发的模式,这不仅可以大大提高我们的开发效率,也可以规避由于对原有代码的修改而带来的风险。

多态(Polymorphism)

多态是又一个重要的基本概念,上面说到了,它是面向对象的三个基本特征之一。究竟什么是多态呢?我们先看看下面的例子,来帮助理解:

  1. //汽车接口
  2. interface Car {
  3. // 汽车名称
  4. String getName();
  5. // 获得汽车售价
  6. int getPrice();
  7. }
  8. // 宝马
  9. class BMW implements Car {
  10. public String getName() {
  11. return "BMW";
  12. }
  13. public int getPrice() {
  14. ;
  15. }
  16. }
  17. // 奇瑞QQ
  18. class CheryQQ implements Car {
  19. public String getName() {
  20. return "CheryQQ";
  21. }
  22. public int getPrice() {
  23. ;
  24. }
  25. }
  26. // 汽车出售店
  27. public class CarShop {
  28. // 售车收入
  29. ;
  30. // 卖出一部车
  31. public void sellCar(Car car) {
  32. System.out.println("车型:" + car.getName() + "  单价:" + car.getPrice());
  33. // 增加卖出车售价的收入
  34. money += car.getPrice();
  35. }
  36. // 售车总收入
  37. public int getMoney() {
  38. return money;
  39. }
  40. public static void main(String[] args) {
  41. CarShop aShop = new CarShop();
  42. // 卖出一辆宝马
  43. aShop.sellCar(new BMW());
  44. // 卖出一辆奇瑞QQ
  45. aShop.sellCar(new CheryQQ());
  46. System.out.println("总收入:" + aShop.getMoney());
  47. }
  48. }

运行结果:

  1. 车型:BMW 单价:300000
  2. 车型:CheryQQ 单价:20000
  3. 总收入:320000

继承是多态得以实现的基础。从字面上理解,多态就是一种类型(都是Car类型)表现出多种状态(宝马汽车的名称是BMW,售价是300000;奇瑞汽车的名称是CheryQQ,售价是2000)。将一个方法调用同这个方法所属的主体(也就是对象或类)关联起来叫做绑定,分前期绑定和后期绑定两种。下面解释一下它们的定义:

  1. 前期绑定:在程序运行之前进行绑定,由编译器和连接程序实现,又叫做静态绑定。比如static方法和final方法,注意,这里也包括private方法,因为它是隐式final的。
  2. 后期绑定:在运行时根据对象的类型进行绑定,由方法调用机制实现,因此又叫做动态绑定,或者运行时绑定。除了前期绑定外的所有方法都属于后期绑定。

多态就是在后期绑定这种机制上实现的。多态给我们带来的好处是消除了类之间的耦合关系,使程序更容易扩展。比如在上例中,新增加一种类型汽车的销售,只需要让新定义的类继承Car类并实现它的所有方法,而无需对原有代码做任何修改,CarShop类的sellCar(Car car)方法就可以处理新的车型了。新增代码如下:

  1. // 桑塔纳汽车
  2. class Santana implements Car {
  3. public String getName() {
  4. return "Santana";
  5. }
  6. public int getPrice() {
  7. ;
  8. }
  9. }

重载(overloading)和重写(overriding)

重载和重写都是针对方法的概念,在弄清楚这两个概念之前,我们先来了解一下什么叫方法的型构(英文名是signature,有的译作“签名”,虽然它被使用的较为广泛,但是这个翻译不准确的)。型构就是指方法的组成结构,具体包括方法的名称和参数,涵盖参数的数量、类型以及出现的顺序,但是不包括方法的返回值类型,访问权限修饰符,以及abstract、static、final等修饰符。比如下面两个就是具有相同型构的方法:

  1. public void method(int i, String s) {
  2. // do something
  3. }
  4. public String method(int i, String s) {
  5. // do something
  6. }

而这两个就是具有不同型构的方法:

  1. public void method(int i, String s) {
  2. // do something
  3. }
  4. public void method(String s, int i) {
  5. // do something
  6. }

了解完型构的概念后我们再来看看重载和重写,请看它们的定义:

  • 重写,英文名是overriding,是指在继承情况下,子类中定义了与其基类中方法具有相同型构的新方法,就叫做子类把基类的方法重写了。这是实现多态必须的步骤。
  • 重载,英文名是overloading,是指在同一个类中定义了一个以上具有相同名称,但是型构不同的方法。在同一个类中,是不允许定义多于一个的具有相同型构的方法的。

我们来考虑一个有趣的问题:构造器可以被重载吗?答案当然是可以的,我们在实际的编程中也经常这么做。实际上构造器也是一个方法,构造器名就是方法名,构造器参数就是方法参数,而它的返回值就是新创建的类的实例。但是构造器却不可以被子类重写,因为子类无法定义与基类具有相同型构的构造器。

本文出自 “臧圩人·海纳百川” 博客,转载请与作者联系!

[Java]重载,重写以及继承,多态的区别的更多相关文章

  1. java面向对象(封装-继承-多态)

    框架图 理解面向对象 面向对象是相对面向过程而言 面向对象和面向过程都是一种思想 面向过程强调的是功能行为 面向对象将功能封装进对象,强调具备了功能的对象. 面向对象是基于面向过程的. 面向对象的特点 ...

  2. java类的封装 继承 多态

    1.猜数字小游戏 package cn.jiemoxiaodi_02; import java.util.Scanner; /** * 猜数字小游戏 * * @author huli * */ pub ...

  3. Java三大特性(封装,继承,多态)

    Java中有三大特性,分别是封装继承多态,其理念十分抽象,并且是层层深入式的. 一.封装 概念:封装,即隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别:将抽象得到的数据 ...

  4. Java基础——面向对象(封装——继承——多态 )

    对象 对象: 是类的实例(实现世界中 真 实存在的一切事物 可以称为对象) 类: 类是对象的抽象描述 步骤: 1.定义一个类 (用于 描述人:) ( * 人:有特征和行为) 2.根据类 创建对象 -- ...

  5. Java基础笔记-抽象,继承,多态

    抽象类: abstract修饰 抽象方法必须定义在抽象类中,抽象类不能创建对象. 在抽象方法中可以不定义抽象方法,作用是:让该类不能建立对象. 特点是: 1.定义在抽象类中 2.方法和类都用abstr ...

  6. Java重载重写与实现方法的规则

    这几天在实训过程中做到了一个题,这个题目问的是 非抽象类实现接口后,必须实现接口中的所有抽象方法,除了abstract外,方法头必须完全一致.这句话是错误的.然后在做错以后自己总结一下重载 重写 和实 ...

  7. Java基础语法<八> 继承 多态 抽象 反射

    1.超类和子类 超类和子类 父类与子类 多态: 一个对象变量可以指示多种实际类型的现象称为多态 一个变量可以引用父类对象,也可以引用其子类对象,这就是多态. 不能将一个超类的引用赋给子类变量,因为调用 ...

  8. Java自学-接口与继承 多态

    Java的多态 操作符的多态 +可以作为算数运算,也可以作为字符串连接 类的多态 父类引用指向子类对象 示例 1 : 操作符的多态 同一个操作符在不同情境下,具备不同的作用 如果+号两侧都是整型,那么 ...

  9. [java核心技术01]__继承与多态、重载与重写、抽象类与接口

    前言 前面简单学习了面向对象的知识,知道了其两个重要的特性,继承与多态,今天就围绕着面向对象的这两个特性,将继承与多态及相关的几个几个定义重载与重写,抽象类与接口的相关知识具体学习一下. 类的继承 关 ...

随机推荐

  1. 关于JQuery 中$.ajax函数利用jsonp实现Ajax跨域请求ASP.NET的WebService成功获取数据的案例

    部署环境:Window 7 SP1+IIS7 成功方案: 其成功解决问题的几个重要因素如下: 1.       由于WebService默认不支持Get请求,所以要在Web.config配置文件内的& ...

  2. 关于GC进行垃圾回收的时机

    前言 今天查看一个同事的代码,发现代码中多处地方使用了GC.Collect()方法,我问他为什么这么做,他说感觉程序中定义了好多变量,怕GC回收不及时,用GC.Collect()可以手动掌控GC进行垃 ...

  3. OC加强-day05

    #program mark - 0_11 NSRange结构体介绍 [掌握] 是Foundation框架中的一个结构体 NSRange 定义的一个变量的两个属性 location:起始下标 lengt ...

  4. 对比iOS中的四种数据存储

    来自于大牛的文章给大家分享下 :http://www.infoq.com/cn/articles/data-storage-in-ios/

  5. ES6的编码风格

    编程风格 [转自http://es6.ruanyifeng.com/#docs/style] 块级作用域 字符串 解构赋值 对象 数组 函数 Map结构 Class 模块 ESLint的使用 本章探讨 ...

  6. 第3章 Struts2框架--2、完整的Struts2框架应用实例

    1.建立一个Dynamic Web project,项目名:UserManager,把Struts2所必需的JAR复制到项目WEB-INF/lib目录下 2.修改web.xml文件,在web.xml文 ...

  7. webui layout like desktop rich client

    similarity similarlike desktop js frameworklike extj js frameworklike rich client js frameworkjs lay ...

  8. 管理员把我的admin权限去掉了,那么如何获得jdk zip安装呢?这篇可以帮你。

    JDK is not available as a portable zip unfortunately. However, you can: Create working JDK directory ...

  9. uglifyjs 压缩js

    第一步 安装nodejs dos下执行 node -v npm -v

  10. Django 创建第一个项目(转)

    转自(http://www.runoob.com/django/django-first-app.html) 前面写了不少python程序,由于之前都是作为工具用,所以命令行就足够了,最近写的测试用例 ...