一.抽象方法及抽象类
1-1 抽象方法
抽象方法:这种方法是不完整的,仅有声明而没有方法体。
public abstract void f();
1-2 抽象类
- 包含抽象方法的类一定是抽象方法,该类必须使用abstract关键字将其限定为抽象的,否则编译器会报错;
- 抽象类可以不包含抽象方法;
- 如果从一个抽象类继承,并想创建该导出类的对象,那么就必须为基类中的所有抽象方法提供方法定义(方法体)。如果不这样做(可以选择不这样做),那么导出类依然是抽象类,且编译器会强制我们用abstract关键字来限定这个导出类;
- 不能为抽象类创建对象。
虽然不能为抽象类创建对象,但是抽象类中的非抽象方法仍然可以通过抽象类的引用去调用,以下为一种情形:
package com.hutao.page.page170; import static com.hutao.util.Print.*; 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){ print("Wind.play()"+n); } public void adjust(){} } public class Music4 { public static void main(String[] args) { Instrument instrument = new Wind(); print(instrument.what()); } }
运行结果为:
Instrument
Process finished with exit code 0
这里能通过抽象类引用instrument调用其非抽象方法what()是因为导出类Wind没有覆盖抽象类Instrument的what()方法,所以通过基类引用调用what()方法时,根据总是调用最派生方法的原理(Most derived,在https://www.cnblogs.com/certainTao/p/14798451.html的1-3中有介绍),调用的就是基类的what()方法。
二.接口
interface关键字产生一个完全抽象的类,它不提供任何具体的实现。
接口可以包含域,但这些域是隐式static和final的。
- 是static的原因可能是:接口不能被实例化,域只能与类相关,所以为static;
- 是final的原因可能是:interface设计的初衷就是将其作为规范的,不可更改,所以域也是不可以被修改的。
接口中的方法声明默认是public和abstract的,这意味着可以不显示的指明public abstract修饰。
public abstract void print(); = void print();
注意:在实现接口中的方法时,实现类中实现方法的修饰符应保持>=接口中对应的方法声明的修饰符。因为接口中的方法声明默认是public的,那么实现类中实现方法的修饰符也应是public的(public>protected>包访问权限>private)。
2-1 通过继承扩展接口
可通过继承在新接口中组合多个接口。extends可用于多个接口基类(接口可以多继承)。
一个接口的实现类对象可以向上转型为该接口的类型及其所有祖先接口(有继承关系)类型。
package com.hutao.page.page180; interface Monster{ void menace(); } interface DangerousMonster extends Monster{ void destroy(); } class DragonZilla implements DangerousMonster{ public void menace(){} public void destroy(){} } interface Lethal{ void kill(); } //接口可通过继承(支持多继承)来扩展接口 interface Vampire extends DangerousMonster, Lethal{ void drinkBlood(); } class VeryBadVampire implements Vampire{ public void menace(){} public void destroy(){} public void kill(){} public void drinkBlood(){} } public class HorrorShow { static void m(Monster m){ m.menace(); } static void d(DangerousMonster d){ d.menace(); d.destroy(); } static void l(Lethal l){ l.kill(); } static void v(Vampire v){ v.menace(); v.destroy(); v.kill(); v.drinkBlood(); } public static void main(String[] args) { DangerousMonster dangerousMonster = new DragonZilla(); m(dangerousMonster); d(dangerousMonster); Vampire vampire = new VeryBadVampire(); m(vampire); d(vampire); l(vampire); v(vampire); } }
上述接口和类之间的关系为:
所以DangerousMonster接口的实现类对象new DragonZilla()可向上转型为DangerousMonster,Monster类型;Vampire接口的实现类对象new VeryVadVampire()可向上转型为Vampire,DangerousMonster,Lethal,Monster类型。
2-2 组合接口时的名字冲突
前提:一个类或者接口中不允许出现两个签名(方法名和形参列表)一致的方法,不论这两个方法的返回值类型是否一致。
class SameErasure { void method() { } int method() { return 1; } }
现在有这样一组接口和类:
interface I1{ void f(); } interface I2{ int f(); } class C{ public void f(){ System.out.println("C.f()"); } }
①一个新的接口AssemblyInterface组合接口I1和I2:
interface AssemblyInterface extends I1, I2{ }
分析:因为接口I1和I2中的两个方法的签名相同,而接口AssemblyInterface组合了这两个接口,就相当于AssemblyInterface接口中有了两个签名一样的方法,不被允许。
②一个类AssemblyClass1继承了类C并实现I1接口:
class AssemblyClass1 extends C implements I1{ } public class Test{ public static void main(String[] args) { AssemblyClass1 assemblyClass1 = new AssemblyClass1(); assemblyClass1.f(); } }
运行结果为:
C.f()
Process finished with exit code 0
这里有个现象:AssemblyClass1类 implements了I1接口 ,看起来并未在类中实现I1接口中的void f()方法,但却能创建此类的对象,并成功调用void f()方法(编译通过)。
分析:C类中有一个有方法体的public方法void f(),所以子类AssemblyClass1类中也就有一个pulic void f()方法(有方法体);I1接口中有一个void f()声明,所以实现类AssemblyClass1中就有一个void f()方法需要实现。而此时,AssemblyClass1类中已经一个有方法体的void f()方法了,相当于继承自父类的方法实现了接口中需要实现的方法。
③一个类AssemblyClass2继承了类C并实现了I2接口:
class AssemblyClass2 extends C implements I2{ }
分析:AssemblyClass2类中有一个继承而来的public void f()方法,因为AssemblyClass2实现了I2接口,所以就需要在类中去实现方法public int f(),这就会出现一个类中有两个签名一样的方法的情况。
来源:https://www.cnblogs.com/certainTao/p/14825528.html