Java抽象方法、抽象类以及接口
1.抽象方法
由于多态的存在,每个子类都可以覆写父类的方法。
class Person { public void run() { … } } class Student extends Person { @Override public void run() { … } } class Teacher extends Person { @Override public void run() { … } }
从Person类派生的Student和Teacher都可以覆写run()方法。
如果父类的方法本身不需要实现任何功能,仅仅是为了定义方法签名,
目的是让子类覆写它,那么可以把父类的方法声明为抽象方法。
class Person { public abstract void run(); }
把一个方法声明为abstract,表示它是一个抽象方法,本身没有实现任何方法语句。
因为这个抽象方法本身是无法执行的,所以,Person类也无法被实例化,无法编译Person类,因为它包含抽象方法。
必须把Person类本身也声明为abstract,才能正确编译它,其实这就是下面要说的抽象类:
abstract class Person { public abstract void run(); }
2.抽象类
如果一个class定义了方法,但没有具体执行代码,这个方法就是抽象方法,抽象方法用abstract修饰。
因为无法执行抽象方法,因此这个类也必须声明为抽象类(abstract class).
使用abstract修饰的类就是抽象类。我们无法实例化一个抽象类。
抽象类本身被设计成只能被用于被继承。因此,抽象类可以强迫子类实现其定义的抽象方法,
否则编译会报错。因此,抽象方法实际上相当于定义了“规范”。
package com.imooc.objectoriented; abstract class Person { public abstract void run(); } class Student extends Person { public void run() { System.out.println("Student run"); }} public class ObjectMethod2 { public static void main(String[] args) { Person p = new Student(); p.run(); } }
3.接口
在抽象类中,抽象方法本质上是定义接口规范:即规定高层类的接口,从而保证所有子类都有相同的接口实现。
如果一个抽象类没有字段,所有方法全部都是抽象方法:
abstract class Person { public abstract void run(); public abstract String getName(); }
就可以把该抽象类改写为接口:interface。
在Java中,使用interface可以声明一个接口:
interface Person { void run(); String getName(); }
所谓interface,就是比抽象类还要抽象的纯抽象接口,因为它连字段都不能有。
因为接口定义的所有方法默认都是public abstract(抽象方法)的。
当一个具体的class去实现一个interface时,需要使用implements关键字。
package com.imooc.objectoriented; interface Person { void run(); String getName(); } class Student implements Person { private String name; public Student(String name) { this.name =name; } public void run() { System.out.println(this.name + "run"); } public String getName() { return this.name } } public class ObjectMethod2 { public static void main(String[] args) { Student s = new Student("mao"); } }
接口类中定义的方法,子类中必须实现。
在Java中,一个类只能继承自另一个类,不能从多个类继承。
但是,一个类可以实现多个interface。
class Student implements Person,Hello { // 实现了两个interface }
一个interface可以继承另一个interface。
interface Hello { void hello(); } interface Person extends Hello { void run(); String getName(); }
此时,Person接口继承自Hello接口,因此,Person接口现在实际上3个抽象方法签名,
其中一个来自继承的Hello接口。
合理设计interface和abstract class的继承关系,可以充分复用代码。
一般来说,公共逻辑适合放在abstract class中,具体逻辑放到各个子类,
而接口层次代表抽象程度。
4.接口中的default方法
(1)实现类会继承接口中的default方法
如果接口A中有default方法:
public interface A { public default void a(){ System.out.println("这是A"); } }
Test类实现接口A:
public class Test implements A{ }
那么Test类将会继承接口A中的a方法:
public class Test2 { public static void main(String[] args) { Test t = new Test(); t.a(); } }
(2)如果一个类同时实现接口A和B,接口A和B中有相同的default方法,这时,该类必须重写接口中的default方法
类在继承接口中的default方法时,不知道应该继承哪一个接口中的default方法。
接口A:
public interface A { public default void a(){ System.out.println("这是A"); } }
接口B:
public interface B { public default void a(){ System.out.println("这是B"); } }
Test类:
(3)如果子类继承父类,父类中有b方法,该子类同时实现的接口中也有b方法(被default修饰),那么子类会继承父类的b方法而不是继承接口中的b方法。
接口A:
public interface A { public default void b(){ System.out.println("AAA"); } }
类C:
public class C { public void b(){ System.out.println("CCC"); } }
子类:
public class Test extends C implements A{ }
测试:
说明子类继承的b方法为父类C中的b方法,而不是接口中的default b()方法。
实现类可以不必覆写default方法。
default方法的目的是,当我们需要给接口新增一个方法时,会涉及到修改全部子类。
如果新增的是default方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法。
default方法和抽象类的普通方法是有所不同的。
因为interface没有字段,default方法无法访问字段,而抽象类的普通方法可以访问实例字段。