重温经典《Thinking in java》第四版之第九章 接口(四十六)

简介: 重温经典《Thinking in java》第四版之第九章 接口(四十六)

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

这种机制在编程语言中并不通用。例如,C++对这些概念只有间接的支持。Java中存在语言关键字这个事实表明人们认为这些思想是很重要的,以至于要提供对它们的直接支持。

首先我们将学习抽象类,它是普通类与接口之间的一种中庸之道。尽管构建具有某些未实现方法的类时,你的第一想法可能是创建接口,但是抽象类仍旧是用于此目的的一种重要而必须的工具,因为你不可能总是使用纯接口。

 

9.1 抽象类和抽象方法

Java提供一个叫做抽象方法的机制,这种方法是不完整的:仅有声明而没有方法体。下面是抽象方法声明所采用的语法:

abstract void f( );

包含抽象方法的类叫做抽象类。如果一个类包含一个或者多个抽象方法,该类必须被限定为抽象的。(否则,编译器就会报错。)

如果一个抽象类不完整,那么当我们试图产生该类的对象时,编译器会怎样处理呢?由于为抽象类创建对象时不安全的,所以我们会从编译器那里得到一条出错消息。这样,编译器会确保抽象类的纯粹性,我们不必担心误用它。

如果从一个抽象类继承,并想创建该新类的对象,那么必须为基类中的所有抽象方法提供方法定义。如果不这样做(可以选择不做),那么导出类便也是抽象类,且编译器将会强制我们用abstract关键字来限定这个类、

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

即使使某个非抽象类成为抽象类并不需要所有的方法都是抽象的,所以仅需将某些方法声明为抽象的即可。如下图所示:

image.png

下面是修改过的“管弦乐器”的例子,其中采用了抽象类和抽象方法:

//: interfaces/music4/Music4.java // Abstract classes and methods. packageinterfaces.music4; 
importpolymorphism.music.Note; 
importstaticnet.mindview.util.Print.*; 
abstractclassInstrument { 
privateinti; // Storage allocated for each publicabstractvoidplay(Noten); 
publicStringwhat() { return"Instrument"; } 
publicabstractvoidadjust(); 
} 
classWindextendsInstrument { 
publicvoidplay(Noten) { 
print("Wind.play() "+n); 
    } 
publicStringwhat() { return"Wind"; } 
publicvoidadjust() {} 
} 
classPercussionextendsInstrument { 
publicvoidplay(Noten) { 
print("Percussion.play() "+n); 
    } 
publicStringwhat() { return"Percussion"; } 
publicvoidadjust() {} 
} 
classStringedextendsInstrument { 
publicvoidplay(Noten) { 
print("Stringed.play() "+n); 
    } 
publicStringwhat() { return"Stringed"; } 
publicvoidadjust() {} 
} 
classBrassextendsWind { 
publicvoidplay(Noten) { 
print("Brass.play() "+n); 
    } 
publicvoidadjust() { print("Brass.adjust()"); } 
} 
classWoodwindextendsWind { 
publicvoidplay(Noten) { 
print("Woodwind.play() "+n); 
    } 
publicStringwhat() { return"Woodwind"; } 
} 
publicclassMusic4 { 
// Doesn’t care about type, so new types // added to the system still work right: staticvoidtune(Instrumenti) { 
// ... i.play(Note.MIDDLE_C); 
    } 
staticvoidtuneAll(Instrument[] e) { 
for(Instrumenti : e) 
tune(i); 
    } 
publicstaticvoidmain(String[] args) { 
// Upcasting during addition to the array: Instrument[] orchestra= { 
newWind(), 
newPercussion(), 
newStringed(), 
newBrass(), 
newWoodwind() 
        }; 
tuneAll(orchestra); 
    } 
}

/* Output:

Wind.play() MIDDLE_C

Percussion.play() MIDDLE_C

Stringed.play() MIDDLE_C

Brass.play() MIDDLE_C

Woodwind.play() MIDDLE_C

*///:~

我们看到除了基类,实际上并没有什么改变。

创建抽象类和抽象方法非常有用,因为他们可以使类的抽象性明确起来,并告诉用户和编译器打算怎样来使用它们。抽象类还是很有用的重构工具,因为它们使得我们可以很容易地将公共方法沿着继承层次结构向上移动。

 

9.2 接口

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

一个接口表示:“所有实现了该特定接口的类看起来都像这样”。因此,任何使用某特定接口的代码都知道可以调用该接口的哪些方法,而且仅需知道这些。因此,接口被用来建立类与类之间的协议。(某些面向对象编程语言使用关键字protocol来完成这一功能。)

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

要想创建一个接口,需要使用interface关键字来替代class关键字。就像一个类一样,可以在interface关键字前面添加public关键字(但仅限于该接口在与其同名的文件中被定义)。如果不添加public关键字,则它只具有包访问权限,这样它就只能在同一个包内可用。接口也可以包含域,但是这些域隐式地是static和final的。

要让一个类遵循某个特定接口或者一组接口,需要使用implements关键字,它表示:“interface只是它的外貌,但是现在我要声明它是如何工作的。”除此以外,它看起来还很像继承。“乐器”示例的图说明了这一点:

image.png

可以从WoodwindBrass类中看到,一旦实现了某个接口,其实现就变成了一个普通的类,就可以按常规方式扩展它。

可以选择在接口中显式地将方法声明为public的,但即使你不这么做,它们也是public的。因此,当要实现一个接口时,在接口中被定义的方法必须被定义为public的;否则它们将只能得到默认的包访问权限,这样在方法被继承的过程中,其可访问权限就被降低了,这是Java编译器所不允许的。

//: interfaces/music5/Music5.java // Interfaces. packageinterfaces.music5; 
importpolymorphism.music.Note; 
importstaticnet.mindview.util.Print.*; 
interfaceInstrument { 
// Compile-time constant: intVALUE=5; // static & final // Cannot have method definitions: voidplay(Noten); // Automatically public voidadjust(); 
} 
classWindimplementsInstrument { 
publicvoidplay(Noten) { 
print(this+".play() "+n); 
    } 
publicStringtoString() { return"Wind"; } 
publicvoidadjust() { print(this+".adjust()"); } 
} 
classPercussionimplementsInstrument { 
publicvoidplay(Noten) { 
print(this+".play() "+n); 
    } 
publicStringtoString() { return"Percussion"; } 
publicvoidadjust() { print(this+".adjust()"); } 
} 
classStringedimplementsInstrument { 
publicvoidplay(Noten) { 
print(this+".play() "+n); 
    } 
publicStringtoString() { return"Stringed"; } 
publicvoidadjust() { print(this+".adjust()"); } 
} 
classBrassextendsWind { 
publicStringtoString() { return"Brass"; } 
} 
classWoodwindextendsWind { 
publicStringtoString() { return"Woodwind"; } 
} 
publicclassMusic5 { 
// Doesn’t care about type, so new types // added to the system still work right: staticvoidtune(Instrumenti) { 
// ... i.play(Note.MIDDLE_C); 
    } 
staticvoidtuneAll(Instrument[] e) { 
for(Instrumenti : e) 
tune(i); 
    } 
publicstaticvoidmain(String[] args) { 
// Upcasting during addition to the array: Instrument[] orchestra= { 
newWind(), 
newPercussion(), 
newStringed(), 
newBrass(), 
newWoodwind() 
        }; 
tuneAll(orchestra); 
    } 
}

/* Output:

Wind.play() MIDDLE_C

Percussion.play() MIDDLE_C

Stringed.play() MIDDLE_C

Brass.play() MIDDLE_C

Woodwind.play() MIDDLE_C

*///:~

目录
相关文章
|
4天前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
|
5天前
|
Java
java线程接口
Thread的构造方法创建对象的时候传入了Runnable接口的对象 ,Runnable接口对象重写run方法相当于指定线程任务,创建线程的时候绑定了该线程对象要干的任务。 Runnable的对象称之为:线程任务对象 不是线程对象 必须要交给Thread线程对象。 通过Thread的构造方法, 就可以把任务对象Runnable,绑定到Thread对象中, 将来执行start方法,就会自动执行Runable实现类对象中的run里面的内容。
16 1
|
10天前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
38 4
|
17天前
|
安全 Java
在 Java 中使用实现 Runnable 接口的方式创建线程
【10月更文挑战第22天】通过以上内容的介绍,相信你已经对在 Java 中如何使用实现 Runnable 接口的方式创建线程有了更深入的了解。在实际应用中,需要根据具体的需求和场景,合理选择线程创建方式,并注意线程安全、同步、通信等相关问题,以确保程序的正确性和稳定性。
|
15天前
|
Java
Java基础(13)抽象类、接口
本文介绍了Java面向对象编程中的抽象类和接口两个核心概念。抽象类不能被实例化,通常用于定义子类的通用方法和属性;接口则是完全抽象的类,允许声明一组方法但不实现它们。文章通过代码示例详细解析了抽象类和接口的定义及实现,并讨论了它们的区别和使用场景。
|
15天前
|
Java 测试技术 API
Java零基础-接口详解
【10月更文挑战第19天】Java零基础教学篇,手把手实践教学!
16 1
|
20天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
15 3
|
20天前
|
Java
在Java多线程编程中,实现Runnable接口通常优于继承Thread类
【10月更文挑战第20天】在Java多线程编程中,实现Runnable接口通常优于继承Thread类。原因包括:1) Java只支持单继承,实现接口不受此限制;2) Runnable接口便于代码复用和线程池管理;3) 分离任务与线程,提高灵活性。因此,实现Runnable接口是更佳选择。
29 2
|
20天前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
27 2
|
20天前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
27 1