抽象类
一般我们编写一个类时,常常会为该类定义一些方法,这些方法都有具体的实现。但是在某些情况下,父类只知道子类应该包含什么方法,但是不知道子类是怎么实现这个方法的。比如几何图形类包含三角形子类,长方形子类,圆形子类,父类知道这些子类要有计算周长和面积的方法,但是却不知道各自怎么实现的(各自实现方法也是不同的)。
如何既能让父类定义计算周长和面积的方法,又不用提供具体的方法实现呢?
使用抽象方法即可满足该要求:抽象方法只有方法的声明,没有方法的具体实现。
抽象方法和抽象类
抽象方法和抽象类必须使用 abstract 修饰符来定义,有抽象方法的类只能被定义成抽象类,抽象类里可以没有抽象方法。
抽象方法和抽象类的规则如下。
①抽象类必须使用 abstract 修饰符来修饰,抽象方法也必须使用 abstract 修饰符来修饰,抽象方法不能有方法体。
②抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例。即使抽象类里不包含抽象方法,这个抽象类也不能创建实例。
③抽象类可以包含成员变量、方法〈普通方法和抽象方法都可以)、构造器、初始化块、内部类(接口、枚举)5中成分。抽象类的构造器不能用来创建实例,主要用来被其他子类调用。
④抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public。
⑤如果一个类继承于一个抽象类,则子类必须实现父类的抽象方法。如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类。
抽象类可以用“有得有失”来形容:
得:抽象类可以包含抽象方法;
失:抽象类不能创建实例。
定义抽象类的方法:
在普通类上增加 abstract 修饰符即可。
定义抽象方法:
在普通方法上增加 abstract 修饰符,并把普通方法大括号中的方法体部分全部删掉,并在方法后增加分号。
通过代码来演示抽象类和抽象方法:
abstract class Shape{//抽象图形类
public abstract double getArea();//抽象方法,计算面积
public abstract double getPan();//计算周长
}
class Square extends Shape{ //定义一个正方形类,继承图形抽象类
protected double len;
Square(double len){
this.len = len;
}
public double getArea(){//抽象方法的具体实现
return len*len;
}
public double getPan(){//抽象方法的具体实现
return 4*len;
}
}
class Rectangle extends Square{//定义一个长方形类,继承图形抽象类
private double wid;
Rectangle(double len,double wid){
super(len);
this.wid = wid;
}
public double getArea(){//抽象方法的具体实现
return wid*len;
}
public double getPan(){//抽象方法的具体实现
return (wid+len)*2;
}
}
class Circle extends Shape{//定义一个圆形类,继承图形抽象类
private double r;
Circle (double r){
this.r = r;
}
public double getArea(){//抽象方法的具体实现
return Math.PI*r*r;
}
public double getPan(){//抽象方法的具体实现
return Math.PI*r*2;
}
}
从这里可以看出,抽象类就是为了继承而存在的,如果你定义了一个抽象类,却不去继承它,那么等于白白创建了这个抽象类,因为你不能用它来做任何事情。对于一个父类,如果它的某个方法在父类中实现出来没有任何意义,必须根据子类的实际需求来进行不同的实现,那么就可以将这个方法声明为abstract方法,此时这个类也就成为abstract类了。
接口
在Java中,可以通过两种形式来体现OOP的抽象:接口和抽象类。这两者有太多相似的地方,又有太多不同的地方。
抽象类是从多个类中抽象出来的模板,如果将这种抽象进行得更彻底,则可以提取出一种更加特殊的“抽象类”——接口。
接口里不能包含普通方法(抽象类可以),接口里所有的方法都是抽象方法。
接口定义了某一些类所需要遵守的规范,接口不关心这些类的内部状态,也不关心这些类里方法的具体实现细节,它只规定类里必须提供的方法。
接口不提供任何实现,接口体现的是规范和实现分离的哲学。
接口和抽象类语法上的区别
①抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法。
②抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的。
③接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法。
④一个类只能继承一个抽象类,而一个类却可以实现多个接口。(单继承多实现)。
抽象类和接口的例子
下面看一个网上流传最广泛的例子:
门和警报的例子:门都有open( )和close( )两个动作,此时我们可以通过抽象类和接口来定义这个抽象概念。
abstract class Door {//抽象类
public abstract void open();
public abstract void close();
}
interface Door {//接口
public abstract void open();
public abstract void close();
}
现在如果我们需要门具有报警alarm( )的功能,那么该如何实现?下面提供两种思路:
①将这三个功能都放在抽象类里面,但是这样一来所有继承于这个抽象类的子类都具备了报警功能,但是有的门并不一定具备报警功能。
②将这三个功能都放在接口里面,需要用到报警功能的类就必须实现这个接口中的所有抽象方法,包括open( )和close( ),但是很可能这个类根本就不具备open( )和close( )这两个功能,比如火灾报警器。
从这里可以看出, Door的open() 、close()和alarm()根本就属于两个不同范畴内的行为,open()和close()属于门本身固有的行为特性,而alarm()属于延伸的附加行为。
因此最好的解决办法是单独将报警设计为一个接口,包含alarm()行为,Door设计为单独的一个抽象类,包含open和close两种行为。再设计一个报警门继承Door类和实现Alarm接口。
interface Alram {//单独定义一个警报接口,谁需要谁实现它
void alarm();//接口中方法和属性的修饰符都是可以省略的,为防止写错,直接不写就行
}
abstract class Door {//定义门的抽象类
void open();//这里没有abstract,是抽象类中的普通方法
void close();
}
class AlarmDoor extends Door implements Alarm {
void oepn() {
//....
}
void close() {
//....
}
void alarm() {
//....
}
}