Android知识体系梳理(3)-Java基础篇-OOP的理解、继承、封装、多态以及面试会问什么

四、继承、封装、多态

今天我们开始讲这个面试时老生常谈的问题。什么是继承?什么是封装?什么是多态?

我们一步步往前走:

对象?我们现实世界中,一切物体,一切能看得到摸得着的实体,都可以视之为一个对象。而我们java中也是一样,只不过java中的对象是一个逻辑实体,它可以和现实进行对应,也可以是一个虚拟的概念。

比如我们有一支笔,它在现实生活中是一个对象。那么接下来,我们把它映射到java的世界中:

public class Pen {

    private float length;

    private float color;

    public float getLength() {
        return length;
    }

    public void setLength(float length) {
        this.length = length;
    }

    public float getColor() {
        return color;
    }

    public void setColor(float color) {
        this.color = color;
    }
}

可以见到,我们新建了一个笔的对象。它跟其他的笔一样,都至少是有两个属性的,一个是它的长度,一个是它的颜色。这样,我们是不是就把它和现实中的这支笔关联了起来呢。

我们知道如何建立一个类之后,接下来就是java面向对象的核心概念,继承:当一个对象获取父对象的所有属性和行为时,称为继承。它提供代码可重用性,它用于实现运行时多态性。在java中使用extends关键字来实现继承:

public class Pencil extends Pen {

    @Override
    public float getLength() {
        return length - currentTime;
    }

    @Override
    public void setLength(float length) {
        super.setLength(length);
    }

    @Override
    public float getColor() {
        return super.getColor();
    }

    @Override
    public void setColor(float color) {
        super.setColor(color);
    }
}

这里我们看到,我们新建了一个类,继承了Pen,这个类代表了Pencil。是不是很形象?笔是这一类实体的总称,而铅笔只是其中一种。我们可以通过继承笔的一些特性,同时也可以覆写笔的一些方法。那么假设,我们有两支笔,一只是铅笔,另一只是钢笔呢?他们有着共同的特质,但又有着不同的点。那么这就要讲到java的多态:

public class FountainPen extends Pen {

    @Override
    public float getLength() {
        return length;
    }

    @Override
    public void setLength(float length) {
        super.setLength(length);
    }

    @Override
    public float getColor() {
        return super.getColor();
    }

    @Override
    public void setColor(float color) {
        super.setColor(color);
    }
}

细心的同学应该已经发现了差别,铅笔中,它的长度是随着时间递减的,因为铅笔会越用越短。而钢笔呢,我们发现他的长度是与时间无关的。这就是我们常说的多态。他们都继承自Pen,但他们又各自重写了获取长度的方法。好,相信大家这时已经明白了对象、继承和多态的意义。

我们理解了上面的概念之后,我们再回过头来看第一段代码。我们发现,它的长度length,颜色color两个属性,都是private私有的。我们在外部新建一个Pen对象后,并不能直接对这两个属性进行操作或者访问,这时,我们就只能通过Pen实例暴露出来的set或者get方法来实现操作和访问。

 private void showDifferent(){
        Pen pen = new Pen();
        //pen.color = 2f;
        pen.setColor(2f);
    }

我们发现,我们做到了将颜色和长度甚至是其他各种各样的属性整合在一起,成为一个java类,而外部不能直接对它的属性进行直接操作,只能通过Pen对象暴露出来的具体方法达到一定的目的。这是不是就好像我们把一堆糖果包装了起来,并且不让别人获得?这个时候,如果外部需要拿你的糖果,是不是只能通过你的手,让你拿给他们?而这个让我们感觉到安全的模式,我们称之为封装。

关键问题?

1、什么是抽象类?什么是接口?抽象类和接口有什么区别?

首先,抽象类和抽象方法都使用 abstract 关键字进行声明,如果一个类中包含抽象方法,那么这个类必须声明为抽象类。我们在前面已经能看到什么是类,我们可以通过一个类实例化成一个对象。那抽象类和普通类最大的区别是,抽象类不能被实例化,只能被继承。为了方便理解,我们再举一个例子:普通类实例化获得一个对象,那么普通类就好像是一个实际的生产者,可以通过它不断地生产新的对象,而抽象类,就好比是一张图纸,它不能从事建造,只是定义了一部分的建造规范和特征。我们必须要有一个生产者继承自这张图纸,才能把图纸里面的内容发挥出来。

接口是抽象类的延伸,在 Java 8 之前,它可以看成是一个完全抽象的类,也就是说它不能有任何的方法实现。从 Java 8 开始,接口也可以拥有默认的方法实现,这是因为不支持默认方法的接口的维护成本太高了。在 Java 8 之前,如果一个接口想要添加新的方法,那么要修改所有实现了该接口的类,让它们都实现新增的方法。接口的成员(字段 + 方法)默认都是 public 的,并且不允许定义为 private 或者 protected。接口的字段默认都是 static 和 final 的。

比较:从设计层面上看,抽象类提供了一种 IS-A 关系,需要满足里式替换原则,即子类对象必须能够替换掉所有父类对象。而接口更像是一种 LIKE-A 关系,它只是提供一种方法实现契约,并不要求接口和实现接口的类具有 IS-A 关系。从使用上来看,一个类可以实现多个接口,但是不能继承多个抽象类。接口的字段只能是 static 和 final 类型的,而抽象类的字段没有这种限制。接口的成员只能是 public 的,而抽象类的成员可以有多种访问权限。

那么该如何选择:在很多情况下,接口优先于抽象类。因为接口没有抽象类严格的类层次结构要求,可以灵活地为一个类添加行为。并且从 Java 8 开始,接口也可以有默认的方法实现,使得修改接口的成本也变的很低。使用接口的话,需要让不相关的类都实现一个方法,例如不相关的类都可以实现 Compareable 接口中的 compareTo() 方法;需要使用多重继承。使用抽象类的话,需要在几个相关的类中共享代码。需要能控制继承来的成员的访问权限,而不是都为 public。需要继承非静态和非常量字段。

2、开闭原则

开闭原则的命名被应用在两种方式上。这两种方式都使用了继承来解决明显的困境,但是它们的目的,技术以及结果是不同的。

梅耶开闭原则这一想法认为一旦完成,一个类的实现只应该因错误而修改,新的或者改变的特性应该通过新建不同的类实现。新建的类可以通过继承的方式来重用原类的代码。衍生的子类可以或不可以拥有和原类相同的接口。

多态开闭原则的定义倡导对抽象基类的继承。接口规约可以通过继承来重用,但是实现不必重用。已存在的接口对于修改是封闭的,并且新的实现必须,至少,实现那个接口。

3、动态绑定

动态绑定(后期绑定)是指:在程序运行过程中,根据具体的实例对象才能具体确定是哪个方法。例子:我们假设Father ft = newSon();ft.say();Father中包含say方法,Son继承自Father,重写了say(),此时执行Son中重写的say方法。向上转型时,用父类引用执行子类对象,并可以用父类引用调用子类中重写了的同名方法。但是不能调用子类中新增的方法。

4、里氏替换原则

定义:任何基类可以出现的地方,子类一定可以出现。

5、重写和重载

符合里氏替换重写有以下三个限制:子类方法的访问权限必须大于等于父类方法;子类方法的返回类型必须是父类方法返回类型或为其子类型。子类方法抛出的异常类型必须是父类抛出异常类型或为其子类型。

重写和继承会导致一个调用权限的问题。java的实现机制是:比如在调用A类的一个方法时,先从A类中查找看是否有对应的方法,如果没有再到A的父类中查看,看是否从父类继承来。否则就要对参数进行转型,比如参数是对象B,那么B转成父类之后看A类中是否有对应的方法,没有的话再去A的父类中查看。总的来说,方法调用的优先级为:

1.this.func(this) 2.super.func(this) 3.this.func(super) 4.super.func(super)

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值