Day15 抽象类与接口

发布时间 2023-12-04 17:49:45作者: 问稻

1.抽象类格式 关键字: abstract


1.1抽象类:

abstract class 类名{}
public abstract class ClassName{
    int a;
    public abstract void fun();
}
  • 除非该继承的子类也是抽象类,否则继承了抽象类的所有类都要重写抽象类的抽象方法

  • 不能 new 一个 java 抽象类,有点像c++中的抽象类,即含纯虚函数的抽象类,需要子类完全重载(java中的重写)后才能生成实例

  • 抽象类内可以有变量,变量写法与普通类一致

  • 抽象类中有构造方法


1.2 抽象类内的抽象方法

修饰符 abstract 返回值类型 方法名(参数列表);
    
public abstract void fun();//不能写函数体代码块
  • 抽象类不一定有抽象方法,但是有抽象方法的类必须定义成抽象方法

  • 不包含抽象方法的抽象类,目的就是不想让调用者实例化该对象,通常用于某些特殊的类的结构设计

  • 抽象方法的修饰符可以是 public 或者是 protected, 省缺情况下默认是 public

  • 抽象类的构造方法能不能是抽象方法?

    public abstract class Student {
        String name;
        abstract Student();//直接报错!
    }
    /*抽象类的构造方法不能是抽象方法
    原因很简单:执行子类构造函数之前需要执行父类的构造函数,如果父类的构造函数可以是抽象方法的话,需要在子类重写,但是调用父类构造函数时,子类的构造函数尚未执行,拿来的重写?
    */
    

1.3 抽象类中静态方法的调用

  • 直接 [抽象类].[静态方法] !!!
public abstract class Student {
	public static void fun(){};
    public abstract void fun2();
}

public class main {
    public static void main(String[] args) {
        Student.fun();
    }
}

2.接口

​ **接口的本质是规范,定义的是一组规则 **

  • 类的关键字是class, 接口的关键字是 interface

    class Person{}//类
    interface Person{}//接口
    
  • 接口内的所有方法都是抽象的,不能写函数体代码块

  • 接口内的方法都是 (public abstract) 类型,不写的话编译器自动补全

  • 接口与抽象类一样,其内同样可以定义变量

  • 接口内定义的变量都是 (public static final) 类型的全局静态常量,所有比较少用

    interface Person{//接口
      public static final String name= “name”;
      void getName();
      public abstract void getName()
    }
    

2.1 实现类

  • 实现了接口的类简称实现类

  • 接口都需要有实现类, 用法类似于类继承,把关键字 extends 换成 implements

public class [实现类名 = "接口名" + Impl] implements [接口名],[接口名]{}

例如:
    public interface Person{}//接口
	public interface Man{}//接口
	public class PersonImpl implements Person,Man{}//实现类
	

注意:

  • 实现类还没有把继承的所有接口里的所有抽象方法全重写之前,编译器会一直报错,这是正常现象
  • 正如上面格式中所写,一个实现类是可以继承多个接口的,也就是伪多继承!!!
  • 接口的多继承,在 implements 后可跟多个接口名,用逗号隔开,且都不需要在接口名字面前写关键字interface
  • 实现类推荐的命名方式为 对应接口名 + Impl

2.2 JDK8的新变化

  • JDK 8 之前接口只能定义抽象方法,JDK8中接口也可以定义默认方法 default 和静态方法 static
  • 默认方法允许接口提供默认实现,从而减少实现类的工作量。当接口的实现类没有提供该方法的具体实现时,将使用默认方法
  • 静态方法可以为接口提供与接口相关的工具方法,静态方法可以直接通过接口名来调用,而不需要创建实现类的实例。因为静态方法无法被实现类覆盖或继承。
public interface MyInterface {
    default void fun1() {
        // 默认方法的实现代码,即实现代码是在接口中定义的
    }//default 需要明写,因为不写默认是public
    static void fun2(){
        //同上,实现代码是在接口中定义的
    }
}
public class main {
    public static void main(String[] args) {
        MyInterface.fun2();//用接口名直接调用静态方法
    }
}
2.2.1 默认方法冲突问题

当一个实现类继承了多个接口,且接口中有同名的默认方法时,必须 显式调用 接口A的默认方法(注意是显式调用!!!不是必须重写!,在不重写的情况下显式调用就不报错!)

格式:[接口名] . super . [默认方法名]

interface A {
    default void doSomething() {
        System.out.println("Do something in A");
    }
}

interface B {
    default void doSomething() {
        System.out.println("Do something in B");
    }
}

class MyClass implements A, B {
    @Override
    public void doSomething() {
        A.super.doSomething();  // 显式调用接口A的默认方法
    }
}

3.抽象类与接口的异同

异:

  • 接口中没有构造方法,抽象类中有构造方法
  • 接口的关键字为interface 和 implements,抽象类的关键字为 class 和 extends
  • 接口可以多继承,抽象类只有单继承
  • 接口中的方法必须全是抽象方法,抽象类中可以有非抽象方法(JDK8 后接口也可以定义默认方法和静态方法)
  • 接口中的变量都是 public static final 类型(全局静态常量),抽象了没有要求
  • 接口的抽象方法一定是 public abstract, 抽象类的抽象方法可以是public abstract 或者是 protected abstract。(此条不适用于 JDK8)
  • 接口的抽象方法省缺时默认为 public abstract, 抽象类的抽象方法只可以省缺public ,因为省缺默认是public, 而 abstract 必须写

同:

  • 接口和抽象类的抽象函数都是用abstract
  • 接口和抽象类都是为了定义一些约束
  • 接口和抽象类都不能直接实例化,只有继承其的子类(接口为实现类)在完全重写抽象方法后,才能生成子类实例