### 接口和内部类为我们提供了一种将接口与实现
分离的更加结构化的方法。
### 抽象类是普通的类与接口之间的一种中庸之道。
尽管在构建具有某些未实现方法的类时,你的第一
思想可能是创建接口,但是抽象类仍旧是用于此目
的的一种重要而必须的工具。因为你不可能总是使
用纯接口。
》》抽象类和抽象方法
### 建立通用接口的唯一理由是,不同的子类可以用
不同的方式表示此接口。通用接口建立起一种基本
形式,以此表示所有导出类的共同部分。另一种
说法是将父类写成抽此基类,简称抽象类。
### Java 提供了一个叫做抽象方法的机制,这种方法
是不完整的;仅有声明而没有方法体。下面是抽象
方法所采用的语法:
abstract void f();
### 包含抽象方法的类叫做抽象类。如果一个类包含一
个或多个抽象方法,该类必须被限定为抽象的。(否
则,编译器就会报错)。
### 由于为抽象类创建对象是不安全的,所以我们会从
编译器那里得到一条出错消息。这样,编译器会确保
抽象类的纯粹性,我们不必担心会误用它。
### 如果从一个抽象类继承,并想创建该新类的对象,
那么 就必须为基类中的所有抽象方法提供方法定义。
如果不这样做(可以选择不做),那么导出类便也是
抽象类,且编译器将会强制我们用 abstract 关键字来
限定这个类。
### 我们也可能会创建一个没有任何抽象方法的抽象类。
### 既然使某个类成为抽象类并不需要所有的方法都是
抽象的,所以仅需将某些方法声明为抽象的即可。
### 创建抽象类和抽象方法非常有用,因为它们可以使
类的抽象性明确起来,并告诉用户和编译器打算怎样
来使用它们。抽象类还是很有用的重构工具,因为它
们使得我们可以很容易地将公共方法沿着继承层次结构
向上移动。
》》接口
### interface 关键字
### interface 关键字产生了一个完全抽象的类,它根本就
没有提供任何具体实现。它允许创建者确定方法名、
参数列表和返回类型,但是没有任何方法体。接口只
提供了形式,而未提供任何具体实现。
### 任何使用某特定接口的代码都知道可以调用该接口的
哪些方法,而且仅需要知道这些。因此,接口被用来
建立类与类之间的协议。
### interface 不仅仅是一个极度抽象的类,因为它允许人们
通过创建一个能够被向上转型为多种基类的类型,来实现
某种类似多重继变种的特性。
### 要想创建一个接口,需要用 interface 关键字来代替
class关键字。可以在关键字 interface 前面加上 public
关键字,如果不加 public关键字,则它默认是只具有
包访问权限,这样它就只能在同一个包内可用。
### 要让一个类遵循某个特定接口(或是一组接口),需要使用
implements 关键字 ,它表示:“interface 只是它的外貌,
但是现在我要声明它是如何工作的”。除此之外,它看起来还
很像继承。
### 在接口中的每一个方法确实都只是一个声明,这是编译器所允
许的在接口中唯一能够存在的事物。此外,在接口中声明的方法
都是 public 的。(即使你没有显式的声明是 public 的,但它们
自动都是 public 的)
》》完全解耦
### 只要一个方法操作的是类而非接口,那么你就只能使用这个类
及其子类。
### 复用代码的第一种方式是客户端程序员遵循某个接口来编写自
己的类。
### 你可能经常碰到的情况是你无法修改你想要使用的类。类库是被
发现而非被创建的。在这些情况下,可以使用适配器模式。适配
器中的代码将接受你所拥有的接口,并产生你所需要的接口。
### 适配器模式分为两种:类适配器模式和对象适配器模式。
对象适配器模式,即所谓的“代理”。
### 将接口从具体实现中解耦使得接口可以应用于多种不同的具体
实现,因此代码也就更具有可复用性。
》》Java 中的多重继承
### 在导出类中,不强制要求必须有一个抽象的或“具体的”(没有任何
抽象方法)基类。如果要从一个非接口的类继承,那么只能从一个
类去继承。其余的基元素都必须是接口。需要将所有的接口都置于
implements 关键字之后,用逗号将它们一一隔开。可以实现任
意多个接口,并且可以向上转型为每个接口,因为每一个接口都是
独立类型。
### 当通过一种方式将一个具体的类和多个接口组合到一起时,这个
具体的类必须放到前面,后面跟着的才是接口(否则编译器会报错)。
即:先继承单个类,然后再实现多个接口。
### 使用接口的原因:
第一(核心):为了能够向上转型为多个基类型(以及由此带来的
灵活性)
第二(与使用抽象类基类相同):防止客户端程序员创建该类的对象,
并确保这仅仅是一个接口。
### 我们应该使用接口还是抽象类??
如果要创建不带任何方法定义和成员变量的基类,那么就应该选择
接口而不是抽象类。事实上,如果知道某事物应该成为一个基类,那
么第一选择应该是它使它成为一个接口。
》》通过继承来扩展接口
### 在接口中,使用 extends 关键字来继承其他接口。(而且可以继承
多个接口,用逗号将各个接口隔开)---》这点与类之间的继承是不同的。
(类之间的继承是单继承的)
### 通过继承,可以很容易地在接口中添加新的方法声明,还可以通过
继承在新接口中组合数个接口。
例如:
public interface Monster {
void menace();
}
public interface Lethal {
void kill();
}
public interface Vampire extends Monster , Lethal {
void drinkBlood();
}
补充: 上面语法仅仅适用于接口继承。一般情况下,只可以将 extends
用于单一类,但是可以引用多个基类接口。只需要将多个接口名用
逗号一一隔开。
------组合接口时的名字冲突
### 在打算组合的不同接口中使用相同的方法名通常会造成代码可读性的
混乱,请尽量避免这种情况。
》》适配接口
### 接口最吸引人的原因之一就是允许同一个接口具有多个不同的具体
实现。在简单的情况中,它的体现形式通常是一个接受接口类型的方法,
而该接口的实现和向该方法传递的对象则取决于方法的使用者。
### 接口一种常见的用法就是:策略模式。此时你编写一个执行某些操作
的方法,而该方法将接受一个同样是你指定的接口。你主要就是声明:
”你可以用任何你想要的对象来调用我的方法,只要你的对象遵循我的
接口“这使得你的方法更加灵活、通用,并更具可复用性。
### 让方法接受接口类型,是一种让任何类都可以对该方法进行适配的方式,
这就是使用接口而不是类的强大之处。
》》接口中的域
### 因为你放如入接口中的任何域都自动是 static 和 final 的,所以接口
就成为了一种很便捷的用来创建常量组的工具。
例如:
public interface Months {
int JANUARY = 1 , FEBRURAY = 2 ,
MARCH = 3;
}
请注意,在Java 中标识具有常量初始化值的 static final 时, 会使用大写
字母的风格(在一个标识符中用下划线啦分隔多个单词)。接口中的域自动
是 public 的,所以没有显式地指明这一点。
综合上述:在接口中定义的任何域 默认是 : public static final 的
### 有了 Java SE 5 ,你就可以使用更加强大而灵活的 enum 关键字,因此,
使用接口来群组常量已经显得没有什么意义了。
------初始化接口中的域
### 在接口中定义的域不能是”空final“,但是可以是被非常量表达式初始化。
例如:
public interface RandVals {
Random RAND = new Random(47);
int RANDOM_INT = RAND.nextInt(10);
}
### 既然域是 static 的,它们就可以在类第一次被加载时初始化,这发生在任何
域被首次被访问时。
### 这些域不是接口的一部分,它们的值被存储在该接口的静态存储区域内。
》》嵌套接口
### 接口可以嵌套在类或其他接口中。
### 特别注意,当实现某个接口时,并不需要实现嵌套在其内部的任何接口。
而且,private 接口不能在定义它的类之外被实现。
》》接口与工厂
### 接口是实现多重继承的途径,而生成遵循某个接口的对象的典型方式是
工厂方法设计模式。这与直接调用构造器不同,我们在工厂对象上调用的是
创建方法,而该工厂对象将生成接口的某个实现的对象。理论上,通过这种
方式,我们的代码将完全与接口的实现分离,这就使得我们可以透明地将某个
实现替换为另一个实现。
### 如果不使用工厂方法,你的代码就必须在某处指定将要创建的某种类型的
确切类型,以便调用合适的构造器。
### 为什么我们想要添加这种额外级别的间接性呢??
一个常见的原因是想要创建”框架“。
### 下一节,你将看到另一种更加优雅的工厂实现方式,那就是使用匿名内部类。
》》总结
### ”确定接口是理想选择,因而应该总是选择接口而不是具体的类。“这是一种诱惑。
### 对于创建类,几乎在任何时刻,都可以替代为创建一个接口和一个工厂。
### 任何抽象性都应该是应真正的需求而产生的。当必需时,你应该重构接口而不是
到处添加额外级别的间接性,并由此带来额外的复杂性。
### 恰当的原则应该是优先选择类而不是接口。从类开始,如果接口的必需性变
得非常明确,那么就进行重构。接口是一种重要的工具,但是它们容易被滥用。