抽象方法:
使用 abstract 修饰的方法,没有方法体,只有声明。
定义的是一种 “规范”,就是告诉子类必须要给抽象方法提供具体的实现。
抽象类:
包含抽象方法的类就是抽象类。
通过 abstract 方法定义规范,然后要求子类必须定义具体实现。
通过抽象类,可以做到严格限制子类的设计,使得子类之间更加通用。
package cn.jungle.polymophism; // 定义抽象类 abstract class Animal1{ // 定义抽象方法,规定子类必须实现的方法 abstract public void shout(); } class Dog1 extends Animal1{ // 子类必须实现父类的抽象方法,否则编译错误 public void shout(){ System.out.println("子类必须实现父类的抽象方法,否则编译错误"); System.out.println("听到有狗的警告叫声!"); } public void seeDog(){ System.out.println("发现一只狼狗正在看门!"); } } public class TestAbstract { public static void main(String[] args) { Dog1 a = new Dog1(); a.shout(); a.seeDog(); } }
接口:将设计与实现彻底分开。
接口即是规范,定义的是一组规则。思想为:“如果你是……则必须能……”,比如你是飞机,则必须能飞。
接口的本质是契约,如同国家规定的法律,必须去遵守。
面向对象的精髓,是对对象的抽象,接口最能体现这一点。
(1)为何需要接口?
接口是比 “抽象类” 还要 “抽象” 的 “抽象类”,可以更加规范的对子类进行约束。
全面且专业地实现了:规范和具体实现的分离。
(2)接口与抽象类的区别?
抽象类会提供某些具体的实现,接口不提供任何实现,接口中所有方法都是抽象方法。
接口是完全面向规范的,规定了一批类具有的公共方法规范。
从接口的实现者角度看,接口定义了可以向外部提供的服务。
从接口的调用者角度看,接口定义了实现者能够提供哪些服务。
接口是两个模块之间通信的标准,通信的规范。
接口和实现类不是父子关系,是实现规则的关系。
(3)本质区别
接口中只能定义方法和常量
[访问修饰符] interface 接口名 [extends 父接口1,父接口2……] { 常量定义: 方法定义 }
两个文件:Volant.java(接口定义文件)、SuperMan.java(主文件)
(1)Volant.java
package cn.jungle.test.TestInterface; /** * 这里是一个飞行器的接口。 * * */ public interface Volant { /** * 接口中定义一个 FLY_HEIGHT 常量。 * 这里表示飞行器在地球上的飞行最高高度,单位:公里。 */ /* public static final 这里可写可不写*/ int FLY_HEIGHT = 1000; /** * 接口中定义一个 fly() 抽象方法。 * 定义飞行方法,表示飞行器可以起飞。 */ /* public abstract */ void fly(); /** * 接口中定义一个 stop() 抽象方法。 * 定义停止方法,表示可以让飞行器停止。如果在空中就是飞行,在地面就是静止。 */ /* public static final */ void stop(); } /** * 一个行为接口,帮助他人 */ interface Honest{ void helperOther(); }
(2)SuperMan.java
package cn.jungle.test.TestInterface; // 定义超人的飞行类和行为类 public class SuperMan implements Volant,Honest { // 重写父类方法 @Override public void fly() { System.out.println("从天而降"); } // 重写父类方法 @Override public void stop() { System.out.println("空中悬停"); } // 重写父类方法 @Override public void helperOther() { System.out.println("哪里需要帮助,哪里就有我马丁超人!"); } // main 方法入口,可以定义在这里,也可以单独创建一个 java 文件来存放 main 方法 public static void main(String[] args) { // 定义类对象 SuperMan m1 = new SuperMan(); // 类对象调用方法 m1.fly(); m1.helperOther(); m1.stop(); /** * 另一种调用方式,定义一个多态对象,然后通过强转去调用方法,但是比较麻烦 */ } }
Java8 之前,接口里的方法要求全部是抽象方法。
Java8(含8) 之后,允许在接口里使用默认方法和类方法。
Java8 及以上旧版本,允许给接口添加一个非抽象的方法实现,只需要使用 default 关键字即可,这个特征又叫做默认方法(也称为扩展方法)。
默认方法和抽象方法的区别是抽象方法必须要被实现,默认方法并不是。
作为替代方式,接口可以提供默认方法的实现,所有这个接口的实现类都会通过继承得到这个方法。
调用接口的默认方法需要实现类的对象来调用。
package cn.jungle.test.TestInterface; /** * 测试接口中定义默认方法 */ public class Test01 { public static void main(String[] args) { // 父类引用指向子类对象。调用接口的默认方法需要实现类的对象来调用。 A a = new Test_A(); a.moren(); } } // 定义接口 interface A{ // 用 default 关键字来定义默认方法 default void moren(){ System.out.println("这里是接口 A 中的默认方法。"); System.out.println("如果默认方法没有被重写,调用时则会执行默认方法中的语句。"); } } // 定义普通类 class Test_A implements A{ // moren 方法的重写 @Override public void moren() { System.out.println("接口中的默认方法重写以后,调用时会被执行,不会再去执行接口中的默认方法语句。"); } }
Java8 以后,可以在接口中直接定义静态方法的实现。
这个静态方法直接从属于接口(接口也是类,一种特殊的类),可以通过接口名调用。
如果子类中定义了相同名字的静态方法,那就是完全不同的方法了,直接从属于子类。可以通过子类名直接调用。
如果调用静态方法,是用 “类名.静态方法名” 去调用。静态方法从属于类。
静态方法中不能调普通方法,普通方法中可以调静态方法。
接口完全支持多继承。接口可以有多个父类接口
和类的继承类似,子继承扩展某个父接口,将会获得父接口中定义的一切。
package cn.jungle.test.TestInterface; /** * 测试接口的多继承 */ public class Test02{ public static void main(String[] args) { Test t = new Test(); t.testB(); t.testC(); t.testD(); } } interface B{ void testB(); } interface C{ void testC(); } /* 接口可以多继承,接口 D 继承 B 和 C*/ interface D extends B,C{ void testD(); } // Test 类作为 C 的实现类。实现类需要实现各个接口中的方法,即重写 class Test implements D{ // 实现类中重写和实现接口的方法 @Override public void testB() { System.out.println("testB() 方法"); } // 实现类中重写和实现接口的方法 @Override public void testC() { System.out.println("testC() 方法"); } // 实现类中重写和实现接口的方法 @Override public void testD() { System.out.println("testD() 方法"); } }