arvinljw

  • 主页
  • Android
  • Java
  • Python
  • 技术杂谈
所有文章 关于我

arvinljw

  • 主页
  • Android
  • Java
  • Python
  • 技术杂谈

Java之多态

2018-06-12

Java语言的三大特征正是封装、继承以及多态,在前边总结了一切皆对象和复用类两篇,正是封装以及继承。三者的作用如下:

  • 封装:通过合并特征和行为而创建新的数据类型
  • 继承:复用已有接口,将接口与实现分离
  • 多态:消除类型之间的耦合

向上转型

在复用类的一章中其实就有接触到向上转型,表示为对某个对象的引用视为对其父类的引用。而向上转型可能会缩小接口范围,但是至少也是父类接口的大小。正因为此,便可节省大量的重复性代码。例如书中的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class Chapter8 {
private static void tune(Instrument instrument) {
instrument.play();
}

public static void main(String[] args) {
tune(new Wind());
tune(new Stringed());
tune(new Brass());
}

static class Instrument {
public void play() {
print("Instrument.play()");
}

void print(String msg) {
System.out.println(msg);
}
}

static class Wind extends Instrument {
@Override
public void play() {
print("Wind.play()");
}
}

static class Stringed extends Instrument {
@Override
public void play() {
print("Stringed.play()");
}
}

static class Brass extends Instrument {
@Override
public void play() {
print("Brass.play()");
}
}
}

在调用tune方法时,如果不使用向上转型,那么传入的参数是什么类型就要写成什么类型,这里的乐器有三种就要重载三个tune方法(如下),而且每新增一种乐器就要重载一个新的tune方法,这样修改起来就会很麻烦,也容易出错。而这向上转型正是多态允许的,当然也是继承的一个作用。

1
2
3
4
5
6
7
8
9
10
11
private static void tune(Wind wind) {
wind.play();
}

private static void tune(Stringed stringed) {
stringed.play();
}

private static void tune(Brass brass) {
brass.play();
}

说到这里可能会好奇,这是怎么实现向上转型后,依旧能调用到正确的方法的呢?这正是接下来要说的方法调用绑定。

方法调用绑定

方法的绑定分为前期绑定和后期绑定。

  • 绑定:一个方法的调用和一个方法的主体关联的过程就叫绑定
  • 前期绑定:程序执行钱惊醒绑定
  • 后期绑定:在程序运行时更具对象类型进行绑定

正是这个后期绑定为向上转型后依旧能正确调用方法主体提供了解决办法,在Java中除了static和final的方法外都是后期绑定。因为这些在继承中是不能够被重写的,所以也就有了将方法申明为final能提高一定效率的说法。但是建议使用final关键字是根据设计理念出发,而不是提高效率而言,因为这对程序本身提高的效率不是很大。

有了多态机制(向上转型-后期绑定),在程序设计的过程中就能更好的扩展,在父类中新增新的方法,也能在调用父类的接口中,不用修改就能照旧使用,并使用根据父类派生出来的子类。做到了将改变的事物与不变的事物相分离。

上文中说到除了static和final的方法外都是后期绑定,所以private的方法和static的域和方法都是无能被覆盖的,无法被重写,如果方法或字段名相同,那么就会容易让人混淆,所以最好采用不同的名字。

构造器与多态

初始化顺序

在之前的文章中,我总结出了一个类的初始化过程中,各个位置的变量、静态变量以及构造器的调用顺序,也是需要在这章中理解的,顺序如下:

父类静态变量>子类静态变量>父类变量>父类构造函数>子类变量>子类构造函数

同一个类中同一级别的调用顺序和代码顺序有关

为什么先初始化父类

子类被创建为什么要先调用父类的构造器呢?是因为子类只能访问父类的public和protected的字段和方法,而private的字段和方法无法直接访问,那么如果使用子类去初始化这个类,那么就会发现private的部分就无法被初始化,所以private的部分只能父类去初始化,这样才能保证在子类中要使用到父类的内容时,都是被初始化了的。

注:这里的初始化并非只数据的初始化,更像是某个事物被创建,即使被创建后是null。就像某个事物不存在到存在的过程

在父类的构造器中调用被重写的方法,若该方法中调用了子类的字段,则会导致,该字段还未被初始化,只有一个默认值。所以应该尽可能避免在构造器中调用能被重写的方法。

还有一点,重写的方法有返回值,那么返回值的类型可以是父类方法返回类型的子类。

总结

多态,在设计过程中,核心就是使用继承的方式,而对于设计程序,应该更多考虑组合的形式,除非你确定这两个类的关系是”is-a”的关系而不是”is-like-a”。

可以看到多态是在封装和继承的基础上存在的特性,这些特性便是程序设计的基石,只有多见识如何设计,多实践才能更好的用好这些特性,写出更好维护和扩展的代码。

  • Java
  • 《Thinking in Java》

扫一扫,分享到微信

微信分享二维码
Java之接口和内部类
Python生成词云
  1. 1. 向上转型
  2. 2. 方法调用绑定
  3. 3. 构造器与多态
    1. 3.1. 初始化顺序
    2. 3.2. 为什么先初始化父类
  4. 4. 总结
Like Issue Page
Error: Comments Not Initialized
Login with GitHub
Styling with Markdown is supported
Powered by Gitment
© 2019 arvinljw
Hexo Theme Yilia by Litten
  • 所有文章
  • 关于我

tag:

  • Android
  • 项目经验
  • Java
  • 《Effective Java》
  • 开源库
  • 源码分析
  • 《Thinking in Java》
  • 技术杂谈
  • sql
  • FFmpeg交叉编译
  • Python
  • Scrapy爬虫
  • 数据分析
  • git操作

    缺失模块。
    1、请确保node版本大于6.2
    2、在博客根目录(注意不是yilia根目录)执行以下命令:
    npm i hexo-generator-json-content --save

    3、在根目录_config.yml里添加配置:

      jsonContent:
        meta: false
        pages: false
        posts:
          title: true
          date: true
          path: true
          text: false
          raw: false
          content: false
          slug: false
          updated: false
          comments: false
          link: false
          permalink: false
          excerpt: false
          categories: false
          tags: true
    

  • Mac下交叉编译FFmpeg3.4.5出Android的so包

    2019-01-10

    #技术杂谈#FFmpeg交叉编译

  • Android开源之PermissionHelper

    2018-09-19

    #Android#开源库

  • Java之类和接口

    2018-09-03

    #Java#《Effective Java》

  • Android JNI开发系列之Java与C相互调用

    2018-08-31

    #Android

  • Android JNI开发系列之配置

    2018-08-29

    #Android

  • Android学习之RecyclerView.ItemDecoration实践

    2018-07-26

    #Android#开源库

  • MySQL基础语句

    2018-07-18

    #技术杂谈#sql

  • Android学习之联系人列表

    2018-07-12

    #Android

  • git常用操作

    2018-07-04

    #技术杂谈#git操作

  • Android学习之ConstraintLayout

    2018-06-26

    #Android

  • Java之字符串

    2018-06-25

    #Java#《Thinking in Java》

  • Java之类的通用方法

    2018-06-23

    #Java#《Effective Java》

  • Python抓取QQ音乐歌单并分析

    2018-06-20

    #Python#Scrapy爬虫#数据分析

  • Java之接口和内部类

    2018-06-14

    #Java#《Thinking in Java》

  • Java之多态

    2018-06-12

    #Java#《Thinking in Java》

  • Python生成词云

    2018-06-11

    #Python

  • Java之复用类

    2018-06-06

    #Java#《Thinking in Java》

  • Java之操作符、流程控制、初始化和清理以及访问权限控制

    2018-06-05

    #Java#《Thinking in Java》

  • Java之对象的创建和销毁

    2018-06-03

    #Java#《Effective Java》

  • Java之一切皆对象

    2018-05-30

    #Java#《Thinking in Java》

  • Android源码分析之PhotoView

    2017-12-28

    #Android#源码分析

  • Android开源之SocialHelper

    2017-12-01

    #Android#开源库

  • Android动态更换桌面图标

    2017-10-10

    #Android

  • Android之ViewPager使用

    2017-06-05

    #Android

  • Android代码混淆

    2017-04-10

    #Android

  • Android消息机制

    2017-02-20

    #Android

  • Android之Activity总结

    2016-11-19

    #Android

热爱Android开发
喜欢学习新技术...

有问题可以给我发邮件~