1、抽象方法和抽象类:
就是定义一个方法,这个方法只有函数名,没有具体功能的函数体。含有抽象方法的类必须是抽象类,当然抽象类也可以含有普通的方法。
2、抽象类的作用:
抽象类就是相当于从多个具体类中抽象出来的父类。从多个具有相同特征的类中抽象出来一个抽象类,以这个类作为子类的模板,抽象类体现的就是一种,模板模式的设计。抽象类中的方法不写方法体,函数的具体实现由继承这个抽象类的子类来实现。
3、抽象类需要注意的部分
(1)抽象类不可以被实例化,无法使用new关键字来调用抽象类的构造器来创建抽象类的实例。即使抽象类有构造器,也不能用作于创建对象,主要是被其子类调用。
(2)因为抽象方法一定会被重写,所以abstract与final不可以一起使用。
(3)因为当使用static修饰一个方法时,表明这个方法是属于类本身,既可以用类就可以调用该方法,但如果该方法被定义成抽象方法,则导致通过该类直接调用该方法会出错(因为调用一个没有函数体的函数一定会引起错误)所以static与abstract一般不可以在一起使用修饰某个方法,但是可以在一起修饰某个内部类。
(4)例如Shape是抽象类。Triangle和Circle是继承Shape的子类,那么可以这样定义对象
Shape s1=new Triangle();
Shape s2=new Circle();
这也体现了多态性。所以这样子是s1和s2都是Triangle和Circle的实例,因为Shape有s1,s2要调用方法的声明(抽象类)。所以s1,s2可以直接调用,这就是为什么父类中即使不给抽象类具体方法,也要声明抽象类的原因,不可以删除。
4、接口定义:
接口也是一种“类(抽象类)”,这个类中所有的方法都必须是抽象方法。但是接口又不是类。接口定义了一种规范,接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的具体实现细节,他只规范这些类里必须要提供某些方法。可见。接口是从多个相似类中抽象出来的规范,接口不提供任何实现。接口体现的是规范和实现分离的设计哲学。
接口需要注意的一些规则
(1)接口中的变量默认并且只能是指定为public static final变量,而方法会被隐式地指定为public abstract方法且只能是public abstract方法
(2)java8以后接口中可以有默认方法(default修饰)和静态方法(public static修饰的),这两种方法必须有函数体。添加默认方法的原因是:在Java 8之前,接口与其实现类之间的耦合度过高(tightly coupled),当需要为一个接口添加方法时,所有的实现类都必须随之修改。默认方法可以为接口添加新的方法,而不会破坏已有的接口的实现。这在lambda表达式作为Java 8语言的重要特性出现之际,为升级旧接口且保持向后兼容(backward compatibility)提供了途径。
(3)默认方法的继承
接口默认方法的继承分三种情况(分别对应下面的InterfaceB接口、InterfaceC接口和InterfaceD接口):
(1)不覆写默认方法,直接从父接口中获取方法的默认实现。
(2)覆写默认方法,这跟类与类之间的覆写规则相类似。
(3)覆写默认方法并将它重新声明为抽象方法,这样新接口的子类必须再次覆写并实现这个抽象方法。
interface InterfaceA {
default void foo() {
System.out.println("InterfaceA foo");
}
}
interface InterfaceB extends InterfaceA {
}
interface InterfaceC extends InterfaceA {
@Override
default void foo() {
System.out.println("InterfaceC foo");
}
}
interface InterfaceD extends InterfaceA {
@Override
void foo();
}
public class Test {
public static void main(String[] args) {
// 打印:“InterfaceA foo”
new InterfaceB() {}.foo();
// 打印:“InterfaceC foo”
new InterfaceC() {}.foo();
new InterfaceD() {
@Override
public void foo() {
// 打印:“InterfaceD foo”
System.out.println("InterfaceD foo");
}
}.foo();
// 或者使用lambda表达式
((InterfaceD) () -> System.out.println("InterfaceD foo")).foo();
}
}
(4)接口中静态方法的使用
interface InterfaceA {
default void foo() {
printHelloWorld();
}
static void printHelloWorld() {
System.out.println("hello, world");
}
}
public class Test {
public static void main(String[] args) {
// 打印:“hello, world”
InterfaceA.printHelloWorld();
}
}
(5)default关键字只能在接口中使用(以及用在switch语句的default分支),不能用在抽象类中;接口默认方法不能覆写Object类的equals、hashCode和toString方法;接口中的静态方法必须是public的,public修饰符可以省略,static修饰符不能省略。
(6)可以拥有多个default方法,也可以拥有多个static方法
实现某个接口的时候,仅仅需要实现抽象方法,default、static方法不需要强制自己新实现
5、接口的语法定义。
public interface 接口名 extends 父接口1,父接口2 //也可以不继承别的接口
{
public stract final 常量名 //接口中只可以定义常量
public abstract 方法名 //方法中只能是抽象方法
零到多个默认方法或类方法 //这两种方法必须要有函数体
零到多个内部类,接口,枚举定义
//接口中不可以有构造器和初始化块,接口中的成员变量在定义时候就要有赋值。既是另一个不在一个包下的另一个类也可以利用接口名来访问成员变量格式如下:
包名.接口名.变量名
}
6、接口的继承
和类的继承一样,接口的继承支持多继承(类的继承只能是单继承),接口可以继承多个父接口,子接口将会继承父接口里的所有抽象方法和常量
7、接口的使用实现
所谓的接口的实现就是定义一个类,这个类实现了接口中的具体的方法。
(1)语法;
修饰符 class 类名 extends 父类 implements 接口1,接口2 //一个类可以继承一个父类,实现多个接口
{
类体部分
}
(2)一些规则
实现接口与继承父类一样,一样可以获得这个接口里的常量和方法(抽象方法和默认方法),所以实现接口可以看成一种特殊的继承,相当于继承了一个彻底抽象的类(相当于除了默认方法外,所有方法都是抽象方法的类)
一个类实现了一个接口就意味着这个类要实现这个接口里的所有抽象方法,如果不能实现,那么就仍然要在类中把这个方法定义成抽象类。
8、抽象类和接口相比
(1)语法层面上的区别
1)抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法(java8之后接口有默尔方法和静态方法可以有具体的函数实现);
2)抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;
3)接口中不能含有静态代码块以及静态方法(java8之后有静态方法),而抽象类可以有静态代码块和静态方法;
4)一个类只能继承一个抽象类,而一个类却可以实现多个接口。
(2)设计层面上的区别
1)抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。举个简单的例子,飞机和鸟是不同类的事物,但是它们都有一个共性,就是都会飞。那么在设计的时候,可以将飞机设计为一个类Airplane,将鸟设计为一个类Bird,但是不能将 飞行 这个特性也设计为类,因此它只是一个行为特性,并不是对一类事物的抽象描述。此时可以将 飞行 设计为一个接口Fly,包含方法fly( ),然后Airplane和Bird分别根据自己的需要实现Fly这个接口。然后至于有不同种类的飞机,比如战斗机、民用飞机等直接继承Airplane即可,对于鸟也是类似的,不同种类的鸟直接继承Bird类即可。从这里可以看出,继承是一个 "是不是"的关系,而 接口 实现则是 "有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如鸟是否能飞(或者是否具备飞行这个特点),能飞行则可以实现这个接口,不能飞行就不实现这个接口。
2)设计层面不同,抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。什么是模板式设计?最简单例子,大家都用过ppt里面的模板,如果用模板A设计了ppt B和ppt C,ppt B和ppt C公共的部分就是模板A了,如果它们的公共部分需要改动,则只需要改动模板A就可以了,不需要重新对ppt B和ppt C进行改动。而辐射式设计,比如某个电梯都装了某种报警器,一旦要更新报警器,就必须全部更新。也就是说对于抽象类,如果需要添加新的方法,可以直接在抽象类中添加具体的实现,子类可以不进行变更;而对于接口则不行,如果接口进行了变更,则所有实现这个接口的类都必须进行相应的改动。
(Java8之后的可以把要添加的方法放在默认方法中,这样就改接口就可以了,不需要改实现类)。
很重要的一句话:继承是一个 "是不是"的关系,而 接口 实现则是 "有没有"的关系