后端面试题:接口和抽象类的区别?抽象类可以多继承吗?

简介: 字节后端面试题:接口和抽象类的区别?抽象类可以多继承吗?

后端面试题:接口和抽象类的区别?抽象类可以多继承吗?


接口和抽象类的区别


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 方法
    }
}


主要区别:


  1. 方法实现: 接口中的方法只有声明,没有实现,而抽象类中的抽象方法可以有实现。
  2. 多继承: Java中接口可以多继承,一个类可以实现多个接口,但是类只能单继承。而抽象类只能单继承。
  3. 访问修饰符: 接口中的方法默认是 public,抽象类中的抽象方法可以有不同的访问修饰符。
  4. 成员变量: 接口中只能包含常量,而抽象类可以包含非抽象方法、字段和常量。
  5. 设计理念: 接口用于定义类应该遵循的行为,而抽象类用于建模类之间的共性关系。


在设计程序时,要根据需求和设计理念选择使用接口还是抽象类。接口用于定义类之间的协议,实现了接口的类必须实现接口中的所有方法。抽象类则用于建模类之间的通用关系,它提供了一组默认的实现,但允许子类覆盖它们。


演示代码

// 定义接口
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();
    }
}


这个运行结果说明了程序中的几个重要点:


  1. Method 1 implemented from interface.

这一行输出表明 MyClass 类成功实现了接口 MyInterface 中的抽象方法 method1()。通过实现接口,MyClass 类表明它遵循了接口所定义的行为。


  1. Method 2 implemented from abstract class.

这一行输出表明通过匿名内部类的方式,成功实现了抽象类 MyAbstractClass 中的抽象方法 method2()。匿名内部类是在实例化抽象类时直接定义的,它重写了抽象方法 method2(),提供了具体的实现。


  1. 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 方法
    }
}


这个运行结果表明了程序成功创建了两个对象并调用了它们的方法:


  1. Dog barks…

这一行输出表示程序成功创建了一个 Dog 对象,并调用了它的 makeSound() 方法。输出表明狗发出了叫声,这是 Dog 类中的实现。


  1. 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();
    }
}

这个运行结果表示了两个具体游戏类的实例在执行模板方法模式下的游戏流程:


  1. Cricket 游戏流程:

Cricket 游戏被初始化,输出 “Cricket Game Initialized! Start playing.”。

游戏开始,输出 “Cricket Game Started. Enjoy the game!”。

游戏结束,输出 “Cricket Game Finished!”。


  1. 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();
    }
}


这个运行结果反映了设计框架的典型模式。


  1. Framework: Providing basic functionality…

这一行输出表示框架提供了基础功能,即 basicFunctionality() 方法的执行结果。


  1. 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();
    }
}

这段代码展示了使用抽象类和具体类来表示不同形状(圆形和矩形)的面积和周长。


  1. Shape: Circle


  • 这一行表示创建了一个圆形,并展示了它的形状名称为 “Circle”。
  • 下一行 “Area: 78.53981633974483” 表示圆形的面积为约 78.54。
  • 最后一行 “Perimeter: 31.41592653589793” 表示圆形的周长为约 31.42。


  1. Shape: Rectangle


  • 这一行表示创建了一个矩形,并展示了它的形状名称为 “Rectangle”。
  • 下一行 “Area: 24.0” 表示矩形的面积为 24。
  • 最后一行 “Perimeter: 20.0” 表示矩形的周长为 20。


这个代码展示了如何利用抽象类 AbstractShape 和具体类 Circle 和 Rectangle 来表示不同形状的特性(面积和周长),并通过调用相应的方法展示了这些形状的具体信息。这种设计模式使得代码更加模块化和可扩展,同时提高了代码的可读性和维护性。


相关文章
|
21天前
|
前端开发 JavaScript 关系型数据库
从前端到后端:构建现代化Web应用的技术探索
在当今互联网时代,Web应用的开发已成为了各行各业不可或缺的一部分。从前端到后端,这篇文章将带你深入探索如何构建现代化的Web应用。我们将介绍多种技术,包括前端开发、后端开发以及各种编程语言(如Java、Python、C、PHP、Go)和数据库,帮助你了解如何利用这些技术构建出高效、安全和可扩展的Web应用。
|
23天前
|
前端开发 小程序 Java
uniapp上传图片 前端以及java后端代码实现
uniapp上传图片 前端以及java后端代码实现
34 0
|
2月前
|
JSON 前端开发 Java
layui上传图片,前端直接拷代码,后端……
layui上传图片,前端直接拷代码,后端……
31 0
|
2月前
|
前端开发
前端接受后端文件流并下载到本地的方法
前端接受后端文件流并下载到本地的方法
60 0
|
2月前
|
存储 前端开发 JavaScript
从前端到后端,探索现代Web开发技术
本文探索了现代Web开发技术的各个方面,包括前端和后端开发以及多种编程语言的应用。通过对JavaScript、Java、Python、C、PHP和Go等语言的介绍,深入探讨了前端和后端开发的基本原理和常用工具。同时,还涵盖了数据库技术在Web开发中的重要性和应用场景。无论你是初学者还是有经验的开发者,本文都能为你提供全面的视角和实用的知识,帮助你在Web开发领域取得更好的成果。
|
3月前
|
前端开发 JavaScript Java
从前端到后端:构建全栈应用的技术路线探析
【2月更文挑战第3天】本文通过探讨前端和后端开发的基本概念和技术要点,深入剖析了构建全栈应用的技术路线。从前端的HTML、CSS和JavaScript,到后端的Java、C和数据库,我们将带您逐步了解如何将不同技术组合起来实现高效、稳定的全栈应用。
58 7
|
2月前
|
弹性计算 前端开发 小程序
微信小程序上传文件至阿里云OSS直传(java后端签名+前端直传)
当前的通用文件上传方式是通过前端上传到服务器,再由服务器转存至对象存储。这种方式在处理小文件时效率尚可,但大文件上传因受限于服务器带宽,速度较慢。例如,一个100MB的文件在5Mbps带宽的阿里云ECS上上传至服务器需160秒。为解决此问题,可以采用后端签名的方式,使微信小程序直接上传文件到阿里云OSS,绕过服务器中转。具体操作包括在JAVA后端引入相关依赖,生成签名,并在微信小程序前端使用这个签名进行文件上传,注意设置正确的请求头和formData参数。这样能提高大文件上传的速度。
|
3月前
|
前端开发 NoSQL Java
从前端到后端:构建现代化的全栈应用
【2月更文挑战第3天】本文将探讨如何从前端到后端构建现代化的全栈应用。我们将介绍前端技术的发展趋势,包括最新的框架和工具,以及如何与后端进行无缝集成。同时,我们将深入研究后端开发的关键技术,如Java、C和数据库,并提供实用的建议和最佳实践。
|
5天前
|
前端开发 JavaScript Java
前端与后端:构建现代Web应用的双翼
前端与后端:构建现代Web应用的双翼