九、接口

接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法。

1.抽象类和抽象方法 

  抽象类是普通的类与接口之间的一种中庸之道。创建抽象类是希望通过这个通用接口操纵一系列类。

  Java提供一个叫做抽象方法的机制,这种方法是不完整的;仅有声明而没有方法体。例:

  abstract void f();

  包含抽象方法的类叫做抽象类。

  如果从一个抽象类继承,并想创建该新类的对象,那么就必须为基类中的所有抽象方法提供方法定义。如果不这样做,那么导出类也是抽象类。

  我们可能会创建一个没有任何抽象方法的抽象类。考虑这种情况:如果有一个类,让其包含任何abstract方法都显得没有实际意义,而且我们也想要阻止产生这个类的任何对象,那么这时这样做就很有用了。

2.接口 

  abstract关键字允许人们在类中创建一个或多个没有任何定义的方法——提供了接口部分,但是没有提供任何相应的具体实现,这些实现是由此类的继承者创建的。interface这个关键字产生一个完全抽象的类,它根本就没有提供任何具体实现。它允许创建者确定方法名、参数列表和返回类型,但是没有任何的方法体。接口只提供了形式,而未提供任何具体实现。

  一个接口表示:“所以实现了该特定接口的类看起来都像这样”。接口用来建立类与类之间的协议。

  但是,interface不仅仅是一个极度抽象的类,因为它允许人们通过创建一个能够被向上转型为多种基类的类型,来实现某种类似多重继变种的特性。

  接口也可以包含域,但是这些域隐式地是staticfinal的。

  要让类遵循某个特定接口,需要使用implements关键字,它表示“interface只是它的外貌,但是现在我要声明它是如何工作的。”

  接口中的方法都隐式被定义为public,因此,当要实现一个接口时,在接口中被定义的方法必须被定义为是public的。

3.完全解耦

  只要一个方法操作的是类而非接口,那么就只能使用这个类及其子类。如果你想要将这个方法应用于不在此继承结构中的某个类,那么你就会遇到障碍了。接口可以在很大程度上放宽这种限制,因此,它使得我们可以编写可复用性更好的代码。

  创建一个能够根据所传递的参数对象的不同而具有不同行为的方法,被称为策略设计模式。这类方法包含所要执行的算法中固定不变的部分,而“策略”包含变化的部分。策略就是传递进去的参数对象,它包含要执行的代码。

  适配器设计模式,适配器中的代码将接受你所拥有的接口,并产生你所需要的接口。

4.Java中的多重继承 

  可以继承任意多个接口,并可以向上转型为每个接口。

  使用接口的核心原因:为了能够向上转型为多个基类型(以及由此带来的灵活性)。然而,使用接口的第二个原因却是与使用抽象类相同:防止客户端程序员创建该类的对象,并确保这仅仅是建立一个接口。

  如果要创建不带任何方法定义和成员变量的基类,那么就应该选择接口而不是抽象类。事实上,如果知道某事物应该成为一个基类,那么第一选择应该是使它成为一个接口。

5.通过继承来扩展接口 

  通过继承,可以很容易地在接口中添加新的方法声明。还可以通过继承在新接口中组合数个接口。这两种情况都可以获得新的接口。

  ①组合接口时的名字冲突

  当实现的多个接口中存在相同名字的方法,但它们的签名或返回类型不同,会产生混乱,所以尽量避免这种情况。

6.适配接口 

  接口最吸引人的原因之一就是允许同一个接口具有多个不同的具体实现。在简单的情况中,它的体现形式通常是一个接受接口类型的方法,而该接口的实现和向该方法传递的对象则取决于方法的使用者。

  因此,接口的一种常见用法就是策略设计模式,此时你编写一个执行某些操作的方法,而该方法将接受一个同样是你指定的接口。你主要就是声明:“你可以用任何你想要的对象来调用我的方法,只要你的对象遵循我的接口。”

7.接口中的域 

  因为放入接口的任何域都自动是staticfinal的,所以接口就成为了一种很便捷的用来创建常量组的工具。这些域不是接口的一部分,它们的值被存储在该接口的静态存储区域内。

  有了Java SE5,使用接口来群组常量已经没有什么意义了,因为我们有了enum关键字。

8.嵌套接口

  接口可以嵌套在类中或其他接口中。

class A {
interface B {
void f();
}
public class BImp implements B {
public void f() {}
}
private class BImp2 implements B {
public void f() {}
}
public interface C {
void f();
}
class CImp implements C {
public void f() {}
}
private class CImp2 implements C {
public void f() {}
}
interface D {
void f();
}
private class DImp implements D {
public void f() {}
}
public class DImp2 implements D {
public void f() {}
}
public D getD() { return new DImp(); }
private D dRef;
public void receiveD(D d) {
dRef = d;
dRef.f();
}
} interface E {
interface G {
void f();
} public interface H {
void f();
}
void g();
// 不能创建一个private接口
// private interface I {}
} public class NestingInterfaces {
public class BImp implements A.B {
public void f() {}
}
class CImp implements A.C {
public void f() {}
}
//不能实现私有的接口
//class DImp implements A.D {
// public void f() {}
// }
class EImp implements E {
public void g() {}
}
class EGImp implements E.G {
public void f() {}
}
class EImp2 implements E {
public void g() {}
class EG implements E.G {
public void f() {}
}
}
public static void main(String[] args) {
A a = new A();
//无法访问A.D
// A.D ad = a.getD();
//只返回A.D
//A.DImp di2 = a.getD();
//不能访问接口的成员
//a.getD().f();
A a2 = new A();
a2.receiveD(a.getD());
}
}

  接口也可以被实现为private的,就像A.D。那么private的嵌套接口能带来什么好处呢?它即可实现为private类,也可实现为public类(A.DImp2)。但是,A.DImp2只能被其自身所使用。你无法说它实现了一个private接口D。因此,实现一个private接口只是一种方式。它可以强制该接口中的定义不要添加任何类型信息(也就是说,不允许向上转型)。

  在main()中,数次尝试使用getD()都失败了。只有一种方式能成功,那就是将返回值交给有权使用它的对象。

  接口E说明接口彼此之间也可以嵌套。嵌套在另一个接口中的接口自动就是public的。

  特别要注意的是,当实现某个接口时,并不需要实现嵌套在其内部的任何接口。而且,private接口不能在定义它的类之外被实现。

9.接口与工厂

  接口是实现多重继承的途径,而生成遵循某个接口的对象的典型方式就是工厂方法设计模式。这与直接调用不同,我们在工厂对象上调用的是创建方法,而该工厂对象将生成接口的某个实现的对象。理论上,通过这种方式,我们的代码将完全与接口的实现分离,这就使我们可以透明地将某个实现替换为另一个实现。下面展示了工厂方法的结构:

interface Service {
void method1();
void method2();
} interface ServiceFactory {
Service getService();
} class Implementation1 implements Service {
public void method1() {
System.out.println("Implementation1 method1");
}
public void method2() {
System.out.println("Implementation1 method2");
}
} class Implementation1Factory implements ServiceFactory {
public Service getService() {
return new Implementation1();
}
} class Implementation2 implements Service {
public void method1() {
System.out.println("Implementation2 method1");
}
public void method2() {
System.out.println("Implementation2 method2");
}
} class Implementation2Factory implements ServiceFactory {
public Service getService() {
return new Implementation2();
}
} public class Factories {
public static void serviceConsumer (ServiceFactory fact) {
Service s = new fact.getService();
s.method1();
s.method2();
}
public static void main(String[] args) {
serviceConsumer( new Implementation1Factory() );
serviceConsumer( new Implementation2Factory() );
}
/*Output
Implementation1 method1
Implementation1 method2
Implementation2 method1
Implementation2 method2
*/

  如果不是用工厂方式,上面代码就必须在某处指定将要创建的Service的确切类型,以便调用合适的构造器。

10.总结 

  任何抽象性都应该是应真正的需求而产生的。恰当的原则是优先选择类而不是接口。从类开始,如果接口的必须性变得非常明确,那么就进行重构。接口是一种只要的工具,但容易被滥用。

Java编程思想 学习笔记9的更多相关文章

  1. [Java编程思想-学习笔记]第3章 操作符

    3.1  更简单的打印语句 学习编程语言的通许遇到的第一个程序无非打印"Hello, world"了,然而在Java中要写成 System.out.println("He ...

  2. Java编程思想 学习笔记1

    一.对象导论 1.抽象过程 Alan Kay曾经总结了第一个成功的面向对象语言.同时也是Java所基于的语言之一的Smalltalk的五个基本特性,这些特性表现了纯粹的面向对象程序设计方式 1)万物皆 ...

  3. [Java编程思想-学习笔记]第1章 对象导论

    1.1  抽象过程 Java是一门面向对象的语言,它的一个优点在于只针对待解问题抽象,而不用为具体的计算机结构而烦心,这使得Java有完美的移植性,也即Java的口号"Write Once, ...

  4. Java编程思想 学习笔记11

    十一.持有对象  通常,程序总是根据运行时才知道的某些条件去创建新对象.在此之前,不会知道所需对象的数量,甚至不知道确切的类型. Java实用库还提供了一套相当完整的容器类来解决这个问题,其中基本的类 ...

  5. Java编程思想学习笔记——类型信息

    前言 运行时类型信息(RTTI:Runtime Type Information)使得我们可以在程序运行时发现和使用类型信息. Java在运行时识别对象和类的信息的方式: (1)一种是RTTI,它假定 ...

  6. Java编程思想 学习笔记12

    十二.通过异常处理错误  Java的基本理念是“结构不佳的代码不能运行”. Java中的异常处理的目的在于通过使用少于目前数量的代码来简化大型.可靠的程序的生成,并且通过这种方式可以使你更加自信:你的 ...

  7. Java编程思想 学习笔记10

    十.内部类  可以将一个类的定义放在另一个类的定义内部,这就是内部类. 内部类是一种非常有用的特性,因为它允许你把一些逻辑相关的类组织在一起,并控制位于内部的类的可视性.然而必须要了解,内部类和组合是 ...

  8. Java编程思想 学习笔记7

    七.复用类 1.组合语法 在新的类中产生现有类的对象.由于新的类是由现有类的对象所组成,所以这种方法叫做组合. 类中域为基本类型时能够自动被初始化为零.对象引用被初始化为null. 编译器不是简单地为 ...

  9. Java编程思想 学习笔记5

    五.初始化与清理 1.用构造器确保初始化  在Java中,通过提供构造器,类的设计者可确保每个对象都会得到初始化.创建对象时,如果其类具有构造器,Java就会在用户有能力操作对象之前自动调用相应的构造 ...

  10. Java编程思想 学习笔记4

    四.控制执行流程 1.true和false 所有条件语句都利用条件表达式的真或假来决定执行路径.注意Java不允许我们将一个数字作为布尔值使用. 2.if-else 3.迭代 while.do-whi ...

随机推荐

  1. PAT 1018 锤子剪刀布

    https://pintia.cn/problem-sets/994805260223102976/problems/994805304020025344 大家应该都会玩“锤子剪刀布”的游戏:两人同时 ...

  2. 如何为TreeView定义三层模板并实现数据绑定

    一直以来都想对TreeView定义多层模板,并实现数据绑定做一个总结,今天在这里做一个概述,我们常用的两层的TreeView绑定的话,我们首先修改TreeView的模板,这里我们使用的是级联的数据模板 ...

  3. Lodop导出图片,导出单页内容的图片

    用如下语句设置图片的格式,设置导出的图片初识的默认名称,Lodop可以把打印内容导出成图片. LODOP.SET_SAVE_MODE("SAVEAS_IMGFILE_EXENAME" ...

  4. anaconda2安装cv2

    http://m.blog.csdn.net/u010167269/article/details/62447648 下载离线安装包:https://anaconda.org/menpo/opencv ...

  5. instanceof判断的对象可以是接口

    instanceof是Java的一个二元操作符(运算符) 用法 boolean result = object instanceof class instanceof通过返回一个布尔值来指出,这个对象 ...

  6. Python3网络爬虫(1):利用urllib进行简单的网页抓取

    1.开发环境 pycharm2017.3.3 python3.5 2.网络爬虫的定义 网络爬虫,也叫网络蜘蛛(web spider),如果把互联网比喻成一个蜘蛛网,spider就是一只在网上爬来爬去的 ...

  7. MT【24】一道五次方程的求根题

    解答: 评:一般的五次及以上的多项式方程是无根式解的,只能用计算机去精确到某某位.但是特殊的比如$x^5=1$显然有根式解,本题就是一个不平凡的特殊的例子,这里的代换用于求解三次方程的求根过程是一样的 ...

  8. 01 自学Aruba之功率单位和相对单位

    点击返回:自学Aruba之路 01 自学Aruba之功率单位和相对单位 功率单位是用来测量传输振幅和接受振幅的大小,功率单位测量的是绝对功率 相对单位是用来计算增加电缆或天线后的损耗和增益的大小,相对 ...

  9. 构建MySQL-Cluster

    Mysql Cluster概述与部署MySQL Cluster 是一种技术,该技术允许在无共享的系统中部署“内存中”数据库的 Cluster .通过无共享体系结构,系统能够使用廉价的硬件,而且对软硬件 ...

  10. Java:判断当前操作系统界面采用的主题是windows经典样式还是xp样式

    想起两三年前,发现写Java界面的时候,如果将当前界面的layout设为null,由于windows的不同主题界面下,标题栏的高度不一致,导致当前界面表现也不一致. 当时就想找到一个办法先判断当前用户 ...