抽象类与接口

一.抽象类的介绍

1.什么是抽象?

  • 生活中的抽象:从具体事物概括出它们共同的方面,而将个别的方面舍弃,这

    种思维过程称为抽象。

  • Java中的抽象:你可以只给出方法的定义不去实现方法的具体事物,由子类去

    根据具体需求来具体实现

2.抽象方法与抽象类的介绍

  • 抽象方法:这种只给出方法定义而不具体实现的方法被称为抽象方法,抽象方

    法是没有方法体的,在代码的表达上就是没有“{}”。

  • 抽象类:包含一个或多个抽象方法的类也必须被声明为抽象类。使用abstract修

    饰符来表示抽象方法以及抽象类。

例如:

/有抽象方法的类也必须被声明为abstract
public abstract class Person {
//抽象方法,不能有“{}”
public abstract void say(); //无方法体{}
}

3.抽象类的特点

  • 抽象类除了包含抽象方法外,还可以包含具体的变量和具体的方法。类即使不包含抽象方法,也可以被声明为抽象类,防止被实例化。

也就是说:抽象方法一定在抽象类中,但是抽象类不一定有抽象方法

  • 抽象类不能被实例化,也就是不能使用new关键字来得到一个抽象类的实例,

    可以这样理解抽象类为什么不能实例化:抽象类中从抽象方法没有方法实现,也就是抽象方法是半成品,导致抽象类也是半成品,半成品是不能使用的,因此抽象类不能实例化

  • 抽象方法必须在子类中被实现。

  • 子类不是抽象类时,一定要重写全部抽象方法

因此抽象类也是一种模板,可以规范子类必须实现的方法

  • 抽象类不能直接使用,需要子类去实现抽象类,然后使用其子类的实例

  • 可以创建抽象类类型的变量,该变量的类型也是一个抽象类,并让他指向具体
    子类的一个实例,也就是可以使用抽象类来充当形参,实际实现类为实参,也
    就是多态的应用

  • private关键字无法修饰抽象方法,因为由于封装会导致无法重写抽象方法

  • abstract修饰符无法和final修饰符一起使用,原因导致继承

例如

People people=new Teacher("教师");
people.work();

注:

抽象类不能有抽象构造方法或抽象静态方法。可以有构造方法和静态方法

4.抽象类的使用场景

  1. 当一个类的一个或多个方法是抽象方法时。
  2. 当类是一个抽象类的子类,并且不能实现父类的所有抽象方法时。
  3. 当一个类实现一个接口,并且不能实现接口的所有抽象方法时。

二.接口的介绍

java 中接口作用和生活中的接口类似,它提供一种约定,使实现接口的类在形式上保持一致。

抽象类中可以有普通方法二接口中的方法默认都是抽象的,也可以说接口是一个特殊的 抽象类 ,接口不能被实例化,而且没有构造方法。

1 .定义接口

public interface 接口名 {
	// 接口成员
}
123

语法解析

  • 和抽象类不同,定义接口使用 interface 修饰符。抽象类使用 abstract 修饰符。
  • 接口的访问权限是 public 或包访问权限,与类的访问权限类似。
  • 一个接口可以继承其他接口,称为父接口,并且接口可以多继承。它将继承父接口中声明的常量和抽象方法。
  • 成员列表中的成员变量声明 [public] [static] [final] 数据类型 成员变量名 = 常量 ;即 接口中成员变量默认都是 public、static、final 的,因此 public static final 可以省略。
  • 成员列表中的成员方法声明 [public] [abstract] 返回值类型 方法名称(参数列表) ;即 接口中的方法默认都是 public、abstract 的,因此 public abstract 可以省略。

与抽象类一样,使用接口要通过子类,子类通过关键字 implements 关键字实现接口。实现接口的语法格式如下:

public 类名 implements 接口名 {
	// 实现方法
	// 普通方法
	// 属性 
}
  • 实现接口使用关键字 implements ,实现抽象类使用关键字 extends
  • 一个类可以实现多个接口,各个接口之间用逗号分隔。
  • 实现接口的类必须实现接口中定义的抽象方法,即使类中不使用某个抽象方法也必须实现它,通常用空方法体实现子类不需要的抽象方法,如果抽象方法有返回值,可返回默认值。
  • 接口的实现类允许包含普通方法。
  • 在实现抽象方法是需要指定 public 权限,否则会产生编译错误。(重写的访问修饰符只能增大或者相同)

示例

定义接口,关键词:interface
*/
public interface USBInterface {  // 定义USB接口
void service();
}

// 定义U盘类,实现USB接口
class UDisk implements USBInterface {
    // 实现接口的抽象方法
    public void service() {
        System.out.println("连接USB接口,开始传输数据");
    }
}

// 定义风扇类,实现USB接口
class UsbFan implements USBInterface {
    // 实现接口的抽象方法
    public void service() {
        System.out.println("连接USB接口,风扇开始转动");
    }
}
public class UsbTest {
    public static void main(String[] args) {
        // U盘插入USB接口
        USBInterface uDisk = new UDisk();
        uDisk.service();  // 连接USB接口,开始传输数据

        USBInterface usbFan = new UsbFan();
        usbFan.service();  // 连接USB接口,风扇开始转动
    }
}

2. 实现多个接口

接口本身也可以继承接口,接口继承的语法格式如下:

[修饰符] interface 接口名 extends 父接口1,父接口2,... {
	// 常量定义
	// 方法定义
}

注意:java 中继承是单继承,一个类只能有一个父类(直接父类),使用 extends 关键字;

但是一个类可以实现多个接口,使用 implements , 多个接口之间用 , 隔开。实现多个接口的语法如下:

class 类名 extends 父类名 implements 接口1,接口2,... {
	// 类的成员
}

但是有一点需要注意:如果一个类实现了两个接口,并且这两个接口中有方法签名相同的方法,但是方法的返回类型不同,那么会导致编译错误。

interface InterfaceA {
 int commonMethod();
}

interface InterfaceB {
 double commonMethod();
}

// 无法通过编译,因为返回类型不是唯一的
class MyClass implements InterfaceA, InterfaceB {
 // 编译错误:'commonMethod()' in 'MyClass' clashes with 'commonMethod()' in 'InterfaceA'; attempting to use incompatible return type
 @Override
 public int commonMethod() {
     return 0;
 }

 @Override
 public double commonMethod() {
     return 0.0;
 }
}

原因是:Java 在设计时允许方法重载(方法名相同,参数列表不同),但返回类型不是方法签名的一部分。然而,如果一个类实现了两个接口,而这两个接口中的方法具有相同的方法名和参数列表,但返回类型不同,Java 编译器无法确定应该使用哪个方法,因为方法的重载是基于参数的,而不是返回类型的。

3.一个类可以同时继承和实现接口

注意:extends 要在 implements 之前

public class LenovoComputer extends Computer implements USBInterface,ChargeInterface{

}

4.接口与接口之间是继承关系

使用 extends 关键字。多个接口使用 , 隔开。

public interface USBInterface {
 void transferData();
}

public interface ChargeInterface {
 void chargeDevice();
}

public interface USBC extends USBInterface, ChargeInterface {
 // 可以添加自己的方法
}

public class Main {
 public static void main(String[] args) {
     USBC usbC = new SomeClass();
     usbC.transferData();    // 调用继承自 USBInterface 的方法
     usbC.chargeDevice();    // 调用继承自 ChargeInterface 的方法
 }
}

class SomeClass implements USBC {
 @Override
 public void transferData() {
     System.out.println("Transferring data via USB-C");
 }

 @Override
 public void chargeDevice() {
     System.out.println("Charging device via USB-C");
 }
}

5. jdk 8 接口新特性(default)

jdk8.0default 关键字可用于在接口中修饰方法(默认方法),default 修饰的方法可以具体实现,也只能在接口中出现。default 修饰的方法可以被重写。

public interface MyInterface {
    void existingMethod();

    default void newDefaultMethod() {
        System.out.println("This is a new default method.");
    }
}

public class MyClass implements MyInterface {
    @Override
    public void existingMethod() {
        System.out.println("Existing method implementation.");
    }
}

public class Main {
    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        myClass.existingMethod();        // 输出:Existing method implementation.
        myClass.newDefaultMethod();      // 输出:This is a new default method.
    }
}

默认方法可以在不破坏已经在使用该接口的所有代码。默认方法有时也称为防御方法(defender method)或 虚拟扩展方法(virtual extension method)

接口中还可以有 static 修饰的方法,称为静态方法(类方法)。 static 方法必须直接使用接口名.方法名调用。

public interface MyInterface {
    void instanceMethod();

    static void staticMethod() {
        System.out.println("This is a static method.");
    }
}

public class MyClass implements MyInterface {
    @Override
    public void instanceMethod() {
        System.out.println("Instance method implementation.");
    }
}

public class Main {
    public static void main(String[] args) {
        MyInterface.staticMethod();     // 输出:This is a static method.

        MyClass myClass = new MyClass();
        myClass.instanceMethod();       // 输出:Instance method implementation.
    }
}

6.jdk 9.0 中接口的新特性(private)

通过将接口中的方法声明为 private,我们可以定义仅在接口内部使用的辅助方法,这些方法不会成为对外暴露的 API 的一部分。私有方法可以被接口中的其他方法调用,从而提供了代码复用和封装的能力。

public interface MyInterface {
    void publicMethod();

    default void defaultMethod() {
        privateMethod();
        System.out.println("This is a default method.");
    }

    private void privateMethod() {
        System.out.println("This is a private method.");
    }
}

public class MyClass implements MyInterface {
    @Override
    public void publicMethod() {
        System.out.println("Public method implementation.");
    }
}

public class Main {
    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        myClass.publicMethod();       // 输出:Public method implementation.
        myClass.defaultMethod();      // 输出:This is a private method.
                                      //      This is a default method.
    }
}

三.抽象类和接口的区别

抽象类接口抽象类
多继承接口可以继承多个父类接口子类只能继承一个直接抽象类
实现子类通过implements实现多个接口子类通过extends继承抽象类
成员接口中只能有常量(变量必须有初始值)、抽象方法。JDK8.0及以后的版本中可以有static方法和default方法抽象类中可以有实例成员、静态成员抽象方法。抽象类中不能使用default关键字修饰
成员修饰符接口中只能定义常量(public static final 修饰的变量)抽象类可以定义变量,也可以定义常量
子类实现子类在实现抽象方法时必须指定public权限子类在实现抽象方法时不影响缩小访问权限
构造函数接口中不能定义构造函数抽象类可以有构造函数,但不能进行实例化
最高层接口没有最高层类的最高层是Object