Java笔试面试题AI答之面向对象(9)

news/2024/9/19 0:44:26/ 标签: java, 开发语言

文章目录

  • 49. 简述Java继承时,类的执行顺序是什么?
      • 一、类的静态成员初始化顺序
      • 二、对象的初始化顺序
      • 三、总结
  • 50. 举例说明什么情况下会更倾向于使用抽象类而不是接口?
      • 1. 当需要定义和实现部分通用行为时
      • 2. 当需要访问修饰符或方法修饰符时
      • 3. 当需要定义成员变量时
      • 4. 当需要限制继承关系时
  • 51. 请列举适配器模式的应用场景 ?
      • 1. 接口不兼容
      • 2. 类的复用
      • 3. 封装适配逻辑
      • 4. 设计灵活性
      • 5. 其他应用场景
  • 52. 解释 Java 中,为什么不允许从静态方法中访问非静态变量?
  • 53. 简述Java中继承与聚合的区别 ?
      • 继承(Inheritance)
        • 优点
        • 缺点
      • 聚合(Aggregation)
        • 优点
        • 缺点
      • 总结
  • 54. Java序列化和反序列化为什么要实现Serializable接口?

49. 简述Java继承时,类的执行顺序是什么?

在Java中,继承是面向对象编程的一个重要特性,它允许我们定义一个类(子类)来继承另一个类(父类)的属性和方法。当涉及到类的执行顺序时,主要关注的是类的初始化顺序,包括静态成员、非静态成员以及构造方法的执行顺序。以下是Java继承时类的执行顺序的详细说明:

一、类的静态成员初始化顺序

  1. 父类静态变量和静态代码块:当类被加载到JVM中时,首先会初始化父类的静态变量和静态代码块(按它们在类中出现的顺序)。静态代码块在类加载时执行,且只执行一次。

  2. 子类静态变量和静态代码块:接着,会初始化子类的静态变量和静态代码块(同样按它们在类中出现的顺序)。

二、对象的初始化顺序

  1. 父类非静态变量:在创建对象时,首先为父类的非静态变量(实例变量)分配内存,并进行默认初始化(如果未显式初始化)。

  2. 父类构造代码块:然后执行父类的构造代码块(如果有的话),这些代码块在父类构造方法之前执行。

  3. 父类构造方法:接下来调用父类的构造方法。如果子类构造方法中没有显式调用父类的构造方法(通过super()),则默认调用父类的无参构造方法。

  4. 子类非静态变量:为子类的非静态变量分配内存,并进行默认初始化(如果未显式初始化)。

  5. 子类构造代码块:执行子类的构造代码块(如果有的话)。

  6. 子类构造方法:最后执行子类的构造方法。

三、总结

Java继承时类的执行顺序可以概括为:

  1. 父类静态变量和静态代码块(按出现顺序)
  2. 子类静态变量和静态代码块(按出现顺序)
  3. 父类非静态变量初始化
  4. 父类构造代码块
  5. 父类构造方法
  6. 子类非静态变量初始化
  7. 子类构造代码块
  8. 子类构造方法

需要注意的是,静态成员(包括静态变量和静态代码块)的初始化只会在类加载时执行一次,与创建多少个类的实例无关。而非静态成员的初始化和构造方法的调用则是在每次创建类的实例时都会进行的。此外,如果子类构造方法中显式调用了父类的有参构造方法,则不会调用父类的无参构造方法。

50. 举例说明什么情况下会更倾向于使用抽象类而不是接口?

在软件开发过程中,选择使用抽象类还是接口往往取决于具体的设计需求和场景。以下是一些更倾向于使用抽象类而不是接口的情况:

1. 当需要定义和实现部分通用行为时

场景描述:当你希望为一系列子类提供一个共同的模板,并在这个模板中实现一些通用方法时,抽象类是一个更好的选择。接口只能定义方法的签名,而不能提供具体实现。

举例:假设你正在开发一个游戏,其中有多种角色(如战士、法师、盗贼等),这些角色都有一些共同的行为,如移动、攻击等。同时,每种角色还有其独特的技能。在这种情况下,你可以定义一个抽象类Character,其中包含移动和攻击等通用方法的实现,并让具体的角色类(如WarriorMageRogue)继承这个抽象类,并实现各自独特的技能方法。

2. 当需要访问修饰符或方法修饰符时

场景描述:接口中的方法默认是public abstract的,且不能有实现体。而抽象类中的方法可以有各种访问修饰符(如publicprotected)和方法修饰符(如staticfinal,但抽象方法不能是final的),以及可以有实现体。

举例:如果你需要定义一个方法,这个方法在某些子类中可能有默认实现,但在其他子类中需要被重写,那么你可以将这个方法放在抽象类中,并根据需要设置其访问修饰符。

3. 当需要定义成员变量时

场景描述:接口中不能定义成员变量(只能定义常量),而抽象类中可以定义成员变量。如果子类需要继承一些共同的属性,那么抽象类更合适。

举例:假设你正在开发一个图书馆管理系统,其中有多种书籍(如小说、教材、杂志等)。这些书籍都有一些共同的属性,如ISBN号、作者、出版日期等。你可以定义一个抽象类Book,其中包含这些共同属性的定义,并让具体的书籍类(如NovelTextbookMagazine)继承这个抽象类。

4. 当需要限制继承关系时

场景描述:在某些情况下,你可能希望限制只有特定的类才能继承某个类,或者希望控制继承的层次结构。虽然这可以通过其他设计模式(如工厂模式、单例模式等)来实现,但抽象类本身也可以作为一种限制手段。

举例:在某些框架或库中,可能有一个基础的抽象类,它定义了一系列核心方法和属性,但出于设计考虑,只允许特定的子类来继承这个抽象类。这可以通过将抽象类的构造方法设置为protectedprivate(虽然通常不推荐将抽象类的构造方法设置为private,因为这将使得抽象类无法被实例化,但可以通过其他方式如静态工厂方法来创建实例),并结合包级私有访问控制来实现。

综上所述,抽象类和接口各有其适用场景。在需要定义和实现部分通用行为、需要访问修饰符或方法修饰符、需要定义成员变量或需要限制继承关系时,更倾向于使用抽象类。然而,在实际开发中,应根据具体的设计需求和场景来灵活选择使用抽象类还是接口。

51. 请列举适配器模式的应用场景 ?

适配器模式(Adapter Pattern)是一种结构型设计模式,它允许将一个类的接口转换成客户端所期望的另一个接口,使原本由于接口不兼容而不能一起工作的类可以协同工作。以下是适配器模式的一些主要应用场景:

1. 接口不兼容

  • 老代码与新接口:当系统中的某个类的接口与客户端期望的接口不兼容时,可以使用适配器模式将老代码适配到新接口上,从而避免修改原有代码带来的风险。
  • 第三方库接口适配:在使用第三方库时,如果第三方库的接口与现有系统的接口不兼容,可以通过适配器模式将第三方库的接口转换成系统所期望的接口。

2. 类的复用

  • 复用现有类:当需要复用一些现有的类,但其接口与系统要求的接口不匹配时,可以通过适配器模式将这些类适配到系统中,提高代码复用率。
  • 遗留系统整合:在整合遗留系统时,可能会遇到接口不兼容的问题,适配器模式可以帮助解决这一问题,使得新系统能够与遗留系统顺利交互。

3. 封装适配逻辑

  • 业务逻辑无关的适配:当需要封装与具体业务逻辑无关的适配逻辑时,可以使用适配器模式将适配的细节封装在适配器中,使客户端代码更简洁、更专注于业务逻辑的实现。

4. 设计灵活性

  • 扩展接口:在系统设计初期,可能无法准确预测未来所有可能的接口需求。通过使用适配器模式,可以在不修改现有代码的情况下,通过添加新的适配器类来扩展接口,提高系统的可扩展性。
  • 支持多种数据源:在处理多种数据源时,每种数据源可能有不同的接口。通过适配器模式,可以为每种数据源提供一个适配器类,将这些数据源的接口统一转换成系统内部使用的接口,从而提高系统的灵活性和可维护性。

5. 其他应用场景

  • 双向适配器:在某些情况下,可能需要双向适配,即既可以将适配者接口转换成目标接口,也可以将目标接口转换成适配者接口。双向适配器模式可以满足这种需求。
  • 框架与库之间的桥接:在开发过程中,可能会使用多个框架或库,这些框架或库之间可能存在接口不兼容的问题。通过适配器模式,可以在这些框架或库之间建立桥接,使它们能够协同工作。

综上所述,适配器模式在软件开发中具有广泛的应用场景,特别是在处理接口不兼容、类的复用、封装适配逻辑、提高设计灵活性等方面发挥着重要作用。

52. 解释 Java 中,为什么不允许从静态方法中访问非静态变量?

在Java中,不允许从静态方法中访问非静态变量,这主要是由于静态和非静态成员之间的根本区别决定的。

  1. 存储位置不同

    • 静态变量存储在方法区的静态区域,属于类级别,所有实例共享一份数据。
    • 非静态变量存储在堆内存中,属于对象级别,每个对象都有自己的副本。
  2. 生命周期不同

    • 静态变量随着类的加载而加载,随着类的卸载而卸载。
    • 非静态变量随着对象的创建而存在,随着对象的销毁而销毁。
  3. 访问方式不同

    • 静态方法可以通过类名直接调用,无需创建对象。
    • 非静态方法必须通过对象来调用。

由于这些差异,静态方法中没有this关键字的概念,this关键字用于引用当前对象,而静态方法是属于类的,不属于任何对象。因此,静态方法中无法直接访问属于对象级别的非静态变量。

如果需要在静态方法中访问非静态变量,你可以通过创建类的实例(对象)来访问,或者使用静态方法传递非静态变量作为参数的方式进行访问。这样做是因为非静态变量依赖于具体的对象实例,而静态方法在没有对象实例的情况下也能被调用,所以直接访问非静态变量是不合理的。

53. 简述Java中继承与聚合的区别 ?

在Java中,继承(Inheritance)和聚合(Aggregation)是实现代码复用和组织类之间关系的两种不同方式。它们各有特点,适用于不同的场景。

继承(Inheritance)

继承是面向对象编程中的一个核心概念,它允许创建一个基于现有类的新类,继承其属性和方法,并可以添加新的属性和方法或覆盖(Override)现有的方法。继承关系表示一种“是一个(is-a)”的关系。例如,如果我们有一个Animal类,那么Dog类可以通过继承Animal类来获得Animal类的特性,并且Dog“是一个”Animal

优点
  • 代码复用
  • 方法重写,实现多态
缺点
  • 破坏封装性
  • 父类变,子类就得变
  • 继承是侵入性的

聚合(Aggregation)

聚合是另一种类之间的关系,表示“有一个(has-a)”的关系。在这种关系中,一个类包含对另一个类的引用作为它的成员变量。与继承不同,聚合允许你将不同的类组合在一起,以表示更复杂的结构,同时保持类的独立性。例如,Classroom类可以包含一个Student类的列表,表示这个班级有这些学生,但是Classroom并不是Student的一种类型。

优点
  • 降低了类之间的耦合度
  • 提高了代码的灵活性和可维护性
缺点
  • 需要手动管理对象之间的关系

总结

  • 继承主要用于表示类之间的层次关系,是“是一个”的关系。
  • 聚合主要用于表示类之间的组合关系,是“有一个”的关系。
  • 继承是强耦合的,子类依赖于父类的实现;而聚合是松耦合的,允许类之间的独立开发和使用。
  • 在实际开发中,应该优先考虑使用聚合,因为它提供了更好的灵活性和可维护性,只有在确实需要表示“是一个”的关系时才使用继承。

54. Java序列化和反序列化为什么要实现Serializable接口?

在Java中,序列化和反序列化是对象持久化的两种基本机制。序列化是指将对象的状态信息转换为可以存储或传输的形式的过程,以便稍后可以通过反序列化重建对象。为了实现这一过程,Java提供了一个称为Serializable的标记接口。

Serializable接口是一个标记接口,它不包含任何方法。它的作用主要是告诉Java虚拟机(JVM),这个类的对象可以被序列化。JVM在序列化对象时会检查对象所属的类是否实现了Serializable接口,如果没有实现,就会抛出NotSerializableException异常。

实现Serializable接口有几个好处:

  1. 兼容性:实现Serializable接口的类可以确保序列化的对象版本与类定义版本之间的兼容性。

  2. 安全性:Java序列化机制提供了基于对象序列化的安全特性,比如防止对敏感信息的非法访问。

  3. 易于实现:开发者只需实现Serializable接口,JVM就会自动处理序列化和反序列化的过程,无需编写额外的代码。

然而,仅仅实现Serializable接口并不足以处理所有序列化相关的问题。例如,如果一个类的某些字段是敏感的,不应该被序列化,可以使用transient关键字标记这些字段。此外,还可以自定义序列化过程,通过实现readObjectwriteObject方法,来精确控制序列化和反序列化的行为。

总的来说,Serializable接口是Java序列化机制的核心,它使得对象可以被序列化和反序列化,同时提供了灵活性和安全性,使得开发者可以根据需要定制序列化过程。

答案来自文心一言,仅供参考
在这里插入图片描述


http://www.ppmy.cn/news/1519929.html

相关文章

sqlite3的db.wait方法:等待所有查询完成

Node.js中sqlite3的db.wait方法深入解析 在Node.js环境中,sqlite3库为开发者提供了一个与SQLite数据库进行交互的简洁API。在处理数据库操作时,有时需要等待直到所有的查询都完成,这时db.wait方法就显得尤为重要。本文将深入解析sqlite3库中…

基于Python的机器学习系列(22):高斯混合模型(GMM)聚类的改进版

在之前的篇章中,我们介绍了高斯混合模型(GMM)及其基本实现。本文将扩展这一模型,重点是引入早停机制来提高训练效率,并且在训练过程中每隔一定的迭代次数绘制聚类结果,以便观察模型的收敛情况。 引入早停机…

Windows下使用pm2管理多个前端vue项目

1. 安装Node.js和npm: 确保你已经在Windows系统上安装了Node.js和npm。你可以在Node.js的[官方网站](https://nodejs.org/)下载并安装适合你系统的版本。 2. 安装pm2: 打开命令提示符(或PowerShell),运行以下命令来全局安装pm2: npm install pm2 -g 3. 创建pm2配置…

React16新手教程记录

文章目录 前言一些前端面试题1. 搭建项目1. 1 cdn1. 2 脚手架 2. 基础用法2.1 表达式和js语句区别:2.2 jsx2.3 循环map2.4 函数式组件2.5 类式组件2.6 类组件点击事件2.6.1 事件回调函数this指向2.6.2 this解决方案2.6.2.1 通过bind2.6.2.2 箭头函数(推荐…

【C++ 游戏】密室逃脱

首先来大张旗鼓的介绍一下: 全网之最: 本游戏为全网第一篇C语言的密室逃脱类剧情游戏 本游戏为全网第一篇将画面类同等性质转化为文字类的游戏 本游戏为画——文类型游戏的突破口,适合借鉴 哈哈好了不吹了,不过上面的都是真的。 …

温馨网站练习运用

第二次与团队一起制作网页虽然不进行商用,但是练习一下还是好的😊😊 我主要负责后端部分,该项目用了SpringBoot框架、SpringSecurity的安全框架、结合MyBatis-Plus的数据库查询。如果想看看,网站:温馨网登…

AI大模型之旅-本地安装llm工具dify 和 fastgpt

一:安装dify 官网地址: https://dify.ai/ 克隆 Dify 源代码至本地。 git clone https://github.com/langgenius/dify.git 启动 Dify 进入 Dify 源代码的 docker 目录,执行一键启动命令: cd dify/docker cp .env.example .env docker com…

多目标应用:基于自组织分群的多目标粒子群优化算法(SS-MOPSO)的移动机器人路径规划研究(提供MATLAB代码)

一、机器人路径规划介绍 移动机器人(Mobile robot,MR)的路径规划是 移动机器人研究的重要分支之,是对其进行控制的基础。根据环境信息的已知程度不同,路径规划分为基于环境信息已知的全局路径规划和基于环境信息未知或…

71. 简化路径算法实现详解(goalng版)

LeetCode 71. 简化路径详解 一、题目描述 给你一个字符串 path,表示指向某一文件或目录的 Unix 风格绝对路径(以 ‘/’ 开头),请你将其转化为更加简洁的规范路径。 在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点(…)表示将目录切换到上一级(…

数据结构代码集训day14(适合考研、自学、期末和专升本)

题目均来自b站up:白话拆解数据结构! 今日题目如下:(1)试写一个算法判断给定字符序列是否是回文。 (2)给定一个算法判断输入的表达式中括号是否匹配。假设只有花、中、尖三种括号。 题1 回文序列…

【类模板】类模板的特化

一、类模板的泛化 与函数模板一样&#xff0c;类模板的泛化就是普通的模板&#xff0c;不具有特殊性的模板。 以下的类模板为泛化的版本 //类模板的泛化 template<typename T,typename U> struct TC {//静态成员变量static int static_varible; //声明TC() {std::cout…

【Java EE】JVM

目录 1. JVM简介 2.JVM运行流程 3.JVM运行时数据区 3.1 堆&#xff08;线程共享&#xff09; 3.2 Java虚拟机栈&#xff08;线程私有&#xff09; 1. JVM简介 JVM是 Java Virtual Machine 的简称&#xff0c;意为Java虚拟机。 虚拟机是指通过软件模拟的具有完整硬件功能的…

python-简单的dos攻击

前言 这个是DOS攻击学习(注意&#xff1a;千万别去攻击有商业价值的服务器或应用&#xff0c;不然会死的很惨(只有一个IP通过公网访问容易被抓),前提是网站没有攻击防御) 创建一个以python编写的后端web服务(好观察) 安装flask pip install flask from flask import Flaskapp …

Android使用前台服务

Android使用前台服务 服务几乎都是在后台运行的&#xff0c;一直以来它都是默默地做着辛苦的工作。但是服务的系统优先级还是比较低的&#xff0c;当系统出现内存不足的情况时&#xff0c;就有可能会回收掉正在后台运行的服务。 如果你希望服务可以一直保持运行状态&#xff…

使用golang的AST编写定制化lint

什么是lint &#xff08;来自wiki&#xff09;在计算机科学中&#xff0c;lint是一种工具程序的名称&#xff0c;它用来标记源代码中&#xff0c;某些可疑的、不具结构性&#xff08;可能造成bug&#xff09;的段落。它是一种静态程序分析工具&#xff0c;最早适用于C语言&…

【13年12月CCF计算机软件能力认证】:出现次数最多的数、ISBN号码、最大的矩形、有趣的数、I‘m stuck!

题目概括出现次数最多的数暴力枚举&#xff0c;非常简单ISBN号码直接模拟&#xff0c;非常简单最大的矩形用到双指针&#xff08;优化枚举&#xff09;&#xff0c;非常简单有趣的数用到了数学知识排列组合&#xff0c;有一定思维难度I’m stuck!我用到了两个dfs来解决&#xf…

【区块链 + 供应链】广汽本田区块链合同供应链管理系统 | FISCO BCOS应用案例

广汽本田是国内汽车制造的龙头&#xff0c;每年销售额超千亿级别&#xff0c;每年的合同采购规模量在百亿以上。企业内部采用传 统的中心化方式管理合同&#xff0c;由于涉及部门众多&#xff0c;需要管理的合同要素也各不相同&#xff0c;造成信息不集中、合同版本众多、 合同…

C#中lock(this)与lock(private object)区别

前言 在使用多线程编程时&#xff0c;我们会对代码关键部分确保其一次只由一个线程执行&#xff0c;对于防止争用条件和保持数据完整性至关重要。在C#中&#xff0c;lock 语句就是用于通过同步对共享资源的访问来实现此目的工具。本文介绍lock(this) 与lock(private object) 两…

重新修改 Qt 项目的 Kit 配置

要重新修改 Qt 项目的 Kit 配置&#xff0c;你可以按照以下步骤进行操作&#xff1a; 1. 打开 Qt Creator 首先&#xff0c;启动 Qt Creator&#xff0c;确保你的项目已经打开。 2. 进入项目设置 在 Qt Creator 中&#xff0c;点击菜单栏的 “Projects” 标签&#xff08;通…

Spark MLlib模型训练—回归算法 Decision tree regression

Spark MLlib模型训练—回归算法 Decision tree regression 在机器学习中,决策树是一种常用且直观的模型,广泛应用于分类和回归任务。决策树回归 (Decision Tree Regression) 通过将数据集分割成多个区域,构建一棵树形结构,以预测目标变量的连续值。本文将详细探讨 Spark 中…