后端面试题:接口和抽象类的区别?抽象类可以多继承吗?
接口和抽象类的区别
1. 接口(Interfaces)
接口是一种抽象类型,它定义了一组方法的签名但没有实现。在Java中,接口可以包含常量、方法声明、默认方法、静态方法和嵌套类型。接口通过 interface 关键字声明。
示例:
// 定义接口 interface Shape { double calculateArea(); double calculatePerimeter(); } public class Main { public static void main(String[] args) { // 创建一个圆形对象 Circle circle = new Circle(5.0); // 计算并打印圆形的面积和周长 System.out.println("Circle Area: " + circle.calculateArea()); System.out.println("Circle Perimeter: " + circle.calculatePerimeter()); } } // 实现圆形类 class Circle implements Shape { private double radius; // 构造方法 public Circle(double radius) { this.radius = radius; } // 计算圆形的面积 @Override public double calculateArea() { return Math.PI * radius * radius; } // 计算圆形的周长 @Override public double calculatePerimeter() { return 2 * Math.PI * radius; } }
2. 抽象类(Abstract Classes)
抽象类是一种不能被实例化的类,它可以包含抽象方法和非抽象方法。抽象方法是一种没有具体实现的方法,它只有方法声明,没有方法体。抽象类通过 abstract 关键字声明。
示例:
// 定义抽象类 abstract class Animal { abstract void makeSound(); void eat() { System.out.println("Animal is eating..."); } } // 定义 Dog 类继承 Animal 抽象类 class Dog extends Animal { // 实现抽象方法 void makeSound() { System.out.println("Dog barks..."); } } // 主类 public class Main { // 主方法 public static void main(String[] args) { // 创建 Dog 对象 Dog dog = new Dog(); dog.makeSound(); // 调用 Dog 类中的 makeSound 方法 dog.eat(); // 调用 Animal 类中的 eat 方法 } }
主要区别:
- 方法实现: 接口中的方法只有声明,没有实现,而抽象类中的抽象方法可以有实现。
- 多继承: Java中接口可以多继承,一个类可以实现多个接口,但是类只能单继承。而抽象类只能单继承。
- 访问修饰符: 接口中的方法默认是 public,抽象类中的抽象方法可以有不同的访问修饰符。
- 成员变量: 接口中只能包含常量,而抽象类可以包含非抽象方法、字段和常量。
- 设计理念: 接口用于定义类应该遵循的行为,而抽象类用于建模类之间的共性关系。
在设计程序时,要根据需求和设计理念选择使用接口还是抽象类。接口用于定义类之间的协议,实现了接口的类必须实现接口中的所有方法。抽象类则用于建模类之间的通用关系,它提供了一组默认的实现,但允许子类覆盖它们。
演示代码
// 定义接口 interface MyInterface { // 接口中的方法只有声明,没有实现 void method1(); } // 定义抽象类 abstract class MyAbstractClass { // 抽象方法可以有实现 abstract void method2(); // 抽象类中的非抽象方法可以有实现 void method3() { System.out.println("This is a method in abstract class."); } } // 定义实现多个接口的类 class MyClass implements MyInterface { // 实现接口中的方法 public void method1() { System.out.println("Method 1 implemented from interface."); } } // 主类 public class Main { public static void main(String[] args) { // 创建实现多个接口的类的对象 MyClass myClass = new MyClass(); // 调用接口中的方法 myClass.method1(); // 实例化抽象类 MyAbstractClass myAbstractClass = new MyAbstractClass() { @Override void method2() { System.out.println("Method 2 implemented from abstract class."); } }; // 调用抽象类中的方法 myAbstractClass.method2(); myAbstractClass.method3(); } }
这个运行结果说明了程序中的几个重要点:
- Method 1 implemented from interface.
这一行输出表明 MyClass 类成功实现了接口 MyInterface 中的抽象方法 method1()。通过实现接口,MyClass 类表明它遵循了接口所定义的行为。
- Method 2 implemented from abstract class.
这一行输出表明通过匿名内部类的方式,成功实现了抽象类 MyAbstractClass 中的抽象方法 method2()。匿名内部类是在实例化抽象类时直接定义的,它重写了抽象方法 method2(),提供了具体的实现。
- This is a method in abstract class.
这一行输出表明抽象类 MyAbstractClass 中的非抽象方法 method3() 被调用了。这个方法在抽象类中有具体的实现,因此可以被直接调用。
抽象类的多继承问题
抽象类在Java中只能单继承,不能多继承。这是因为Java的类继承机制决定的。每个类只能有一个直接父类,这样有利于构建简洁而有效的类层次结构。
// 定义抽象类 Animal abstract class Animal { abstract void makeSound(); } // 定义抽象类 Color abstract class Color { abstract void showColor(); } // 定义实现了多个抽象类的子类 class Dog extends Animal { void makeSound() { System.out.println("Dog barks..."); } } class Red extends Color { void showColor() { System.out.println("The color is red."); } } // 主类 public class Main { public static void main(String[] args) { // 创建 Dog 对象 Dog dog = new Dog(); dog.makeSound(); // 调用 Dog 类中的 makeSound 方法 // 创建 Red 对象 Red red = new Red(); red.showColor(); // 调用 Red 类中的 showColor 方法 } }
这个运行结果表明了程序成功创建了两个对象并调用了它们的方法:
- Dog barks…
这一行输出表示程序成功创建了一个 Dog 对象,并调用了它的 makeSound() 方法。输出表明狗发出了叫声,这是 Dog 类中的实现。
- The color is red.
这一行输出表示程序成功创建了一个 Red 对象,并调用了它的 showColor() 方法。输出表明显示的颜色是红色,这是 Red 类中的实现。
抽象类的应用场景
1. 模板方法模式
抽象类常用于模板方法模式中。在模板方法模式中,抽象类定义了算法的骨架,子类可以按需求实现具体的步骤。
示例:
// 定义抽象类 Game abstract class Game { abstract void initialize(); abstract void startPlay(); abstract void endPlay(); // 模板方法 public final void play() { initialize(); startPlay(); endPlay(); } } // 具体的游戏类 class Cricket extends Game { @Override void initialize() { System.out.println("Cricket Game Initialized! Start playing."); } @Override void startPlay() { System.out.println("Cricket Game Started. Enjoy the game!"); } @Override void endPlay() { System.out.println("Cricket Game Finished!"); } } class Football extends Game { @Override void initialize() { System.out.println("Football Game Initialized! Start playing."); } @Override void startPlay() { System.out.println("Football Game Started. Enjoy the game!"); } @Override void endPlay() { System.out.println("Football Game Finished!"); } } // 主类 public class Main { public static void main(String[] args) { Game cricket = new Cricket(); cricket.play(); System.out.println(); Game football = new Football(); football.play(); } }
这个运行结果表示了两个具体游戏类的实例在执行模板方法模式下的游戏流程:
- Cricket 游戏流程:
Cricket 游戏被初始化,输出 “Cricket Game Initialized! Start playing.”。
游戏开始,输出 “Cricket Game Started. Enjoy the game!”。
游戏结束,输出 “Cricket Game Finished!”。
- Football 游戏流程:
Football 游戏被初始化,输出 “Football Game Initialized! Start playing.”。
游戏开始,输出 “Football Game Started. Enjoy the game!”。
游戏结束,输出 “Football Game Finished!”。
这个运行结果说明了模板方法模式的核心思想:抽象类定义了算法的骨架,其中包括了具体步骤的调用顺序,而具体的步骤实现由子类来完成。在这里,Game 类定义了一个通用的游戏流程模板,而 Cricket 和 Football 类则提供了特定游戏的具体实现。
2. 设计框架
抽象类经常被用于设计框架。框架提供了一组基础功能,但允许用户根据自己的需求扩展或定制功能。
演示代码
// 定义抽象类 Framework abstract class Framework { // 框架提供的基础功能 void basicFunctionality() { System.out.println("Framework: Providing basic functionality..."); } // 抽象方法,允许用户根据需要扩展功能 abstract void extendedFunctionality(); } // 用户自定义的类,继承框架类,扩展功能 class MyFramework extends Framework { // 实现扩展功能 @Override void extendedFunctionality() { System.out.println("MyFramework: Adding extended functionality..."); } } // 主类 public class Main { public static void main(String[] args) { // 创建用户自定义的框架对象 MyFramework myFramework = new MyFramework(); // 使用框架提供的基础功能 myFramework.basicFunctionality(); // 使用用户扩展的功能 myFramework.extendedFunctionality(); } }
这个运行结果反映了设计框架的典型模式。
- Framework: Providing basic functionality…
这一行输出表示框架提供了基础功能,即 basicFunctionality() 方法的执行结果。
- MyFramework: Adding extended functionality…
这一行输出表示用户自定义的框架在基础功能的基础上添加了扩展功能,即 extendedFunctionality() 方法的执行结果。
整个运行结果展示了抽象类在设计框架中的用法。基础功能由框架提供,而用户可以根据需要扩展功能,使框架更加灵活和适用于特定的场景。
3. 代码复用
抽象类可以在多个相关的类之间实现代码复用。它可以包含一组通用的方法和字段,减少了重复代码的编写。
演示代码
// 定义抽象类 AbstractShape abstract class AbstractShape { // 通用方法:计算面积 abstract double calculateArea(); // 通用方法:计算周长 abstract double calculatePerimeter(); // 通用字段:形状名称 String shapeName; // 构造方法 public AbstractShape(String shapeName) { this.shapeName = shapeName; } // 通用方法:展示形状信息 void displayInfo() { System.out.println("Shape: " + shapeName); System.out.println("Area: " + calculateArea()); System.out.println("Perimeter: " + calculatePerimeter()); } } // 具体形状类:圆形 class Circle extends AbstractShape { double radius; // 构造方法 public Circle(double radius) { super("Circle"); this.radius = radius; } // 实现抽象方法:计算面积 @Override double calculateArea() { return Math.PI * radius * radius; } // 实现抽象方法:计算周长 @Override double calculatePerimeter() { return 2 * Math.PI * radius; } } // 具体形状类:矩形 class Rectangle extends AbstractShape { double width; double height; // 构造方法 public Rectangle(double width, double height) { super("Rectangle"); this.width = width; this.height = height; } // 实现抽象方法:计算面积 @Override double calculateArea() { return width * height; } // 实现抽象方法:计算周长 @Override double calculatePerimeter() { return 2 * (width + height); } } // 主类 public class Main { public static void main(String[] args) { // 创建圆形对象并展示信息 Circle circle = new Circle(5); circle.displayInfo(); System.out.println(); // 创建矩形对象并展示信息 Rectangle rectangle = new Rectangle(4, 6); rectangle.displayInfo(); } }
这段代码展示了使用抽象类和具体类来表示不同形状(圆形和矩形)的面积和周长。
- Shape: Circle
- 这一行表示创建了一个圆形,并展示了它的形状名称为 “Circle”。
- 下一行 “Area: 78.53981633974483” 表示圆形的面积为约 78.54。
- 最后一行 “Perimeter: 31.41592653589793” 表示圆形的周长为约 31.42。
- Shape: Rectangle
- 这一行表示创建了一个矩形,并展示了它的形状名称为 “Rectangle”。
- 下一行 “Area: 24.0” 表示矩形的面积为 24。
- 最后一行 “Perimeter: 20.0” 表示矩形的周长为 20。
这个代码展示了如何利用抽象类 AbstractShape 和具体类 Circle 和 Rectangle 来表示不同形状的特性(面积和周长),并通过调用相应的方法展示了这些形状的具体信息。这种设计模式使得代码更加模块化和可扩展,同时提高了代码的可读性和维护性。