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

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

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


接口和抽象类的区别


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 来表示不同形状的特性(面积和周长),并通过调用相应的方法展示了这些形状的具体信息。这种设计模式使得代码更加模块化和可扩展,同时提高了代码的可读性和维护性。


相关文章
|
20天前
|
Java 程序员
Java社招面试题:& 和 && 的区别,HR的套路险些让我翻车!
小米,29岁程序员,分享了一次面试经历,详细解析了Java中&和&&的区别及应用场景,展示了扎实的基础知识和良好的应变能力,最终成功获得Offer。
53 14
|
19天前
|
Java 关系型数据库 数据库
京东面试:聊聊Spring事务?Spring事务的10种失效场景?加入型传播和嵌套型传播有什么区别?
45岁老架构师尼恩分享了Spring事务的核心知识点,包括事务的两种管理方式(编程式和声明式)、@Transactional注解的五大属性(transactionManager、propagation、isolation、timeout、readOnly、rollbackFor)、事务的七种传播行为、事务隔离级别及其与数据库隔离级别的关系,以及Spring事务的10种失效场景。尼恩还强调了面试中如何给出高质量答案,推荐阅读《尼恩Java面试宝典PDF》以提升面试表现。更多技术资料可在公众号【技术自由圈】获取。
|
1月前
|
存储 缓存 网络协议
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点,GET、POST的区别,Cookie与Session
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点、状态码、报文格式,GET、POST的区别,DNS的解析过程、数字证书、Cookie与Session,对称加密和非对称加密
|
2月前
|
编译器
经典面试题:变量的声明和定义有什么区别
在编程领域,变量的“声明”与“定义”是经典面试题之一。声明告诉编译器一个变量的存在,但不分配内存,通常包含变量类型和名称;而定义则为变量分配内存空间,一个变量必须至少被定义一次。简而言之,声明是告知变量形式,定义则是实际创建变量并准备使用。
|
2月前
|
XML 前端开发 Java
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
本文阐述了Spring、Spring Boot和Spring MVC的关系与区别,指出Spring是一个轻量级、一站式、模块化的应用程序开发框架,Spring MVC是Spring的一个子框架,专注于Web应用和网络接口开发,而Spring Boot则是对Spring的封装,用于简化Spring应用的开发。
175 0
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
|
3月前
|
SQL JSON Java
springboot 如何编写增删改查后端接口,小白极速入门,附完整代码
本文为Spring Boot增删改查接口的小白入门教程,介绍了项目的构建、配置YML文件、代码编写(包括实体类、Mapper接口、Mapper.xml、Service和Controller)以及使用Postman进行接口测试的方法。同时提供了SQL代码和完整代码的下载链接。
springboot 如何编写增删改查后端接口,小白极速入门,附完整代码
|
2月前
|
前端开发 小程序 JavaScript
面试官:px、em、rem、vw、rpx 之间有什么区别?
面试官:px、em、rem、vw、rpx 之间有什么区别?
59 0
|
4月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
1月前
|
存储 缓存 算法
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
本文介绍了多线程环境下的几个关键概念,包括时间片、超线程、上下文切换及其影响因素,以及线程调度的两种方式——抢占式调度和协同式调度。文章还讨论了减少上下文切换次数以提高多线程程序效率的方法,如无锁并发编程、使用CAS算法等,并提出了合理的线程数量配置策略,以平衡CPU利用率和线程切换开销。
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
|
1月前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?

热门文章

最新文章