第九章 接口

抽象类和抽象方法

抽象:从具体事物抽出、概括出它们共同的方面、本质属性与关系等,而将个别的、非本质的方面、属性与关系舍弃,这种思维过程,称为抽象。

这句话概括了抽象的概念,而在Java中,你可以只给出方法的定义不去实现方法的具体事物,由子类去根据具体需求来具体实现。

抽象类除了包含抽象方法外,还可以包含具体的变量和具体的方法。类即使不包含抽象方法,也可以被声明为抽象类,防止被实例化。

抽象类不能被实例化,也就是不能使用new关键字来得到一个抽象类的实例,抽象方法必须在子类中被实现。

抽象类总结规定:

  1. 抽象类不能被实例化,如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
  2. 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
  3. 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。
  4. 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。
  5. 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。

接口

interface关键字使得抽象的概念更加向前迈进了一步,abstract关键字允许人们在类中创建一个或多个没有任何定义的方法---提供了接口部分。但是没有提供任何相应的具体实现,这些实现是由此类的继承者实现的。

在抽象类中,可以包含一个或多个抽象方法;但在接口(interface)中,所有的方法必须都是抽象的,不能有方法体,它比抽象类更加“抽象”。

接口使用 interface 关键字来声明,可以看做是一种特殊的抽象类,可以指定一个类必须做什么,而不是规定它如何去做。

与抽象类相比,接口有其自身的一些特性:

  • 接口中只能定义抽象方法,这些方法默认为 public abstract 的,因而在声明方法时可以省略这些修饰符。试图在接口中定义实例变量、非抽象的实例方法及静态方法,都是非法的
  • 接口中没有构造方法,不能被实例化
  • 一个接口不实现另一个接口,但可以继承多个其他接口。接口的多继承特点弥补了类的单继承

接口与抽象类的区别:

接口作为系统和外界交互的窗口,接口体现的是一种规范。对于接口的实现者而言,接口规定了实现者必须向外提供哪些服务(以方法的形式来提供);对于接口的调用者而言,接口规定了调用者可以调用哪些服务,以及如何调用这些服务(就是如何来调用方法)。当在一个程序中使用接口时,接口是多个模块间的耦合标准;当在多个应用程序之间使用接口时,接口是多个程序之间的通信标准。

从某种角度上来看,接口类似于整个系统的“总纲”,它制定了系统各模块之间应该遵循的标准,因此一个系统中的接口不应该经常改变。一旦接口改变,对整个系统而言甚至其他系统的影响将是辐射式的,导致系统中的大部分类都需要改写。所以,在一般的应用里,最顶级的是接口,然后是抽象类实现接口,最后才到具体类实现。

抽象类则不一样,抽象类作为系统中多个子类的共同父类,它所体现的是模板式设计。抽象类作为多个子类的的抽象父类,可以被当成系统实现过程中的中间产品,这个产品已经实现了系统的部分功能(那些在抽象类中已经提供实现的方法),但这个产品依然不能当成最终产品,必须有更进一步的完善。

除此之外,接口和抽象类在用法上也存在如下区别:

  • 接口里只能包含抽象方法,抽象类则可以包含普通方法。

  • 接口里不能定义静态方法,抽象类里可以定义静态方法。

  • 接口里不包含构造器,抽象类可以包含构造器。抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。

  • 接口里不能包含初始化块,但抽象类可以包含初始化块。

  • 接口里只能定义静态常量,抽象类既可以定义普通变量,也可以定义静态常量。

  • 接口可以可以继承多个接口,类只能继承一个类。

  • 抽象类主要是用来抽象类别,接口主要是用来抽象方法功能。当关注事物的本质时,使用抽象类,当关注一种操作时,使用接口。

Java中的多重继承

接口不仅仅是一种更加纯粹的抽象类,它的目标比这更高。因为接口中根本没有任何具体实现,所以没有任何与接口相关的存储,因此也就无法阻止多个接口的组合。在C++中,组合多个类的接口的行为叫做多重继承,但这可能会带来很多副作用,因为每个类都有一个具体实现。在Java中,可以执行一样的行为,但是只有一个类可以有具体实现,所以通过组合多个接口,C++的问题不会在Java中发生。

表达这样一个意思:“ x 从属于 a,也从属于 b,也从属于 c ”

使用接口的核心原因:

1).为了能够向上转型为多个基类型(以及由此带来的灵活性);

2).防止客户端程序员创建该类的对象,并确保这仅仅是建立一个接口(这与使用抽象基类原因相同)

这带来的一个问题是,应该使用接口还是抽象类?

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

通过继承来扩展接口

组合接口时的名字冲突

在实现多重继承时,会碰到一个小陷阱,在前面的例子中,CanFight和ActionCharacter都有一个相同的void fight()方法。问题不是它们方法相同,问题是,如果它们的签名(参数)或返回类型不同,会怎么样呢?

//: interfaces/InterfaceCollision.java
package object; interface I1 { void f(); }
interface I2 { int f(int i); }
interface I3 { int f(); }
class C { public int f() { return 1; } } class C2 implements I1, I2 {
public void f() {}
public int f(int i) { return 1; } // overloaded
} class C3 extends C implements I2 {
public int f(int i) { return 1; } // overloaded
} class C4 extends C implements I3 {
// Identical, no problem:
public int f() { return 1; }
} // Methods differ only by return type:
//!class C5 extends C implements I1 {} --23
//! interface I4 extends I1, I3 {} ///:~ --24 I1, I3中f()返回值类型不一致 //class C5 extends C implements I1{ //实现的方法和积累方法命名相同,但方法的返回值不一样。
// int f(){
// return 0;
// }
//}
//
//interface I4 extends I1 , I3{ //重写的方法名相同,但是返回值不同。
//
// @Override
// void f();
//}

因为他们的方法名都相同,但是返回值不同,并不能实现方法重载。所以不能实现多重继承和组合接口。

适配接口

接口最吸引人的原因之一就是允许同一个接口具有多种不同的实现

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

比如Java SE5的Scanner类,它的构造器接收的是一个Readable接口。

public Scanner(Readable source) {
this(Objects.requireNonNull(source, "source"), WHITESPACE_PATTERN);
} // Readable 是一个字符源。read方法的调用方能够通过 CharBuffer 使用 Readable 中的字符。
public interface Readable {
// 将输入内容添加到CharBuffer参数中。
public int read(java.nio.CharBuffer cb) throws IOException;
}

example1 : 实现Readable接口。

import java.io.IOException;
import java.nio.CharBuffer;
import java.util.Random;
import java.util.Scanner; public class RandomWords implements Readable { private int count; public RandomWords(int count) {
this.count = count;
} private static Random random = new Random(47);
private static final char[] capitals = "ABCDEFTHIGKLMNOPQRSTUVWXYZ".toCharArray();
private static final char[] lowers = "abcdefghijklmnopqrstuvwxyz".toCharArray();
private static final char[] vowerls = "aeiou".toCharArray(); @Override
public int read(CharBuffer cb) throws IOException {
if (count-- == 0) {
return -1;
}
cb.append(capitals[random.nextInt(capitals.length)]); for (int i = 0; i < 4; i++) {
cb.append(vowerls[random.nextInt(vowerls.length)]);
cb.append(lowers[random.nextInt(lowers.length)]);
}
cb.append(" ");
return 10;
} public static void main(String[] args) {
@SuppressWarnings("resource")
Scanner scanner = new Scanner(new RandomWords(10));
while (scanner.hasNext()) {
System.out.println(scanner.next());
}
}
}
/*output:
Yazeruyac
Fowenucor
Toeazimom
Raeuuacio
Nuoadesiw
Hageaikux
Ruqicibui
Numasetih
Kuuuuozog
Waqizeyoy
*/

example2 : 未实现Readable的类,就可以使用适配器+代理的方式

class RandomDoubles{
private static Random rand =new Random(47);
public double next(){
return rand.nextDouble();
}
} // --------------------------------------------------- import java.io.IOException;
import java.nio.CharBuffer;
import java.util.Random;
import java.util.Scanner; public class Test {
public static void main(String[] args) {
Scanner s=new Scanner(new AdaptedRandomDoubles(7));
while(s.hasNext()){
System.out.println(s.next());
}
}
}
class AdaptedRandomDoubles extends RandomDoubles implements Readable {
private int count;
public AdaptedRandomDoubles(int count){
this.count=count;
}
public int read(CharBuffer cb) throws IOException {
if(count--==0){
return -1;
}
String result=Double.toString(this.next());
cb.append(result);
return result.length();
} }

接口中的域

实例变量都是static final

嵌套接口

在类中嵌套接口的语法是相当显而易见的,就像非嵌套接口一样,可以拥有public和“包访问”两种可视性。

1.类中的接口

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() {
}
} private interface D {
void f();
} private class DImp implements D {
public void f() {
}
} public class DImpl2 implements D {
public void f() {
}
} public D getD() {
return new DImpl2();
} private D dRef; public void receive(D d) {
dRef = d;
dRef.f();
}
} interface E {
interface G {
void f();
} //Redundant "public"
public interface H {
void f();
} void g();
//cannot be private within an interface
} public class NestingInterface {
public class BImpl implements A.B {
public void f() {
}
} class CImpl implements A.C {
public void f() {
}
} // cannot implement a private interface
// class DImpl implements A.D {
// public void f() {
// }
// } class EImpl implements E {
public void g() {
}
} class EImpl2 implements E.G {
public void f() {
} class EG implements E.G {
public void f() {
}
}
} public static void main(String[] args) {
A a = new A();
A a2 = new A();
//Can't access A.D: 不能访问私有接口A.D
//! A.D ad = a.getD();
//Doesn't return anything but A.D: 除了私有接口A.D,不能返回任何东西
//! A.DImp2 di2 = a.getD(); //返回回来的私有接口A.D, 不能向下转型为A.DImp2
//Cannot access a member of the interface: 不能访问私有接口A.D中的成员
//! a.getD().f();
//Only another A can do anything with getD(): 只有另一个A才能使用getD()做任何事
a2.receive(a.getD());
}
}
  • A.DImp2只能被其自身所使用。你无法说它实现了一个private接口D,因此,实现一个private接口只是一种方式,它可以强制该接口中的方法定义不要添加任何类型信息(也就是说,不允许向上转型),即A.DImp2不能转型为 private接口D;
  • 接口也可以被实现为private的,就像在A.D中看到的那样; private接口不能在定义它的类之外被实现
  • 将返回值交给有权使用它的对象。在本例中,是另一个A通过receiveD()方法来实现的;
  • 嵌套在另一个接口中的接口自动是public的,而不能声明为private的;

2.接口中的接口

interface E{
// 只能是默认或者public
interface G {
//默认为public
void f();
} // Cannot be private within an interface:
//! private interface I {} }
class t2 implements E.G{
public void f() {
}
}

接口与工厂

接口时实现多重继承的途径,而生成遵循某个接口的对象的典型方式就是工厂方法设计模式

通过工厂方法,接口和实现完全分离,可以非常方便的更改实现。

interface Service // Service接口,可以有多种实现
{
void method1();
void method2();
} interface ServiceFactory // 工厂接口,可以由多种实现
{
Service getService();
} class Implementation1 implements Service { //Service接口的实现1
public Implementation1() { }
public void method1() {
System.out.println("Implementation1 method1");
}
public void method2() {
System.out.println("Implementation1 method2");
}
} class Implementation1Factory implements ServiceFactory{ //生成对象1的工厂1
public Service getService() {
return new Implementation1();
}
} class Implementation2 implements Service { // Service接口的实现2
public Implementation2() { }
public void method1() {
System.out.println("Implementation2 method1");
}
public void method2() {
System.out.println("Implementation2 method2");
}
} class Implementation2Factory implements ServiceFactory{//生成对象2的工厂2
public Service getService() {
return new Implementation1();
}
} public class Factories { //使用service的模块
public static void serviceConsumer(ServiceFactory fact) {
Service s = 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
*/

匿名内部类改进

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

总结

优先选择类而不是接口。从类开始,如果接口的必需性变得非常明确,那么就进行重构。

Java编程思想 第九章 接口的更多相关文章

  1. java编程思想第九章接口

    9.1抽象类和抽象方法 为什么要有抽象类? 是希望通过通用接口操作一系列类. 那么抽象类的形式是什么样的呢? 声明类的使用使用abstract关键字,且在该类中应该具有抽象方法. 注:抽象方法被关键字 ...

  2. [Java编程思想] 第一章 对象导论

    第一章 对象导论 "我们之所以将自然界分解,组织成各种概念,并按其含义分类,主要是因为我们是整个口语交流社会共同遵守的协定的参与者,这个协定以语言的形式固定下来--除非赞成这个协定中规定的有 ...

  3. Java编程思想学习笔记——接口

    1.抽象类和抽象方法 抽象方法:不完整的,仅有声明而没有方法体. abstract void f(); 抽象类:包含抽象方法的类.(若一个类包含一个或多个抽象方法,则该类必须限定为抽象的.) 1.用抽 ...

  4. Java编程思想之九 接口

    接口和内部为我们提供了一种将接口与实现分离的更加结构化的方法. 抽象类和抽象方法 创建一个抽象类是希望通过这个通用接口操纵一系列类. Java提供了一个叫做抽象方法的机制,这种方法是不完整的:仅声明而 ...

  5. [Java编程思想] 第二章 一切都是对象

    第二章 一切都是对象 2.1 用引用操纵对象   创建一个String引用: String s;   这里所创建的只是引用,并不是对象.   创建一个引用的同时便初始化: String s = &qu ...

  6. JAVA 编程思想第一章习题

    //: ch1.01/IntChar.java package object; import java.util.*; public class IntChar { int x; char y; pu ...

  7. 学习java编程思想 第一章 对象导论

    一.面向对象的五个基本特性: 1.万物皆为对象.将对象视为奇特的变量,他可以存储数据,还可以要求它在自身上执行操作. 2.程序是对象的合集,他们通过发送消息告诉彼此所要做的. 3.每个对象都有自己的由 ...

  8. JAVA编程思想第一章——对象导论

  9. JAVA编程思想第二章答案

    欢迎访问我的CSDN博客查看https://mp.csdn.net/mdeditor/94797839# 有其他问题欢迎发送邮箱至hpzhangjunjiell@163.com 感谢

随机推荐

  1. FLV简介

    FLV (Flash Video) 是由 Adobe 公司推出的一种封装格式,主要用于流媒体系统. FLV 封装的媒体文件具有体积轻巧.封装播放简单等特点,很适合网络应用. 目前各浏览器普遍使用 Fl ...

  2. Python - 面向对象编程 - 实例方法、静态方法、类方法

    实例方法 在类中定义的方法默认都是实例方法,前面几篇文章已经大量使用到实例方法 实例方法栗子 class PoloBlog: def __init__(self, name, age): print( ...

  3. Spring Boot 入门系列(二十三)整合Mybatis,实现多数据源配置!

    d之前介绍了Spring Boot 整合mybatis 使用注解方式配置的方式实现增删改查以及一些复杂自定义的sql 语句 .想必大家对spring boot 项目中,如何使用mybatis 有了一定 ...

  4. HTTP系列之:HTTP中的cookies

    目录 简介 cookies的作用 创建cookies cookies的生存时间 cookies的权限控制 第三方cookies 总结 简介 如果小伙伴最近有访问国外的一些标准网站的话,可能经常会弹出一 ...

  5. Servlet 之文件下载

    Servlet 之文件下载 import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; impor ...

  6. 简单C++线程池

    简单C++线程池 Java 中有一个很方便的 ThreadPoolExecutor,可以用做线程池.想找一下 C++ 的类似设施,尤其是能方便理解底层原理可上手的.网上找到的 demo,基本都是介绍的 ...

  7. 【开发工具】Postman保姆级入门教程

    目录 一.简单使用 1. 创建命名空间 2. 创建新集合 3. 按模块整理接口 二.使用环境变量 1. 创建环境与环境变量 2. 使用环境变量 3. 登录后自动更新环境变量 转载请注明出处 一.简单使 ...

  8. 性能测试工具JMeter 基础(四)—— 录制脚本

    对于JMeter中HTTP请求除了手动添加以为还可以进行脚本录制,有两个方法: 使用badboy录制,录制完成后,将录制的文件导入JMeter中 使用JMeter自带的录制原件进行录制(HTTP(S) ...

  9. Python网络爬虫——京东商城商品列表

    Python_网络爬虫--京东商城商品列表 最近在拓展自己知识面,想学习一下其他的编程语言,处于多方的考虑最终选择了Python,Python从发布之初就以庞大的用户集群占据了编程的一席之地,pyth ...

  10. CGLib浅析

    CGLib浅析 什么是CGLib CGLIB实现动态代理,并不要求被代理类必须实现接口,底层采用asm字节码生成框架生成代理类字节码(该代理类继承了被代理类). 所以被代理类一定不能定义为final ...