【Java 基础】面向对象(三)抽象类、抽象方法和接口

摘要:本篇文章主要介绍了抽象类、抽象方法和接口的相关概念和基本关系。在 Java 中,一个没有方法体的方法定义为抽象方法,类中如果有抽象方法,该类必须定义为抽象类。抽象类和抽象方法必须使用 abstract 关键字修饰。抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。含有抽象方法的抽象类是不可以被实例化的,但是可以通过多态来实现实例化。接口(interface)是一种公共的规范标准,在 JAVA 编程语言中是一个抽象类型(Abstract Type),它被用来要求类(Class)必须实现指定的方法,使不同类的对象可以利用相同的界面进行沟通。接口不能被直接实例化,需要参照多态的方式,通过实现类对象实例化(接口多态)。最后通过一个教练与运动员的经典案例,说明了具体类、抽象类和接口的具体实现方式。

抽象类和抽象方法

在 Java 中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类。

抽象类和抽象方法的特点:

  • 抽象类和抽象方法必须使用 abstract 关键字修饰。
// 抽象类的定义
public abstract class 类名 {}
// 抽象方法的定义
public abstract void eat();
  • 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
  • 抽象类不能被实例化,需要参照多态的方式,通过子类对象实例化,也叫抽象类多态。
  • 抽象类的子类,要么重写抽象类中的所有抽象方法,要么是抽象类。

抽象类的成员特点:

  • 成员变量:既可以是变量,也可以是常量。
  • 构造方法:用于子类访问父类数据的初始化。
  • 成员方法:既可以是抽象方法的,也可以是非抽象方法的。抽象方法的作用是限制子类必须完成的动作,非抽象方法,子类继承用以提高代码的复用性。

含有抽象方法的抽象类是不可以被实例化的,但是可以通过多态来实现实例化。

接口

接口的概念

接口(interface)是一种公共的规范标准,在 JAVA 编程语言中是一个抽象类型(Abstract Type),它被用来要求类(Class)必须实现指定的方法,使不同类的对象可以利用相同的界面进行沟通。

接口的特点

  • 接口用关键字 interface 修饰。
public interface 接口名 {}  
  • 类实现接口用 implements 表示。
 public class 类名 implements 接口名 {}
  • 接口不能被直接实例化,需要参照多态的方式,通过实现类对象实例化(接口多态)。注:多态的形式分为具体类多态,抽象类多态,接口多态。满足多态的三个前提,有继承或者实现关系,有方法重写,有父类引用指向子类对象。

  • 接口的子类必须满足,要么重写接口中的所有抽象方法,要么子类也是抽象类。

接口的成员特点

  • 接口中的成员变量只能是常量,默认修饰符:public static final。接口中没有变量这一说,只有常量。
  • 接口中没有构造方法,因为接口不是具体存在的,它主要是用于扩展功能的。
  • 接口中的成员方法只能是抽象方法,默认修饰符:public abstract。

示例代码:接口

public interface Inter {
    public int num = 10; // 默认修饰符 public static final
    public final int num2 = 20; // 等价于 public static final int num2 = 20
    int num3 = 30; // 等价于 public static final int num3 = 30;

    // public Inter() {} // 报错,接口中没有构造方法,因为接口是对行为进行规范的。
    // 等价于 public class InterImpl extends Object implements Inter {}
    // public void show() {} // 接口中不允许出现方法体,接口中不能有非抽象方法。

    public abstract void method(); // 接口中的成员方法必须是抽象的。
    void show();// 接口中方法默认修饰符 public abstract
}

测试类:

public class InterfaceDemo {
    public static void main(String[] args) {
        Inter i = new InterImpl();
        // i.num = 20; // 报错,不可重新赋值,默认被 final 修饰
        System.out.println(i.num); // 10 
        // i.num2 = 40; // // 报错,常量不可重新赋值,默认被 final 修饰
        System.out.println(i.num2); // 20
        System.out.println(Inter.num); // 10 默认被 static 修饰,因此可以通过类名访问。
    }
}

一个类如果没有父类,默认继承自 Object。

public class InterImpl  implements Inter {} 
等价于
public class InterImpl extends Object implements Inter {}

类和接口的关系

  • 类与类的关系属于继承关系,只能单继承,但是可以多层继承。
  • 类与接口的关系属于实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口。
// 一个类可以同时实现多个接口
public class InterImpl extends Object implements Inter1,Inter2,Inter3 {}
  • 接口与接口的关系属于继承关系,可以单继承,也可以多继承(Java中是有多继承的,体现的形式是通过接口来实现的)。
// 接口之间通过继承来实现,既可以是单继承,也可以是多继承
public interface Inter3 extends Inter1,Inter2 {}

抽象类与接口的区别

  • 成员区别(语法层面区别),抽象类包含常量和变量,既有构造方法,也有抽象方法和非抽象方法;接口中只有常量和抽象方法。
  • 关系区别(语法层面区别),类与类之间是继承并且是单继承的关系;类与接口是实现的关系,既可以单实现,也可以多实现;接口与接口之间,属于继承的关系,既可以单继承也可以多继承。
  • 设计理念区别,抽象类是对类的属性和行为进行抽象;接口则是只对行为抽象。抽象类是对事物的抽象,而接口是对行为的抽象。

如何理解设计理念的区别?

举例:门和警报的案例,门有 open() 开和 close() 关两个动作,我们都可以通过抽象类和接口来定义这个抽象概念。

抽象类:

public abstract class Door {
    public abstract void open();
    public abstract void close();
}

接口:

public interface Door  {
    void open();
    void close();
}

将来的门继承抽象类或者实现该接口就可以完成相关功能,但是假如我们升级一个版本,增加一个报警功能,那么该如何处理呢?

方法一,将新增的这个报警功能追加到抽象类中,但是这会导致所有的门都有了报警功能,有的门是不需要报警功能的。
方法二,将这三个功能都放在接口中,同样的,也是需要同时实现三个功能,但有的只需要报警功能,因此该方案也不可取。
方法三:门的开和关的功能属于基本功能,而报警功能属于附属的额外功能,最好的解决办法就是将报警单独设计一个接口,而门的开和关
通过抽象类来实现,当需要用到报警功能时,来实现相应的报警接口即可。

示例代码:

// 抽象类和接口的设计理念区别 (通过接口实现报警功能,将子类需要实现的功能灵活化,不受限制)

public interface Alram {
    void alarm();
}

public abstract class Door {
    public abstract void open();
    public abstract void close();
}

// 只实现开、关功能
public class AlarmDoor1 extends Door {
    public void open() {
        // ...
    }
    public void close() {
        // ...
    }
}


// 只实现报警的功能
public class AlarmDoor2 implements Alarm {
    public void alarm() {
        // ...
    }
}

// 实现开、关和报警的功能
public class AlarmDoor3 extends Door implements Alarm {
    public void open() {
        // ...
    }
    public void close() {
        // ...
    }
    public void alarm() {
        // ...
    }
}

经典案例

假设现在有乒乓球运动员和篮球运动员,乒乓球教练和篮球教练,为了出国交流,跟乒乓球相关的人员都需要说英语。请用分析本案例中有哪些具体类,哪些抽象类,哪些接口,并用代码实现。

具体分析如下:

  • 具体类:乒乓球运动员、篮球运动员,乒乓球教练、篮球教练。
  • 抽象类:人(基本属性和行为,姓名,年龄,吃饭等),运动员(学习行为),教练(教学行为)。
  • 接口:说英语。

具体代码如下:
抽象类-人:

// 抽象类人
public abstract class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public abstract void eat();
}

抽象类-运动员:

// 抽象类-运动员
public abstract class Player extends Person {
    public Player() {
    }

    public Player(String name, int age) {
        super(name, age);
    }

    public abstract void study();
}

抽象类-教练:

// 抽象类-教练
public abstract class Coach extends Person {
    public Coach() {
    }

    public Coach(String name, int age) {
        super(name, age);
    }

    public abstract void teach();
}

具体类-乒乓球运动员

public class PingPangPlayer extends Player implements SpeakEnglish {

    public PingPangPlayer() {
    }

    public PingPangPlayer(String name, int age) {
        super(name, age);
    }

    @Override
    public void study() {
        System.out.println("乒乓球运动员学习如何发球和接球");
    }

    @Override
    public void eat() {
        System.out.println("乒乓球运动员吃大白菜,喝小米粥");
    }

    @Override
    public void speak() {
        System.out.println("乒乓球运动员说英语");
    }
}

具体类-乒乓球教练:

public class PingPangCoach extends Coach implements SpeakEnglish {

    public PingPangCoach() {
    }

    public PingPangCoach(String name, int age) {
        super(name, age);
    }

    @Override
    public void teach() {
        System.out.println("乒乓球教练教如何发球和接球");
    }

    @Override
    public void eat() {
        System.out.println("乒乓球教练吃小白菜,喝大米粥");
    }

    @Override
    public void speak() {
        System.out.println("乒乓球教练说英语");
    }
}

具体类-篮球运动员:

public class BasketballPlayer extends Player {

    public BasketballPlayer() {
    }

    public BasketballPlayer(String name, int age) {
        super(name, age);
    }

    @Override
    public void study() {
        System.out.println("篮球运动员学习如何运球和投篮");
    }

    @Override
    public void eat() {
        System.out.println("篮球运动员吃牛肉,喝牛奶");
    }
}

具体类-篮球教练:

public class BasketballCoach extends Coach {
    public BasketballCoach() {
    }

    public BasketballCoach(String name, int age) {
        super(name, age);
    }

    @Override
    public void teach() {
        System.out.println("篮球教练教如何运球和投篮");
    }

    @Override
    public void eat() {
        System.out.println("篮球教练吃羊肉,喝羊奶");
    }
}

接口-说英语:

// 接口 - 说英语
public interface SpeakEnglish {
    public abstract void speak();
}

测试类:

public class PersonDemo {
    public static void main(String[] args) {
        PingPangPlayer ppp = new PingPangPlayer();
        ppp.setName("王浩");
        ppp.setAge(30);
        System.out.println(ppp.getName()+","+ppp.getAge()); // 王浩,30
        ppp.eat(); // 乒乓球运动员吃大白菜,喝小米粥
        ppp.study();// 乒乓球运动员学习如何发球和接球
        ppp.speak();// 乒乓球运动员说英语
        System.out.println("--------");

        BasketballPlayer bp = new BasketballPlayer();
        bp.setName("姚明");
        bp.setAge(35);
        System.out.println(bp.getName()+","+bp.getAge()); // 姚明,35
        bp.eat(); // 篮球运动员学习如何运球和投篮
        bp.study(); // 篮球运动员吃牛肉,喝牛奶
    }
}

相关推荐

微信扫一扫,分享到朋友圈

【Java 基础】面向对象(三)抽象类、抽象方法和接口
返回顶部

显示

忘记密码?

显示

显示

获取验证码

Close