headfirst java ( 第 8 章 )

- 接口是抽象类, 抽象类无法初始化.

  综合以上动物类, 你可以写:

  wolf aWolf = new Wolf();

  Animal aHippo = new Hippo();

  但是这样会很奇怪: 如下:

  Animal anim = new Animal();  // anim 长什么样子…

  所以, 有些类不应该被初始化 !

  所以, 这个 Animal 类就是我们想要的, 可以作为基类来多态, 同时又不想它被“new”出实例来.

  你可以用这个类来做引用, 这就是当初为何要有抽象类型的目的.

  当你设计好继承结构时, 你必须要决定哪些类是抽象的, 哪些是具体的.

  abstract class Canion extends Animal { } // abstract 就是抽象类的关键字

  抽象类 除了被继承过之外, 是没有用途, 没有值, 没有目的. ( 看来只能多态使用 )

  image

- 抽象方法

  抽象类代表此类必须要被 extend 过.

  抽象方法代表此方法一定要被覆盖过. ( 抽象类里的某些行为不会有任何意义, 比如 eat方法, 如果不加上特定的动物, 那么eat这个方法

  执行会是一个什么样的结果呢? )

  抽象方法没与实体! ( public abstract void eat(); )

  如果你声明出一个抽象的方法, 就必须将类也标记为抽象的. 你不能再非抽象类中拥有抽象方法.

- 为什么要有抽象方法? 抽象类得重点就在于可以被子类继承的共同程序代码 ?

  答: 将可继承的方法体( 也就是由内容的方法 )放在父类中是个好主意, 但有时就是没有办法作出给任何子类都有意义的共同代码. 抽象方法

  的意义是就算无法实现出方法的内容, 但还是可以定义出一组子型共同的协议. 这样做的好处是… 就是多态, 记住, 想要达成的目标是要使用

  父类型作为方法的参数, 返回类型或数组的类型. 通过这个机制, 你可以加入新的子型到程序中, 却又不必重写或修改处理这些类型的程序.

  因此多态的好处就在于所有子型都会有那些抽象的方法.

- 必须实现所有的抽象方法

  个人认为: 也是可以理解的, 如果你不实现所有的, 万一你又调用了一个没有实现的, 因为该方法是抽象方法, 它没有具体的意思(类似eat

  方法)

  在继承结构中, 只要有类实现了该方法, 下边的类就可以不在实现, 直接使用, 例如Animal和Canine都标记为 abstract, 则Canine无需

  实现Animal的抽象方法( 个人认为: 这是当然, 因为本身就是抽象的, 就肯定还有类来继承, 那当然是让继承的类来做也可以, 另外,

  因为 abstract 类中可以带抽象和非抽象的方法, 因此, Cannine 如果实现了抽象方法, 下边的例如 Dog 类就不必实现这个部分. )

- java 中的所有类实际上都是继承了 object类, 只有这样java程序员才可以写出类似 ArrayList 类来操作所有的类, 这就是多态.

- Object 这个类不是抽象类, 怎么会准许有人创建Object的对象呢? 这部跟Animal对象一样不合理么?

  答: 因为有时候你就是需要一个通用的对象(Object)对象, 一个轻量化的对象, 最常见的用途是在线程的同步化上面. 以后会说到. ?     

- Object 作用

  1. 多态

  2. 提供java在执行对任何对象都有需要的方法的实现( 有一部分方法与线程有关 )

- 类型检查

  Object o = new Ferrai();

  o.goFast(); // 非法, 虽然在堆中实际分配了Ferrai这个对象的内存, 但是类型是object的, java会有强制类型检查

  在这一点上, 并不与前面矛盾, 你想要多态, 可以, 但是要注意, 哪些方法可都是父类和子类都有的, 是子类继承的方法, 而并非是

  哪些只有子类才有, 父类没有的方法.

- ArrayList<Object>想法

  如果你想定义一个ArrayList, 它可以保存任何类型, 例如 ArrayList<Object>, 然后, 有如下代码:

  ArrayList<Object> myDogArrayList = new ArrayList<Object>();

  Dog aDog = new Dog();

  myDogArrayList.add(aDog);

  Dog d = myDogArrayList.get(0); // 这时候会出错, 右边返回的是一个 Object 类型, 而左边是 Dog类型, 注意, 如果是左边是

  Object 类型, 而右边是一个 Dog类型, 这是可以的, 所以, 左边一定是右边的祖先类才可以. 为什么呢? 结合语境, 如果是左边是Object

  而右边是 Dog 类, 那么翻译过来是, 我定义了一个对象的指针, 这个指针指向了 Dog这个对象, 这肯定是对的, 否则, 就是我定义了一个

  指向 Dog的指针, 但是它指向的确实 Object(没准是lion), 所以肯定是错误的.

  注意: 任何对象放进去 ArrayList<Object>, 返回的对象都是 Object 类型的. 编译器是无法将此对象识别成Object以外的事物.

- 不能想象所有的都通用

  Dog aDog = new Dog();

  Dog sameDog = getObject(aDog); // 这样是有问题的, 因为 getObject 这个方法返回的是 Object 类型, 左边又变成子类, 又边又

  变成祖先类了. 所以: 注意: java 是很注重类型匹配问题的

  编译器是根据引用类型来判断有哪些 method 可以调用, 而不是根据 Object 确实的类型( 即堆里边的实际类型 )

- 对象在堆内存的分配

  当你定义一个对象, 即 new Snowboard() 这里定义了一个 Snowboard对象, 实际上由于继承关系, 此对象包含了一个Object在里边.

  image

- 引用的实质

  如果引用(指针)是个遥控器, 则当你在继承树往下走时, 会发现遥控器的按钮越来越多( 除非子类没有自己的方法, 而只是覆盖父类的方法)

  image 

  还是那句话, 注意: 编译器是不会在意右边的堆中实际的情况, 而是根据左边引用的类型来判断method的多少.

- 强制类型转换

  因为 ArrayList<Object> 返回的都是 Object类型, 而忘掉了以前在放进ArrayList的真实类型, 此时可以使用强制类型转换来恢复以前

  类型, 例如:

  Object o = al.get(index);

  Dog d = (Dog) o;  // 强制类型转换, 另外在转化以前最好先判断一下, if (o instanceof Dog), 在转换

  d.roam();

- 类得重构与设计

  在Animal 类得机构的基础上, 我们现在想增加一些宠物狗和猫特有的方法, 比如”耍宝”和”亲热” 等等, 该如何做呢?

  1. 在 Animal 类中增加该方法, 但是这样不好, 例如 狮子和河马等对该方法不合适.

  2. 把 方法设置成抽象的, 那么每个对象都的实现, 如果是狮子老虎可以什么都不做, 这样, 所有的对象都的实现该方法, 浪费时间

     另外, 让狮子老虎实现该方法, 还是怪怪的, 尽管方法什么都不做.

  3. 只是把方法增加到宠物对类中, 例如, 只增加在猫和狗这两个类里. 这样, 多态将无法起作用, 因为 Animal类里没有这样的方法

  所以: 我们需要的是 a. 一种可以让宠物行为只应用在宠物身上的方法.

                   b. 一种确保所有宠物的类都有的相同的方法定义的方法.

                   c. 一种可以运用多态的方法.

  看起来, 我们需要让猫和狗继承两个类, 一个是宠物类, 一个是 Animal类, 多重继承是这样是有问题的,  如下图:

  image

  对子类调用 burn()方法时, 不知道执行哪个父类的该方法.

  4. 接口解决该问题, interface 可以用来解决多重继承的问题却又不会产生致命方块这种问题. 把该宠物方法设置为抽象的,

  java的接口就好像100%的纯抽象类. 所有接口的方法都是抽象的

  public interface Pet {} // 里边装的是所有的宠物方法. 接口的定义

  public class Dog extends Canine implements Pet {} // 接口的实现, implements 这个关键字表示这个类实现接口

  这么说吧, 使用接口你就可以继承超过一个以上的来源. ( 接口不管你的来源, 只要你用到这些方法, 就实现它就好了 )

  类可以实现多个接口…

- 如何判断应该是设计 类, 子类, 抽象类或 接口 ?

  • 如果新的类无法对其他的类通过IS-A测试, 就设计不继承其他类得类.    // 类
  • 只有在需要某类特殊化的版本时, 以覆盖或增加新的方法来继承现有的类. // 子类
  • 当你需要定义一群子类的模板, 又不想让程序员初始化此模板时, 设计出抽象的类给它们.  // 抽象类
  • 如果要定义出类可以扮演的角色, 使用接口 // 接口 

- 问题

  接口可以被继承, 但是也必须是接口, interface 继承 interface

  接口不能实例化, 因为接口是 100% 的抽象类, 抽象类不能被实例化

  抽象类: 首先, 抽象类是用来被继承的, 如果它不被继承的话, 它什么用也没有, 那么它为什么是抽象的呢? 不是因为它含有所谓的抽象

  方法(一个抽象类可以一个抽象方法都没有, 但是有了一个抽象方法, 必须是抽象类), 是因为它这个类在继承体系中不太好实现实例化, 例如

  Animal 这个类, 里边有实例变量, 重量啊等等, 但是只是一个Animal这些实例变量是没有意义的, 也就是说, 当你需要一个类作为模板时,

  并且又不想该类实例化, 那么就考虑用抽象类.

  接口: 有人总说接口和抽象类的区别, 其实接口更加抽象, 如果说抽象类还可以在继承体系中的一员的话, 那么接口就更加抽象了, 它只是

  一个角色, role(是指有相同权限或方法的集合), 它没有任何IS-A的关系(接口继承接口除外, 方法集合之间的包含关系). 任何类, 只要

  你想就可以实现接口, 根本不用考虑 IS-A 的关系, 而继承是需要考虑 IS-A 的关系的. 接口是 100%的抽象类, 它的作用就是被实现,如果

  没有一个类实现接口, 它就如同抽象类没有被继承一样, 什么用也没有.

原文地址:https://www.cnblogs.com/moveofgod/p/3007007.html