第九章 接口(上)

IEEE 802.1AS-2011 第九章 应用接口(API) 本文翻译自IEEE Standard for Local and metropolitan area networks—Timing and Synchronization for Time-Sensitive Applications in Bridged Local Area Networks 9. 应用接口 9.1 接口总览 以下子条款定义了ClockSource实体和ClockMaster... 阅读详情
                      第九章    接口(上)
         接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法。

9.1    抽象类和抽象方法
         包含抽象方法的类叫做抽象类。如果一个类包含一个或多个抽象方法,该类必须被限定为抽象的。(否则,编译器就会报错。)
         抽象类和抽象方法都用abstract修饰,例如:abstract void f();   abstract class Instrument{}
         第八章Instrument类可以很容易地转化成abstract类。既然使某个类称为抽象类并不需要所有的方法都是抽象的,所以仅需将某些方法声明为抽象的即可。如下图所示:
                 
                                
           下面是修改过的“管弦乐器”的例子,其中采用了抽象类和抽象方法:
     abstract class Instrument{
     private int i;
     public abstract void play(Note n);
     public String what(){
          return "Instrument";
     }
     public abstract void adjust();
}
class Wind extends Instrument{
     public void play(Note n){
          System.out.println("Wind.play"+n);
     }
     public void adjust(){}
     public String what(){
          return "Wind";
     }
}
class Percussion extends Instrument{
     public void play(Note n) {
          System.out.println("Percussion.play"+n);
     }
     public void adjust() {}
     public String what(){
          return "Percussion";
     }
}
class Stringed extends Instrument{

     @Override
     public void play(Note n) {
          System.out.println("Stringed.play"+n);
     }
     @Override
     public void adjust() {}
     public String what(){
          return "Stringed";
     }
}
class Brass extends Wind{
     public void play(Note n){
          System.out.println("Brass.play"+n);
     }
     public void adjust(){
          System.out.println("Brass.adjust");
     }
}
class Woodwind extends Wind{
     public void play(Note n){
          System.out.println("Woodwind.play"+n);
     }
     public String what(){
          return "Woodwind";
     }
}
public class Music4 {
     static void tune(Instrument i){
          i.play(Note.MIDDLE_C);
     }
     static void tuneAll(Instrument [] e){
          for(Instrument i:e){
               tune(i);
          }
     }
     public static void main(String[] args) {
          Instrument [] orchestra={
                    new Wind(),
                    new Percussion(),
                    new Stringed(),
                    new Brass(),
                    new Woodwind()
          };
          tuneAll(orchestra);
     }
}
运行结果:
Wind.playMIDDLE_C
Percussion.playMIDDLE_C
Stringed.playMIDDLE_C
Brass.playMIDDLE_C
Woodwind.playMIDDLE_C
      我们可以看出,除了基类,实际上并没有什么改变。
      创建抽象类和抽象方法非常有用,因为它们可以使类的抽象性明确起来,并告诉用户和编译器打算怎样来使用它们。抽象类还是很有用的重构工具,因为它们使得我们可以很容易地将公共方法沿着继承层次结构向上移动。

9.2  接口
       interface关键字使抽象类的概念更向前迈进了一步。abstract关键字允许人们在类中创建一个或多个没有任何定义的方法——提供了接口部分,但是没有提供任何相应的具体实现,这些实现是由此类的继承者创建的。interface这个关键字产生一个完全抽象的类,它根本就没有提供任何具体实现。它允许创建者确定方法名、参数列表和返回类型,但是没有任何方法体。接口只提供了形式,而未提供任何具体实现。
       一个接口表示:“所有实现了该特定接口的类看起来都像这样”。因此,任何使用某特定接口的代码都知道可以调用该接口的哪些方法,而且仅需知道这些。故接口被用来建立类与类之间的协议。(某些面向对象编程语言使用关键字protocol来完成这一功能。)
        但是,interface不仅仅是一个极度抽象的类,因为它允许人们通过创建一个能够被向上转型为多种基类的类型,来实现某种类似多重继承变种的特性。
       要想创建一个接口,需要用interface关键字来替代class关键字。就像类一样,可以在interface关键字前面添加public关键字(但仅限于该接口在与其同名的文件中被定义)。如果不添加public关键字,则它只是具有包访问权限,这样它就只能在同一个包内可用。接口也可以包含域,但是这些域隐式地是static和final的。
       要让一个类遵循某个特定接口(或者是一组接口),需要使用implements关键字,它表示:“interface只是它的外貌,但是现在我要声明它是如何工作的。”除此之外,它看起来还很像继承。“乐器”示例的图说明了这一点:
                 
                         
        可以从Woodwind和Brass类中看到,一旦实现了某个接口,其实现就变成了一个普通的类,就可以按常规方式扩展它。
        可以选择在接口中显式地将方法声明为public的,但即使你不这么做,它们也是public的。因此,当要实现一个接口时,在接口中被定义的方法必须被定义为是public的,否则,他们将只能得到默认的包访问权限,这样在方法被继承的过程中,其可访问权限就被降低了,这是Java编译器所不允许的。
     interface Instrument{
     int VALUE=5;
     void play(Note n);
     void adjust();
}
class Wind implements Instrument{
     public void play(Note n){
          System.out.println(this+".play()"+n);
     }
     @Override
     public void adjust() {
          System.out.println(this+".adjust()");
     }
     public  String toString(){
          return "Wind";
     }
}
class Percussion implements Instrument{
     @Override
     public void play(Note n) {
          System.out.println(this+".play()"+n);
     }
     @Override
     public void adjust() {
          System.out.println(this+".adjust()");
     }
     public  String toString(){
          return "Percussion";
     }
}
class Stringed implements Instrument{

     @Override
     public void play(Note n) {
          System.out.println(this+".play()"+n);
     }
     @Override
     public void adjust() {
          System.out.println(this+".adjust()");
     }
     public  String toString(){
          return "Stringed";
     }
}
class Brass extends Wind{
     public String toString(){
          return "Brass";
     }
}
class Woodwind extends Wind{
     public String toString(){
          return "Woodwind";
     }
}
public class Music5 {
     static void tune(Instrument i){
          i.play(Note.MIDDLE_C);
     }
     static void tuneAll(Instrument [] e){
          for(Instrument i:e){
               tune(i);
          }
     }
     public static void main(String[] args) {
          Instrument [] orchaestra={
                    new Wind(),
                    new Percussion(),
                    new Stringed(),
                    new Brass(),
                    new Woodwind()
          };
          tuneAll(orchaestra);
     }

}
运行结果:
Wind.play()MIDDLE_C
Percussion.play()MIDDLE_C
Stringed.play()MIDDLE_C
Brass.play()MIDDLE_C
Woodwind.play()MIDDLE_C

       此实例的这个版本还有另外一处改动:what()方法已经被修改为toString()方法,因为toString()的逻辑正是what()要实现的逻辑。由于toString()方法是根据类Object的一部分,因此它不需要出现在接口中。余下的代码其工作方式都是相同的。无论是将其向上转型为称为Instrument的普通类,还是称为Instrument的抽象类,或是称为Instrument的接口,都不会有问题。它的行为都是相同的。事实上,你可以在tune()方法中看到,没有任何依据来证明Instrument是一个普通类、抽象类,还是一个接口。

9.3  完全解耦
      只要一个方法操作的是类而非接口,那么你就只能使用这个类及其子类。如果你想要将这个方法应用于不在此继承结构中的某个类,那么你就会触霉头了。接口可以在很大程度上放宽这种限制,因此,它使得我们可以编写可复用性更好的代码。
      例如,假设有一个Processor类,它有一个name()方法;另外还有一个process()方法,该方法接受输入参数,修改它的值,然后产生输出。这个类做为基类而被扩展,用来创建个中不同类型的Processor。在本例中,Processor的子类将修改String对象(注意,返回类型可以是协变类型,而非参数类型)
     class Processor{
     public String name(){
          return getClass().getSimpleName();
     }
     Object process(Object input){
          return input;
     }
}
class Upcase extends Processor{
     String process(Object input){
          return ((String) input).toUpperCase();
     }
}
class Downcase extends Processor{
     String process(Object input){
          return ((String) input).toLowerCase();
     }
}
class Splitter extends Processor{
     String process(Object input){
          return Arrays.toString(((String) input).split(" "));
     }
}
public class Apply {
     public static void process(Processor p,Object s){
          System.out.println("Using Process "+p.name());
          System.out.println(p.process(s));
     }
     public static String s="Disagereement with beliefs is by definition incorrect";
     public static void main(String[] args) {
          process(new Upcase(),s);
          process(new Downcase(),s);
          process(new Splitter(),s);
     }
}
   运行结果:
Using Process Upcase
DISAGEREEMENT WITH BELIEFS IS BY DEFINITION INCORRECT
Using Process Downcase
disagereement with beliefs is by definition incorrect
Using Process Splitter
[Disagereement, with, beliefs, is, by, definition, incorrect]

       Apply.process()方法可以接受任何类型的Processor,并将其应用到一个Object对象上,然后打印结果。像本例这样,创建一个能够根据所传递的参数对象的不同而具有不同行为的方法称为策略设计模式。这类方法包含所要执行的算法中固定不变的部分,而“策略”包含变化的部分。策略就是传递进去的参数对象,它包含要执行的代码。这里,Processor对象就是一个策略,在main()中可以看到有三种不同类型的策略应用到了String类型的对象上。
       split()方法是String类的一部分,它接受String类型的对象,并以传递进来的参数作为边界,将该String对象隔开,然后返回一个数组String[]。 
       现在假设我们发现了一组电子滤波器,它们看起来好像适用于Apply.process()方法:
     public class Waveform {
     private static long counter;
     private final long id=counter++;
     public String toString(){
          return "Waveform"+id;
     }
}

   public class Filter {
     public String name(){
          return getClass().getSimpleName();
     }
}

public class LowPass extends Filter{
     double cutoff;
     public LowPass(double cutoff){
          this.cutoff=cutoff;
     }
     public Waveform process(Waveform input){
          return input;
     }

}

public class HighPass extends Filter {
     double cutoff;
     public HighPass(double cutoff){
          this.cutoff=cutoff;
     }
     public Waveform process(Waveform input){
          return input;
     }

}

public class BandPass extends Filter{
     double lowCutoff,highCutoff;
     public BandPass(double lowCut,double highCut){
          lowCutoff=lowCut;
          highCutoff=highCut;
     }
     public Waveform process(Waveform input){
          return input;
     }

}
       Filter与Processor具有相同的接口元素,但是因为它并非继承自Process——因为Filter类的创建者压根不清楚你想要将它用作Process——因此你不能将Filter用于Apply.process()方法,即便这样做可以正常运行。这里主要是因为Apply.process()方法和Process之间的耦合过紧,已经超出了所需要的程度,这样就使得应该复用Apply.process()的代码时,复用却被禁止了。另外还需要注意的是它们的输入和输出都是Waveform。
       但是,如果Process是一个接口,那么这些限制就会变得松动,使得你可以复用结构该接口的Apply.process()。下面是Processor和Apply的修改版本:
     public interface Processor {
     String name();
     Object process(Object input);
} 
     
     public class Apply {
     public static void process(Processor p,Object s){
          System.out.println("Using Process "+p.name());
          System.out.println(p.process(s));
     }
复用代码的第一种方式是客户端程序员遵循该接口来编写他们自己的类,就像下面这样:
public abstract class StringProcessor implements Processor{
     public String name(){
          return getClass().getSimpleName();
     }
     public abstract String process(Object input);
     public static String s="If she weighs the same as a duck ,she's made of wood";
     public static void main(String[] args) {
          Apply.process(new Upcase(), s);
          Apply.process(new Downcase(), s);
          Apply.process(new Splitter(), s);
     }

}
class Upcase extends StringProcessor{
     public String process(Object input){
          return ((String) input).toUpperCase();
     }
}
class Downcase extends StringProcessor{
     public String process(Object input){
          return ((String) input).toLowerCase();
     }
}
class Splitter extends StringProcessor{
     public String process(Object input){
          return Arrays.toString(((String) input).split(" "));
     }
}
运行结果:
Using Process Upcase
IF SHE WEIGHS THE SAME AS A DUCK ,SHE'S MADE OF WOOD
Using Process Downcase
if she weighs the same as a duck ,she's made of wood
Using Process Splitter
[If, she, weighs, the, same, as, a, duck, ,she's, made, of, wood]


       但是,你经常碰到的情况是你无法修改你想要使用的类。例如,在电子滤波器的例子中,类库是被发现而非创建的。在这些情况下,可以使用适配器设计模式。适配器中的代码将接受你所拥有的接口,并产生你所需要的接口,就像下面这样:
     class FilterAdapter implements Processor{
     Filter filter;
     public FilterAdapter(Filter filter){
          this.filter=filter;
     }
     @Override
     public String name() {
          return filter.name();
     }
     @Override
     public Waveform process(Object input) {
          return filter.process((Waveform)input);
     }
}
public class FilterProcessor{
     public static void main(String[] args) {
          Waveform w = new Waveform();
          Apply.process(new FilterAdapter(new LowPass(1.0)), w);
          Apply.process(new FilterAdapter(new HighPass(2.0)), w);
          Apply.process(new FilterAdapter(new BandPass(3.0,4.0)), w);
     }
}
  运行结果:
Using Process LowPass
Waveform0
Using Process HighPass
Waveform0
Using Process BandPass
Waveform0

         在这种使用适配器的方式中,FilterAdapter的构造器接受你所拥有的接口Filter,然后生成具有你所需要的Processor接口的对象。你可能还注意到了,在FilterAdapter类中用到了代理。
        将接口从具体实现中解耦使得接口可以应用于多种不同的具体实现,因此代码也就更具可复用性。
第九章、接口开发 一、查询接口开发 2 2 2 2 二、增加接口开发 三、删除接口开发 四、更新接口开发 五、复合查询接口开发 2 2 2 2 阅读详情

相关推荐

关于Win11安装Ubuntu子系统报错:WslRegisterDistribution failed with error 解决办法

Win11安装Ubuntu子系统报错:WslRegisterDistribution failed with error

qq_51908382的博客 3万+

Android谈起泛型,大家都很佛性,kotlin扩展函数equal

return getClass().getSimpleName(); } } 还有测试类Test2th: public class Test2th extends TestBase { @Override public String toString() { return getClass().getSimpleName(); } } 在使用时,我们可以这样为泛型类Tested指定泛型参数类型。迭代如下: public void startTest() { Tested<Test1th, Test2t

m0_65511857的博客 725

Godot 2D游戏开发实战:用GDScript复刻10款经典小游戏(附完整源码)

本文是一份Godot 2D游戏开发实战指南,通过使用GDScript语言复刻10款经典小游戏,帮助开发者快速掌握引擎核心功能。内容涵盖从环境配置、GDScript基础到贪吃蛇、Flappy Bird、打砖块、俄罗斯方块等项目的核心逻辑与代码实现,并附有完整源码,旨在通过实践提升独立开发能力。

gold的博客 777

接口

接口

csdn1428208016的博客 362

Rockchip MPP解码H264实战:从文件到YUV的完整流程解析

本文详细解析了在Rockchip平台上使用MPP(Media Process Platform)硬件解码H264视频文件并输出YUV数据的完整流程。通过实战代码示例,深入讲解了MPP解码器的初始化、数据包投递、非阻塞模式下的双循环控制以及YUV帧的提取与保存等核心步骤,并提供了环境搭建、编译运行和常见问题排查指南,帮助开发者高效利用硬件加速进行视频处理。

life6的博客 478

蒙特卡洛+概率潮流!基于蒙特卡洛和新能源出力模拟的概率潮流分布程序代码!

最后,导出光照强度、风速、网损和节点电压的概率分布图,并计算了各节点电压和网损的平均值和方差,展示了接入前后的节点电压对比图。在不确定性增加的电力系统中,概率潮流成为含随机因素规划问题的求解前提,可以更加真实地反映电力系统的全面信息。此外,概率潮流还可用于研究风电和光伏发电出力以及电动汽车消耗功率和向电力系统反向供电功率的不确定性对电力系统的影响,发现系统运行中的潜在风险,为系统运行控制提供参考。概率潮流的目的是揭示电网中随机变量对系统的影响,发现电网中的薄弱环节,为规划和调度部门的决策提供有价值的信息。

weixin_48747334的博客 1918

类与接口

抽象类 abstract 关键字表明一个抽象方法或抽象类,包含抽象方法的类叫做抽象类。如果一个类包含一个或多个抽象方法,该类必须被限定为抽象的 如果从一个抽象类继承并想创建该新类的对象,那么就必须为基类中的所有抽象方法提供定义,如果不这样做,那么导出类便也是抽象类,且编译器将会强制我们用abstract关键字来限定这个类 interface interface不仅仅是一个极

gaokewoo的专栏 532

Spring AI Alibaba实战训练营-16 NL2SQL (自然语言转 SQL)应用开发指南

本文介绍了一个基于AI的智能BI应用开发项目,主要实现自然语言转SQL(NL2SQL)功能,包含对话模块、MCP模块、向量数据管理模块。项目采用Spring AI Alibaba框架,集成通义千问大模型,支持内存向量库和AnalyticDB向量数据库两种存储方案。重点展示了chat模块的三个控制器实现:AnalyticNl2SqlController用于生产环境,SimpleChatController适用于开发测试,Nl2sqlForGraphController支持基于图的复杂对话流程。系统通过将业务知

program哲学 1583

【笔记】《Java编程思想(第四版)》第9章-接口

第9章 接口 接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法。 这种机制在编程语言中并不通用。例如C++对这些概念只有间接的支持。在Java 中存在语言关键字这个事实表明人们认为这些思想是很重要的,以至于要提供对它们的直接支持。 9.1 抽象类和抽象方法 Instrument类的目的是为它的所有导出类创建一个通用接口。 不同子类可以用不同方式表示此接口。通用接口建立起一种基本形式,...

ZeromaXHe的博客 342

一文吃透 SAP HANA SDA:把分散数据当作本地数据来用的那把钥匙

摘要:SAP HANA SDA(Smart Data Access)技术详解:150字精要 SAP HANA SDA是一种数据虚拟化技术,允许在不复制数据的情况下访问远端数据源。核心功能包括: 通过Remote Source定义连接配置 创建Virtual Table作为本地代理 支持谓词下推优化查询性能 与SDI技术协同工作扩展数据集成能力 典型应用场景包括: 跨系统联邦查询 冷热数据分层管理 混合云数据整合 远程表复制(RTR)实现近实时数据同步 实施要点: 需关注适配器能力差异 合理运用查询下推优化

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 318

Qt入门教程【基础控件篇】QLineEdit输入框

QLineEdit输入框

小鱼酱的博客 3260

【学习笔记】-多态、方法重写、抽象、接口

多态的定义 Java中多态性指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式 发送消息就是方法调用 现实中,关于多态的例子不胜枚举。比方说按下 F1 键这个动作,如果当前在IE界面下弹出的浏览器的帮助文档;如果当前在 Word 下弹出的就是 office帮助;在 Windows 下弹出的就是 Windows 帮助和支持 可见,同一个事件发生在不同的对象上会产生不同的结果,因此,多态的主要作用适用于消除类型之间的耦合关系 多态存在的三个必要条件

kevin_g的博客 269

零基础学simulink仿真建模--使用Simulink来构建一个混合动力无人机中燃料电池与锂电池协同控制的仿真模型

延长飞行时间:燃料电池的能量密度高于锂电池,适合长时间稳定供电。提高功率密度:锂电池能够快速响应瞬时高功率需求,比如起飞和加速阶段。优化能源管理:通过有效的能量管理系统,可以根据实际需求动态调整燃料电池和锂电池的工作状态,从而提高整体效率。通过上述步骤,我们简要介绍了如何基于Simulink实现混合动力无人机中燃料电池与锂电池协同控制的仿真模型。

amy_mhd的博客 189

第九章 接口

文章目录1.抽象类和抽象方法2.接口3.完全解耦复用代码4.Java的多重继承5.通过继承来拓展接口6.适配接口7.接口中的域8.嵌套接口9.接口与工厂 1.抽象类和抽象方法 抽象类(抽象基类):通用接口建立起一种基本形式,以此表示导出类的共同部分 如果一个类包含一个或多个抽象方法,该类必须限定为抽象类 如果从一个抽象继承的,并像创建该新类对象,那么就必须为基类中的所有抽象方法提供方法定义 我们可以创建一个没有任何抽象方法的抽象类 抽象类和抽象方法可以使类的抽象性明确起来,并告诉用户和编译器怎么使用他们

小名同学 22万+

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

thinking in java 笔记 思维导图 第九章 接口

mythling的专栏 620

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Java编程思想第四版读书笔记——第九章 接口

这章介绍了适配器设计模式和策略设计模式。 第九章  接口 接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法。 1、抽象类和抽象方法 public abstract void f(); 创建抽象类是希望通过这个通用接口操纵一系列类。如果一个类包含大于等于一个抽象方法,那么这个类就是抽象类,必须用abstract关键字来限定这个抽象类。 如果试图直接创

severusyue的博客 1738

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

【JAVA SE】第九章 接口

【JAVA SE】第九章 接口

2022年毕业的萌新程序猿,输出一下自己的学习内容~ 428

第九章:接口

第九章:接口 接口和内部类为我们提供了一种接口和实现分离的更加结构化的方法。 一、抽象类和抽象方法(abstract) 抽象类:将所有导出类的共同部分抽取出来,作为一个公共的接口(方法) 优点:可以利用这个接口(抽象类)操作一系列类 特点:1、含有抽象方法的类,可以含有不抽象的具体实现方法,必定是抽象类(abstract),使用抽象限制。 ​ 2、当然抽象类,可以没有抽象方法。也无法创建对象(...

jue6628的博客 342

第九章 抽象类和接口

Java面向对象基础 第九章 抽象类和接口 一、抽象类 1、抽象方法 (1)对于父类的有些方法,子类在继承父类之后,都会对这些方法进行重写,来满足自己的业务需求,那么向这样的方法,就没有必要存在方法体,那么Java就提供了抽象方法这一概念。 (2)使用abstract关键字修饰的没有方法题的方法,就称之为抽象方法。抽象方法存在的最大作用就是约定子类应该具有该方法,并且必须对这些抽象方法进行重写,提供可运行的方法体。 (3)抽象方法的语法结构: [访问修饰符] abstract 返回值类型 方法名(

yrxfight的博客 219

第九章 抽象类 & 接口

9.1 abstract 概述 类中有相似方法,但是又有不同,此方法应定义为抽象方法,类也必须为抽象类。 特点: 1)方法只有声明没有实现 2)抽象类不可以被实例化 3)抽象类的子类必须覆盖其所有抽象方法才能被实例化 9.2 抽象类细节 1)有构造函数,用于其子类实例化 2)抽象类可以不包含抽象方法,目的是不让该类被实例化 (eg:AWT Adapter) 3)不能与ab

Alex Bai的博客 290
上一篇: Findbugs工具的安装和使用
下一篇: 第九章 接口(下)
OODeveloper
博客等级 码龄12年 43粉丝 137原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值