抽象类和接口

135 阅读6分钟

抽象类和接口

抽象类

引出:当父类的某些方法需要申明,但又不确定如何实现的时候,可以把这个类变成抽象类 添加 abstract关键字

抽象类和普通类最大的区别:抽象类不能被实例化;抽象类可以有抽象方法

抽象类和抽象方法的关系:抽象类一般会包含抽象方法,抽象方法一定位于抽象类或者接口中

public class AbstractDemo {
    public static void main(String[] args) {
        // 抽象类不能被实例化
        // AbstractClassExample ac1 = new AbstractClassExample(); 
        // 抽象类会被继承,由自己的子类实现方法
        AbstractClassExample ac2 = new AbstractExtendClassExample();
        // 能直接通过类名.静态属性/静态方法来访问抽象类中的静态属性/静态方法
        System.out.println(AbstractClassExample.x);
        Animal.func3();
    }
}
// 抽象类: 访问修饰符 abstract class 类名{} (class 关键字要放在 abstract关键字后面)
// 抽象类的本质还是类可以有方法,属性,代码块,构造器,但是抽象类不能被实例化
public abstract class AbstractClassExample {
    // 属性
    private int y;
    // 静态属性
    public static int x;
    // 构造器:可以被子类用super关键字调用构造器
    public AbstractClassExample(){}
    // 代码块
    {}
    // 抽象方法:不能使用private,final和static修饰符,因为这些修饰符都是和重写相违背的
    // 访问修饰符 abstract 返回类型 方法名(参数列表); // 没有方法体
    public abstract void func1();
    // 普通方法
    public void func2() {
        System.out.println("func2");
    }
    // 静态方法
    public static void func3() {
        System.out.println("func3");
    }
}
// 继承抽象类,必须重写该抽象类的抽象方法
class AbstractExtendClassExample extends AbstractClassExample {
    @Override
    public void func1() {
        System.out.println("func1");
    }
}
// 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract;如果子类实现抽象类的全部抽象方法,则子类的子类就不需要实现了该抽象类的抽象方法
abstract class AbstractExtendAbstractClassExample extends AbstractClassExample {}

接口

接口的概述

概念:接口不是类,而是对希望符合这个接口的类的一组需求,接口是抽象类的延伸,接口是弥补抽象类不能实现多继承的缺点

在jdk8之前都是没有方法体的抽象方法;自从jdk8开始,接口的方法可以有具体实现,但是必须是静态方法或者是默认方法(添加default关键字)

// 访问修饰符 interface 接口名{}  访问修饰符:public 和默认  
interface Interface{
    // 静态属性 默认是public static final修饰
    int n1 = 10;
    
    // 静态内部类 默认是public static final修饰,可以实现当前接口
    class InterfaceInnerClass{};
    
    // 静态方法  默认是public修饰
    static void method3(){
        System.out.println("静态方法");
    }
​
    // 抽象方法  默认是public abstract
     void method1();
​
    // 默认方法 添加default关键字  默认是public 
    default public void method2(){
        System.out.println("默认方法");
    }
} 
​
// class 类名 implements 接口
class ImplInterface implements Interface{
    // 必须实现的抽象方法
    public void method1() {};
    
    public static void main(String[] args) {
        // 接口不能被实例化
        //  Interface interfaces = new Interface();  error
        // 可以实现接口的静态内部类
        Interface.InterfaceInnerClass innerClass = new Interface.InterfaceInnerClass();
        innerClass.method3();
        
        // 可以通过接口名.静态方法名来调用静态方法 接口名.属性名来访问属性
        Interface.method3();
        System.out.println(Interface.n1);
    }
}
​
// 一个非抽象类实现了该接口就必须实现该接口上的所有抽象方法,抽象类去实现接口时候,可以不用实现接口的抽象方法
abstract class AbstractImplInterface implements Interface{}

注意:一个类同时可以实现多个接口,一个接口不能继承其他的类,但是可以继承多个其他接口

// 接口 <-> 接口 AND 类 <-> 类 称 继承
// 类 -> 接口 称 实现
// 顶级接口
interface top{}
​
// 父级接口  一个接口不能继承其他的类,但是可以继承多个其他接口
interface secondTop extends top{}
​
//另外一个接口
interface another{}
​
// 实现类 一个类同时可以实现多个接口
class imp implements secondTop,another{}

接口的多态性

// Usb接口
public interface UsbInterface {  
    // 规定相关接口的方法
    public void start();
    public void end();
}
​
// 相机实现了该接口
public class Camera implements UsbInterface{ 
    @Override
    public void start() {
        System.out.println("照相机开始运行了~");
    }
    @Override
    public void end() {
        System.out.println("照相机开始关机了~");
    }
}
​
// 手机实现了该接口
public class Phone implements UsbInterface{ // 实现接口
    @Override
    public void start() {
        System.out.println("手机开始运行了~");
    }
    @Override
    public void end() {
        System.out.println("手机开始关机了~");
    }
}
​
// 接口插入,分别实现不同功能
public class Computer {
    // 计算机工作
    public  void work(UsbInterface usbInterface){
        usbInterface.start();
        usbInterface.end();
    }
}
​
public class Interface01 {
    /*
     * 接口多态特性
     * 1)既可以接收手机对象,又可以接受相机对象,就体现了接口多态(接口引用可以指向实现了该接口的类的对象)
     * 2)多态数组
     * usbs[i] instanceof Phone 向下转型,判读Usb接口是否为Phone
     * 接口类型的变量可以指向实现了该接口的类的对象的实例
     */
    public static void main(String[]args){
        // 创建手机和相机对象
        Camera camera = new Camera();
        Phone phone = new Phone();
        // 创建电脑对象
        Computer computer = new Computer();
        // 插入接口,相机和手机分别运作
        computer.work(phone);
        computer.work(camera);
        // 多态数组
        UsbInterface[] UsbS = new UsbInterface[2]; 
        UsbS[0] = new Phone(); 
        UsbS[1] =  new Camera();
        for (int i = 0; i < UsbS.length; i++) {
            computer.work(UsbS[i]);
        }
    }
}

接口实现 Vs 继承类

  1. 接口和继承解决的问题不同 继承的价值主要在于:解决代码的复用性和可维护性 接口的价值主要在于:设计好各种规范(方法),让其他类去实现这些方法
  2. 接口比继承更加灵活 继承满足is-a的关系,而接口只需满足like-a的关系
  3. 接口在一定程度上实现代码解耦[接口的规范性+动态绑定]

抽象类 Vs 接口

  • 从设计层面上看,抽象类提供了一种 IS-A 关系,那么就必须满足里式替换原则,即子类对象必须能够替换掉所有父类对象。而接口更像是一种 LIKE-A 关系,它只是提供一种方法实现契约,并不要求接口和实现接口的类具有 IS-A 关系
  • 一个类只能继承一个抽象类,而一个类却可以实现多个接口
  • 接口的成员只能是 public 的,而抽象类的成员可以有多种访问权限(接口不能有代码块,抽象类和普通类就是多了抽象方法)
  • 抽象可以有普通方法(含有方法体),接口只能是默认方法(带有default关键字)

使用选择

使用接口:

  • 需要让不相关的类都实现一个方法,例如不相关的类都可以实现 Compareable 接口中的 compareTo() 方法
  • 需要使用多重继承(通过多个接口实现弥补单继承)

使用抽象类:

  • 需要在几个相关的类中共享代码
  • 需要能控制继承来的成员的访问权限,而不是都为 public