zoukankan      html  css  js  c++  java
  • 浅析接口与抽象类的设计理念、extends与implements的区别、继承向上转型及上转具体过程

    一、接口与抽象类的设计理念

      不同点的意义:抽象类是对对象的抽象;接口是实现功能的封装

      其本身的设计目的就是不同的。下面引用至阿里新零售事业群CBU技术部招Java高级&专家,感觉说的很好。

    大家讲的都很详细了,我说说我自己的一点浅薄的理解。

    我一直认为,工科的知识有个很明显的特点:“以用为本”。在讨论接口和抽象类的区别时,我也想从“用”的角度试着总结一下区别,所以我想到了设计目的。

    接口的设计目的,是对类的行为进行约束(更准确的说是一种“有”约束,因为接口不能规定类不可以有什么行为),也就是提供一种机制,可以强制要求不同的类具有相同的行为。它只约束了行为的有无,但不对如何实现行为进行限制。对“接口为何是约束”的理解,我觉得配合泛型食用效果更佳。

    抽象类的设计目的,是代码复用。当不同的类具有某些相同的行为(记为行为集合A),且其中一部分行为的实现方式一致时(A的非真子集,记为B),可以让这些类都派生于一个抽象类。在这个抽象类中实现了B,避免让所有的子类来实现B,这就达到了代码复用的目的。而A减B的部分,留给各个子类自己实现。正是因为A-B在这里没有实现,所以抽象类不允许实例化出来(否则当调用到A-B时,无法执行)。

    二、extends与implements的区别

      extends(继承)是针对抽象类,implements(实现)是针对接口类。Java只能单继承,接口弥补单继承的不足。

      从Java编程思想来说,继承解决的是代码复用以及对象的关系,接口解决的是解耦,这是两种不同的理念

    1、在类的声明中,通过关键字extends来创建一个类的子类。

      一个类通过关键字 implements 声明自己使用一个或者多个接口。 

      extends 是继承某个类,继承之后可以使用父类的方法,也可以重写父类的方法。

      implements 是实现多个接口,接口的方法一般为空的,必须重写才能使用 

    2、extends是继承父类,只要那个类不是声明为final或者那个类定义为abstract的就能继承

      Java中不支持多重继承,但是可以用接口来实现,这样就要用到 implements,继承只能继承一个类,但 implements 可以实现多个接口,用逗号分开就行了。比如 :class A extends B implements C,D,E

    3、接口实现的注意点:  

    (1)实现一个接口就是要实现该接口的所有的方法(抽象类除外)

    (2)接口中的方法都是抽象的

    (3)多个无关的类可以实现同一个接口,一个类可以实现多个无关的接口

    4、与Extends的不同

      extends 可以实现父类,也可以调用父类初始化 this.parent(),而且会覆盖父类定义的变量或者函数。这样的好处是:架构师定义好接口,让工程师实现就可以了,整个项目开发效率和开发成本大大降低。

      implements 实现父类,子类不可以覆盖父类的方法或者变量。即使子类定义与父类相同的变量或者函数,也会被父类取代掉。

      这两种实现的具体使用,是要看项目的实际情况,需要实现,不可以修改implements,只定义接口需要具体实现,或者可以被修改扩展性好,用extends。

    三、extends的向上转型

      父类与子类继承关系上的不同:A a = new B(); 结果a是一个A类的实例,只能访问A中的方法,那么又和A a = new A();有什么区别呢?

      class B extends A,继承过后通常会在 B 类上定义一些父类 A 没有的成员或者方法。

      A a = new B(); 这样是可以的,向上转型。

      a 是一个父类对象的实例,因而不能访问子类定义的新成员或方法。

      假如这样定义:

    class A {
      int i;
      void f(){}
    }
    class B extends A {
      int j;
      void f(){} //重写
      void g(){}
    }

      然后:B b = new B(); 那么 b 就是子类对象的实例,不仅能够访问自己的属性和方法,也能够访问父类的属性和方法。诸如b.i,b.j,b.f(),b.g()都是合法的。此时b.f() 是访问的B中的 f()。

      A a = new B(); a 虽然是用的B的构造函数,但经过upcast,成为父类对象的实例,不能访问子类的属性和方法。a.i,a.f()是合法的,而a.j,a.g()非法。此时访问a.f()是访问B中的f()

    四、向上转型的过程

      A a = new B(); 这条语句,实际上有三个过程:

    1、A a;

      将 a 声明为父类对象,只是一个引用,未分配空间

    2、B temp = new B();

      通过B类的构造函数建立了一个B类对象的实例,也就是初始化

    3、a = (A)temp;

      将子类对象 temp 转换为父类对象,并赋给a,这就是上转(upcast),是安全的。

      经过以上3个过程,a就彻底成为了一个A类的实例。

      子类往往比父类有更多的属性和方法,上转只是舍弃,是安全的;而下转(downcast)有时会增加,通常是不安全的。

    4、需要注意的是:

      a.f() 对应的应该是B类的方法 f(),因为调用构造函数建立实例过后,对应方法的入口已经确定了

      如此一来,a 虽被上转为 A类,但其中重写的方法f()仍然是B的方法f()。也就是说,每个对象知道自己应该调用哪个方法。

    A a1 = new B();
    A a2 = new C();

      a1,a2两个虽然都是A类对象,但各自的 f() 不同,这正是多态性的体现

  • 相关阅读:
    Scrum会议5
    小组项目alpha发布的评价
    第二阶段冲刺记录三
    第二阶段冲刺记录二
    第13周学习进度
    第二阶段冲刺记录1
    《人月神话》阅读笔记01
    第12周学习进度
    意见汇总
    双人结对,四则运算(三阶段)
  • 原文地址:https://www.cnblogs.com/goloving/p/14931722.html
Copyright © 2011-2022 走看看