Java学习之抽象类和接口

抽象类

用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的多继承功能。

区别

  1. 抽象类是类,可以有实体方法。
  2. 抽象类不能实现多继承,而接口可以。
  3. 如果需要创建不带任何方法定义和成员变量的基类,则使用接口,如果类中需要有部分具体的实现,则使用抽象类。
  4. 如果事先想要将某类设计为一个基类,那么首选接口。==(注意c和d是接口的使用场景)==
  5. 抽象类是对一种事物的抽象,即对类抽象,而接口更多的是对行为的抽象。

网上流传了一个门和报警器的例子:门都有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

猜你喜欢

转载自blog.csdn.net/dypnlw/article/details/82656138