抽象类
用abstract修饰一个类时,这个类就为抽象类;用abstract修饰的方法称为抽象方法。这里有几点需要注意:
1. 含有抽象方法的类必须被声明为抽象类(接口是抽象类的深度抽象),抽象类必须被继承(不然无意义),抽象方法必须被重写。
2. 抽象方法只需声明,而不需实现。
3. 抽象类不能被实例化,因为大部分抽象类中都存在抽象方法,抽象方法是没有方法体的,实例化会报错。此外,也可以存在一些没有抽象方法的抽象类,但不存在抽象方法的类和实体类没有差别,所以没有抽象方法的抽象类是无意义的。
3. 抽象类可以含有实体方法或者成员变量。
下面是语法结构的例子:
abstract class Animal{
//抽象类可以具有实体方法和成员变量
private String name;
Animal(String name){
this.name=name;
}
//抽象方法,权限只能是public或者protected
public abstract void enjoy();
}
class Cat extends Animal{
private String eyesColor;
Cat(String n,String c){
//调用父类构造函数
super(n);
eyesColor=c;
}
//重写抽象方法
public void enjoy(){
System.out.println("猫叫声....");
}
}
其实,从上面的例子也可以看出,抽象类的抽象方法就是为了让别人重写的,通过这样来实现面向对象的多态特点。如果子类不想实现抽象方法,那么子类也必须添加上关键字abstract,声明子类为抽象类。
接口
使用interface来定义一个接口。接口定义类似类的定义,分为接口的声明和接口体,其中接口体由常量定义和方法定义两部分组成。使用接口需要注意:
1. 接口中可以含有常量(注意不是变量)和方法,并且接口中的常量会被隐式指定为public static final(这也可以解释为什么是常量,而不能存在变量),方法会被隐式指定为public abstract方法。具体原因可以查看该链接:接口变量为什么默认是public static final
2. 接口是一种深度抽象的类型。
3. 接口不具有任何实现,最适合做基类。
定义接口的基本格式如下:
[修饰符] interface 接口名 [extends 父接口名列表]{
//可选参数public,如果省略,则为默认的访问权限;
[public] [static] [final] 常量;
[public] [abstract] 方法;
}
实现接口要使用关键字implements:
[修饰符] class <类名> [extends 父类名] [implements 接口列表]{
//重写接口方法
...
}
这里举一个例子:
//定义各功能接口
interface CanFight {void fight();}
interface CanFly {void fly();}
interface CanSwim {void swim();}
//定义一个抽象
class ActionCharacter {public void fight(){}}
class Hero extends ActionCharacter implements CanFight, CanFly, CanSwim {
@Override
public void swim() {}
@Override
public void fly() { }
}
public class Adventure {
public static void t(CanFight x){x.fight();}
public static void u(CanSwim x){x.swim();}
public static void v(CanFly x){x.fly();}
public static void w(ActionCharacter x){x.fight();}
public static void main(String[] args) {
Hero h = new Hero();
t(h);
u(h);
v(h);
w(h);
}
}
从例子中我们可以看到:
1. Hero同时具备了各接口的功能
2. Hero可以和它实现的这些接口进行相互转换,当我们将hero对象做参数,传入Adventure类的各个方法时,Hero类向上转型了。由此可见,接口的好处可以 实现向上转型,多个具有共同属性的类可以将它们的共同点提取出来,做成抽象,这样层次分明,统一管理。
这里提一个额外的知识点:向上转型是传递的。比如如果A的子类是B,B的子类是C,那么以下语句
A a0=new A();
A a1=new B();
A a2=new C();
都是正确符合条件的。
接口和抽象类的区别和关系
关系
通过结合接口和抽象类共同实现和完善Java的多继承功能。
区别
- 抽象类是类,可以有实体方法。
- 抽象类不能实现多继承,而接口可以。
- 如果需要创建不带任何方法定义和成员变量的基类,则使用接口,如果类中需要有部分具体的实现,则使用抽象类。
- 如果事先想要将某类设计为一个基类,那么首选接口。==(注意c和d是接口的使用场景)==
- 抽象类是对一种事物的抽象,即对类抽象,而接口更多的是对行为的抽象。
网上流传了一个门和报警器的例子:门都有open( )和close( )两个动作,此时我们可以定义通过抽象类和接口来定义这个抽象概念:
abstract class Door {
public abstract void open();
public abstract void close();
}
或者
interface Door {
public abstract void open();
public abstract void close();
}
但是现在如果我们需要门具有报警alarm( )的功能,那么该如何实现?下面提供两种思路:
1)将这三个功能都放在抽象类里面,但是这样一来所有继承于这个抽象类的子类都具备了报警功能,但是有的门并不一定具备报警功能;
2)将这三个功能都放在接口里面,需要用到报警功能的类就需要实现这个接口中的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();
void close();
}
class AlarmDoor extends Door implements Alarm {
void oepn() {
//....
}
void close() {
//....
}
void alarm() {
//....
}
}
参考资料:
https://www.cnblogs.com/xdp-gacl/p/3648398.html
https://blog.csdn.net/zhangerqing/article/details/8298603