JavaSE基础知识(十九)--Java接口之在理解了抽象类的基础上理解接口

Java SE 是什么,包括哪些内容(十九)?

本文内容参考自Java8标准
再次感谢Java编程思想对本文的启发!

接口是在抽象类的基础上进一步抽象。

一开始是public class:extends
然后是public abstract class:extends
现在是public interface…:改成了implements
interface关键字使抽象的概念更向前迈进了一步。
abstract关键字允许人们在类中创建一个或多个没有任何定义的方法,也就是提供了接口部分,没有提供任何相应的具体实现,这些实现是由继承此类的子类去实现的。
但是它还是允许在这个类中存在完全实现了的方法。
interface关键字产生一个完全抽象的类,它里面的所有方法都没有提供实现,也就是所有的方法都不能有方法体。仅允许创建者确定方法名,参数列表和返回类型,接口只提供了形式而已。
它是完全不允许有任何实现了的方法。
一个接口表示:所有实现了(改成了implements,不再是extends)该特定接口的的类看起来都像这样,因此,任何使用某特定接口的代码都知道可以调用这个接口的哪些方法(实际上,可以调用这个接口的所有的方法,因为接口中的所有方法都默认是public的),而且仅需要知道这些。
因此,接口被用来建立类与类之间的协议(某些面向对象编程语言使用关键字protocol来完成这一功能表示)
在Java编程思想中文版第四版描述接口的章节中,有这么一句话:“但是,interface不仅仅是一个极度抽象的类,因为它允许人们通过创建一个能够被向上转型为多种父类的类型,来实现某种类型类似多重继变种的特性
说实话,上面这句话的翻译是在是不敢恭维,其实,这句话需要表达的意思是:interface是Java中解决多重继承的最优方案,一个class允许implements多个interface,当需要的时候,这个class可以选择其中一个interface进行向上转型,所以这个class可以向上转型为多种类型(一个interface代表了一种类型)。这里简单提一下,后面会进行详细的描述。
回到接口的话题…
要想创建一个接口,需要用interface关键字来代替class关键字。就像类一样,可以在interface关键字前面加上public关键字(与class一样,保存这个接口的文件的名称要与这个public关键字修饰的接口名称完全一致,并且在这个文件中只能有一个public关键字修饰的接口):
只能有一个public关键字修饰的接口:
只能有一个public关键字修饰的接口
如果不显式添加public关键字,则它只有包访问权限,这样它就只对固定的这个包内的其他类可见了,包外不可见。
接口也可以包含类变量,但是这些类变量隐式地被static和final修饰了(这是编译器的默认行为,当然你也可以进行显式声明)。
接口中的类变量默认就是static和final的:
接口中的变量默认是static和final的。
在IDE中,你从变量a和b都变蓝色并且加粗了也间接能看出来,在IDE中,被staitc和final同时修饰的变量就是变蓝以及加粗的。
要让一个类遵循特定的接口(或者是一组接口),需要使用implements关键字,它表示"interface只是一个外貌,现在由这个类来具体实现它是如何工作的"。实际上,这种形式看起来和继承差别不大,下面我继续使用类Instrument来说明这一点(一开始Instrument是类,后来变成了抽象类,现在变成接口):
如图:
implements和extends
从图中的类Woodwind和类Brass可以看到,类Wind、Percussion、Stringed实现了接口后,就变成了一个普通的类,就可以按常规的类继承方式去扩展它(类Woodwind和类Brass继承了类Wind)。
可以选择在接口中显式地将方法声明为public的,但即使你不这么做,这些方法默认也是public的,因此,当你要用一个类去实现一个接口的时候,这个接口中的所有方法在你的类中都必须显式声明为public的,如果不显示声明为public的,这些方法将只能得到包访问权限,这样,接口的方法在子类实现的过程中,其访问权限就从public降低为了包访问权限,这是Java编译器所不允许的。
在实现一个接口的时候,这个接口的所有的方法在实现的类中都必须是public的(因为这些方法在接口中就是public的)。因为Java编译器不允许在实现一个接口的过程中降低了接口方法的访问权限。
代码示例:
下面的代码示例中展示了以下几点:
⑴、接口中的每一个方法确实都只是一个声明,这是编译器所允许的在接口中唯一存在的事物。
⑵、接口Instrument中没有任何方法被声明为public的,因为它们默认就是public的。

// 接口Instrument
   interface Instrument{
      //默认就是static final的,这是编译期常量,因为它的值在编译期就确定了.
      int VALUE = 5;
      //只能有方法的声明,不能有方法体,方法声明包括:方法名称,形式参数
      //方法自动是public的
      void play(Note n);
      //只能有方法的声明,不能有方法体
      //方法自动是public的
      void adjust();
   }
   //类Wind实现接口Instrument,需要给接口Instrument的所有方法都提供实现
   class Wind implements Instrument{
      //实现接口中的方法play(Note n)
      public void play(Note n){
         //打印字符串this + ".play()" + n
         System.out.println(this + ".play()" + n);
      }
      //子类Wind中新增的方法toString()
      public String toString(){
         //返回字符串"Wind"
         return "Wind";
      }
      //实现接口中的方法adjust()
      public void adjust(){
         //打印字符串this + "adjust()"
         System.out.println(this + ".adjust()");
      }
   }
   //类Percussion实现接口Instrument,需要给接口Instrument的所有方法都提供实现
   class Percussion implements Instrument{
      //实现接口中的方法play(Note n)
      public void play(Note n){
         //打印字符串this + ".play()" + n
         System.out.println(this + ".play()" + n);
      }
      //子类Percussion中新增的方法toString()
      public String toString(){
         //返回字符串"Percussion"
         return "Percussion";
      }
      //实现接口中的方法adjust()
      public void adjust(){
         //打印字符串this + "adjust()"
         System.out.println(this + ".adjust()");
      }
   }
   //类Stringed实现接口Instrument,需要给接口Instrument的所有方法都提供实现
   class Stringed implements Instrument{
      //实现接口中的方法play(Note n)
      public void play(Note n){
         //打印字符串this + ".play()" + n
         System.out.println(this + ".play()" + n);
      }
      //子类Stringed中新增的方法toString()
      public String toString(){
         //返回字符串"Stringed"
         return "Stringed";
      }
      //实现接口中的方法adjust()
      public void adjust(){
         //打印字符串this + "adjust()"
         System.out.println(this + ".adjust()");
      }
   }
   //类Woodwind继承类Wind
   public Woodwind extends Wind{
      //方法toString()
      public String toString(){
         //返回字符串"Woodwind"
         return "Woodwind";
      }
   }
   //类Brass继承类Wind
   public Brass extends Wind{
      //方法toString()
      public String toString(){
         //返回字符串"Brass"
         return "Brass";
      }
   }
   public class Music5{
      //方法tune(Instrument i),带一个Instrument类型的形式参数
      //这个方法忽略了传入的实际参数的具体类型,只要是抽象Instrument
      //的子类都可以,所以如果需要增加新的类型,只要继承了抽象Instrument
      //就能起作用
      //方法play(Note n)默认开启了后期绑定(多态)
      //会表现出传入的实际参数类型对象的对应行为。
      static void tune(Instrument i){
         //调用方法play(Note n)
         i.play(Note.MIDDLE_C);
      }
      //方法tuneAll(Instrument[] e),带一个Instrument数组类型的形式参数e
      static void tuneAll(Instrument[] e){
         //循环遍历数组中的每一个对象
         for(Instrument i : e){
            //将每个对象作为实际参数传入方法tune(Instrument i)
            //中,会表现出不同的,与实际对象类型对应的行为。
            tune(i);
         }
      }
      //程序执行入口main方法
      public static void main(String[] args){
         //创建Instrument数组类型的对象,引用为orchestra
         Instrument[] orchestra = {
            //自动向上转型为Instrument类型
            new Wind(),
            //自动向上转型为Instrument类型
            new Percussion(),
            //自动向上转型为Instrument类型
            new Stringed(),
            //自动向上转型为Instrument类型
            new Brass(),
            //自动向上转型为Instrument类型
            new Woodwind()
         };
         //由于Java中方法默认为动态绑定,将会根据实际的对象类型
         //表现出具体的行为(多态)。
         tuneAll(orchestra);
      }
   }

其实你会发现,接口示例代码的工作方法和上一篇博文中抽象类的示例代码是一样的。
从普通类、抽象类、甚至是这篇博文的接口看来,针对类Instrument,它的行为都是相同的,它的子类无论是向上转型为Instrument普通类、还是Instrument抽象类、还是Instrument接口,都不会有问题。实际上在方法tune(Instrument i)中,没有任何的依据来证明类Instrument到底是一个普通类、抽象类、还是一个接口。
PS:时间有限,有关Java SE的内容会持续更新!今天就先写这么多,如果有疑问或者有兴趣,可以加QQ:2649160693,并注明CSDN,我会就博文中有疑义的问题做出解答。同时希望博文中不正确的地方各位加以指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值