面试题手册

梳理高频技术问题,帮助你按主题复习和查漏补缺。

前端阅读 02月7日 11:46

YAML和JSON有什么区别?

YAML和JSON都是数据序列化格式,常用于配置文件与数据交换。它们之间的主要区别包括:可读性:YAML 以可读性为设计目标,支持注释,采用缩进表示层级关系,比较适合人类阅读。JSON 更加简洁,数据格式明确,主要用于机器解析,不支持注释。数据表示:YAML 支持的数据类型更丰富,比如可以直接表示日期、时间等类型。JSON 支持的数据类型较少,基本上包括数字、字符串、数组、对象等。冗余:YAML 允许在定义中使用锚点(&)和别名(*)来减少重复内容,增加复用性。JSON 中没有类似于YAML中的锚点和别名的功能。解析:YAML 的解析通常比JSON复杂,因为其支持更丰富的特性和灵活的结构。JSON 解析相对简单,因为格式固定且较为严格。使用场景:YAML 常用于复杂的配置文件,如Kubernetes的配置。JSON 更多用于网络传输中的数据交换格式。根据具体需求选择合适的格式是很重要的,YAML更适合那些需要高可读性和复杂配置的场景,而JSON则更适用于数据交换和Web环境。
前端阅读 02月7日 11:42

单元测试和功能测试有什么区别?

单元测试(Unit Testing)主要是针对程序中的最小可测试单元进行检查和验证,比如函数或方法,重点是检测每个部分能否正确执行其定义的功能。单元测试通常由开发人员编写,目的是确保代码的每个部分都按预期工作。功能测试(Functional Testing),也称为功能性测试,是在更宏观的层面上对软件系统进行测试,以确保软件的各个功能能够按照需求规格说明书执行。功能测试更多地关注于用户界面和用户需求,确保软件系统作为一个整体能够满足用户的业务需求。总的来说,单元测试关注点在于单个组件的内部正确性,而功能测试关注的是多个组件或整个系统在一起时的外部行为和功能是否符合预期。
前端阅读 02月7日 11:35

在C++中什么时候应该使用类和结构?

在C++中,类(class)和结构(struct)都用于定义新的数据类型,但它们在语义上和默认的访问控制上有所不同:默认访问控制:struct的成员和继承默认是公开(public)的。class的成员和继承默认是私有(private)的。使用场景:结构(struct):通常用于定义纯数据结构,没有太多的方法和复杂的逻辑。它主要用于数据的存储和简单的数据处理,适合用于那些需要打包数据的场合,比如定义协议数据包、表示简单的数据记录等。类(class):适用于需要封装(Encapsulation)、继承(Inheritance)和多态(Polymorphism)的复杂对象模型。类提供了更多的功能,比如可以声明为私有的成员,提供接口和实现的分离,支持继承和多态等特性,更适合构建复杂的系统。选择建议:如果数据成员可以自由访问,并且您只需要一个简单的数据容器,那么使用struct更合适。如果您需要封装特性,控制数据访问,或者使用继承和多态等面向对象的特性,那么应该使用class。总的来说,选择struct还是class,主要看您的需求是否需要复杂的功能和访问控制。在实际开发中,这两者的界限可能并不是非常严格,有时候也是根据团队的编程风格和习惯来选择。
前端阅读 02月7日 11:35

Dart 如何删除字符串的所有空格?

在Dart中,你可以使用replaceAll方法来删除字符串中的所有空格。这个方法允许你指定一个模式(这里是空格)和一个替换值(这里是空字符串)。下面是如何实现的具体示例:void main() { String originalString = "这 是 一 个 测试 字 符 串"; String stringWithoutSpaces = originalString.replaceAll(' ', ''); print(stringWithoutSpaces); // 输出:这是一个测试字符串}在这个例子中,replaceAll(' ', '')会将字符串中所有的空格替换成空字符串,从而删除了所有空格。
前端阅读 02月7日 11:32

Dart 如何声明常量?

在Dart中,可以通过final和const关键字来声明常量。final: 当你不想改变一个变量的值,可以使用final。final被赋值后,其值不可改变,但是它需要在运行时被赋值,即可以在构造函数或其他方法中进行赋值。 final String name = 'John Doe';或者在运行时赋值: final DateTime currentTime = DateTime.now();const: 当你想要定义编译时常量时,可以使用const。const常量是一个编译时常量,其所有的值都需要在编译时已知。 const double pi = 3.14159;你也可以用const来创建编译时的不可变集合: const List<int> numbers = [1, 2, 3, 4, 5];总的来说,选择final或const取决于你是否需要在编译时就确定变量的值。如果是,使用const;如果赋值依赖于运行时计算,使用final。
前端阅读 02月7日 11:32

Dart如何对Map的键进行排序

在Dart中,如果你想对一个Map的键进行排序,你可以通过将Map的键提取到一个列表中,然后对列表进行排序,最后根据这个已排序的键列表重新构建一个新的Map。这里是一个具体的步骤和示例代码:提取键并排序:将Map的所有键提取到一个列表中,使用List.sort方法对这个列表进行排序。根据排序的键重建Map:创建一个新的Map,并根据已排序的键列表,从原始Map中取得对应的值来填充新的Map。下面是一个具体的示例:void main() { Map<String, int> unsortedMap = { 'banana': 3, 'apple': 1, 'orange': 2 }; // 提取键到一个列表 var keys = unsortedMap.keys.toList(); // 对键列表进行排序 keys.sort(); // 创建一个新的Map,并根据已排序的键列表重新填充 Map<String, int> sortedMap = { for (var key in keys) key: unsortedMap[key] }; print(sortedMap); // 输出: {apple: 1, banana: 3, orange: 2}}这种方式适用于需要对键进行字典序或自定义排序的场景。如果需要其他类型的排序(如数值大小),可以在sort方法中提供自定义的比较函数。
前端阅读 02月7日 11:31

Dart 中如何处理异常?

在Dart中,异常处理主要依靠try、catch和finally这几个关键字。以下是处理异常的基本步骤:使用try块:将可能引发异常的代码放入try块中。捕获异常:使用catch块来捕获异常。可以指定一个或多个catch块来处理不同类型的异常。catch块可以接收一个异常对象,通常命名为e,还可以选择接收一个堆栈跟踪对象,通常命名为s。示例: try { // 可能抛出异常的代码 } catch (e) { // 处理异常 print('异常: $e'); }或者更详细地捕获: try { // 可能抛出异常的代码 } on SpecificException catch (e) { // 处理特定类型的异常 print('特定异常: $e'); } catch (e, s) { // 处理其它所有异常,并打印堆栈信息 print('异常: $e'); print('堆栈信息: $s'); }使用finally块:无论是否发生异常,finally块中的代码都会被执行。这经常用于资源清理,例如关闭文件或数据库连接。示例: try { // 可能抛出异常的代码 } catch (e) { // 处理异常 print('异常: $e'); } finally { // 清理代码,总是执行 print('这是finally块,无论是否发生异常都会执行。'); }通过这些机制,可以有效地处理在代码执行过程中可能出现的错误和异常,确保程序的稳定性和可靠性。
前端阅读 02月7日 11:29

Dart 如何获取文件名?

在Dart中,您可以使用path包来获取文件名。首先,您需要在项目中引入path包:import 'package:path/path.dart' as path;然后,使用path.basename()函数来获取文件名。这个函数接受一个文件路径作为参数,并返回文件名。例如:void main() { var filePath = '/path/to/the/file.txt'; var fileName = path.basename(filePath); print(fileName); // 输出: file.txt}如果您需要获取不包含扩展名的文件名,可以使用path.basenameWithoutExtension():void main() { var filePath = '/path/to/the/file.txt'; var fileNameWithoutExtension = path.basenameWithoutExtension(filePath); print(fileNameWithoutExtension); // 输出: file}
前端阅读 02月7日 11:29

Dart 如何创建自定义异常类?

在Dart中,您可以通过实现或扩展Exception或Error类来创建自定义异常类。通常,对于开发者期望通过程序控制逻辑来处理的异常情况,应当使用Exception;而对于程序内部错误,应使用Error。以下是创建一个自定义异常类的步骤:定义一个类:该类可以实现Exception接口或者直接继承自它。添加构造函数:通常会添加一个接收错误消息的构造函数。覆写toString方法:这样做可以提供更清晰的错误信息。下面是一个例子,展示如何定义一个名为CustomException的异常类:class CustomException implements Exception { final String message; CustomException(this.message); @override String toString() => "CustomException: $message";}在上面的代码中,CustomException类实现了Exception接口,并包含一个用于传递错误消息的构造函数。toString方法被覆写以提供更具体的错误描述。您可以这样使用这个自定义异常:void someFunction() { throw CustomException('这是一个自定义错误');}void main() { try { someFunction(); } catch (e) { print(e); }}当someFunction函数被调用时,它会抛出CustomException,然后在main函数中被捕获并打印异常信息。
前端阅读 02月7日 11:28

Dart中抽象类和接口有什么区别?

在Dart中,抽象类和接口都用于定义一组功能,但它们在实际使用和意图上有所不同:抽象类(Abstract Classes):抽象类是不能被实例化的类,只能被其他类继承。抽象类允许你定义构造函数,这可以在继承的类中重用。抽象类可以包含具体实现的方法,这意味着你可以为子类提供默认的行为。抽象类通常用于定义一个共通的基础框架,让子类继承并实现或重写特定功能。接口(Interfaces):Dart中没有专门的“interface”关键字,任何类都可以作为接口。当你将一个类用作接口时,实现该接口的类必须重写所有的方法,除非这些方法已经在其他地方得到了实现。接口主要用于定义可以由多种不相关类实现的一组API,这些类可能来自不同的类层次结构。接口强调的是实现多重继承的行为模式,这意味着一个类可以实现多个接口来组合多种行为。总结来说,抽象类更多是用于被继承并提供共通功能的基础模板,而接口则是定义了一组必须由实现类提供具体实现的行为规范。在实际使用时,选择抽象类还是接口取决于你的具体需求,是否需要从基类继承一些实现,或是需要多个类共同遵循一个明确的契约。
前端阅读 02月7日 11:28

Dart 如何定义和使用枚举(enum)?

在Dart中,枚举(enum)是一种特殊的类,用于表示一组固定数量的常量值。以下是如何定义和使用枚举的步骤:定义枚举首先,你可以通过使用关键字 enum 来定义一个枚举。枚举内部的每一个值都是这个枚举类型的一个实例。enum Status { none, running, stopped, paused}这里定义了一个名为 Status 的枚举,它有四个值:none、running、stopped 和 paused。使用枚举一旦定义了枚举,你就可以在你的代码中像使用其他任何数据类型一样使用它。例如,你可以声明一个枚举类型的变量,并给它赋一个枚举值:Status currentStatus = Status.running;枚举的比较你可以使用等号 == 来比较枚举的值:if (currentStatus == Status.running) { print('The application is running.');}枚举中的值和索引每个枚举值都有一个 index 属性,它返回该值在枚举声明中的位置(从0开始计数):print(Status.paused.index); // 输出: 3如果你想要获取所有的枚举值,可以使用 EnumName.values:for (var status in Status.values) { print('Available status: $status');}这样就可以遍历枚举 Status 中的所有值。总结通过这些基本的步骤,你可以在Dart中有效地定义和使用枚举,使得你的代码更加清晰和易于管理。
前端阅读 02月7日 11:28

Dart 如何注释代码?

在Dart中,注释代码有几种不同的方式:单行注释:使用双斜线 // 开始的注释,只会注释掉它后面的内容直到该行结束。 // 这是一个单行注释 int a = 5;多行注释:使用 /* 注释内容 */ 来注释多行。这种注释可以跨越多行,直到遇到关闭的 */。 /* 这是一个 多行注释 */ int b = 10;文档注释:使用三个斜线 /// 或者 /** */ 来进行文档注释,通常用于生成API文档。 /// 这是一个文档注释 /// 用来说明下面的函数功能 void myFunction() { // 函数实现 }使用这些不同类型的注释,可以帮助代码的其他阅读者理解代码的功能,也有助于未来代码的维护和更新。
前端阅读 02月7日 11:28

Dart 如何处理 JSON 数据?

在Dart中处理JSON数据主要涉及两个步骤:解析(parsing)和编码(encoding)。Dart提供了内置的json库来处理这些操作。以下是具体步骤和示例:1. 导入dart:convert库首先,需要导入Dart的dart:convert库,这个库包含了处理JSON所需的工具:import 'dart:convert';2. JSON解析(解码)将JSON字符串转换为Dart的Map或List。这通常在获取API响应数据时使用:String jsonString = '{"name": "John", "age": 30}';Map<String, dynamic> user = jsonDecode(jsonString);print(user['name']); // 输出 Johnprint(user['age']); // 输出 30如果JSON是一个数组:String jsonArray = '[{"name": "John"}, {"name": "Jane"}]';List<dynamic> users = jsonDecode(jsonArray);print(users[0]['name']); // 输出 Johnprint(users[1]['name']); // 输出 Jane3. JSON编码(序列化)将Dart的Map或List转换为JSON字符串。这通常用于发送数据到服务器:Map<String, dynamic> data = {'name': 'John', 'age': 30};String json = jsonEncode(data);print(json); // 输出 {"name":"John","age":30}4. 处理复杂的JSON结构对于更复杂的JSON结构,你可能需要创建模型(model)类来表示数据,然后使用json_serializable或类似的库来简化序列化和反序列化的过程。例如,创建一个User类,并使用json_serializable来自动生成相关的JSON处理代码:import 'package:json_annotation/json_annotation.dart';part 'user.g.dart';@JsonSerializable()class User { String name; int age; User({required this.name, required this.age}); factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json); Map<String, dynamic> toJson() => _$UserToJson(this);}然后,在你的build_runner中运行构建命令来生成自动代码。这些步骤概述了在Dart中处理JSON数据的基本方法。使用内置的函数和库可以非常高效地处理Web开发中常见的数据交换格式。
前端阅读 02月7日 11:27

Dart 如何导入外部库或包?

在Dart中导入外部库或包,通常有几个步骤需要遵循:添加依赖:在项目的pubspec.yaml文件中,你需要在dependencies部分添加你想要使用的库。例如,如果你想使用http库来进行网络请求,你可以这样添加: dependencies: http: ^0.13.3这里的^0.13.3表示你希望使用这个库的0.13.3版本或者更新的版本,但是不会使用一个新的主版本,这有助于避免引入重大更改。获取包:保存pubspec.yaml文件后,你需要运行flutter pub get(如果是Flutter项目)或者pub get(如果只是Dart项目)。这个命令会根据pubspec.yaml文件中列出的依赖关系,从Dart的包管理仓库下载和安装库。导入库:在Dart文件中,你可以使用import语句来导入你需要的库。对于http库,导入的代码可能看起来像这样: import 'package:http/http.dart' as http;这里使用as http是为了给导入的库一个别名,这样可以在代码中更方便地引用库中的功能。遵循这些步骤,你就可以在Dart项目中添加并使用外部库了。
前端阅读 02月7日 11:26

执行 Dart 代码的方法有哪些?

在Dart中执行程序主要有以下几种方法:直接运行:通过在命令行中使用 Dart 命令直接运行源代码文件。例如,执行 dart run filename.dart 可以直接运行文件。使用DartPad:DartPad 是一个在线的 Dart 编辑器和执行环境,允许用户编写和运行 Dart 代码,无需安装任何东西。编译为JavaScript:Dart 程序可以通过 Dart2js 工具编译成 JavaScript,这样就可以在网页浏览器中运行。使用命令如 dart compile js filename.dart 进行编译。Dart虚拟机:通过 Dart 虚拟机 (Dart VM) 来运行 Dart 脚本。这在开发阶段特别有用,因为它支持热重载,即代码改动后可立即看到运行结果,无需重新启动应用。编译为本地代码:Dart 也可以被编译成 AOT (Ahead Of Time) 编译的本地代码,这主要用于生产环境,以提升应用的启动时间和性能。使用命令如 dart compile exe filename.dart 生成可执行文件。Flutter应用:如果是开发 Flutter 应用,Dart 代码将被编译并嵌入到 Flutter 应用中,通过 Flutter 工具链来构建和运行应用。这些都是执行 Dart 程序的常见方法。
前端阅读 02月7日 11:26

Docker中的“expose”和“publish”有什么区别?

在Docker中,“expose”和“publish”这两个概念经常被提到,它们都与容器的网络端口配置相关,但具体用途和行为却有所不同。Expose:EXPOSE指令用于Dockerfile中,其主要目的是指定容器在运行时监听的端口,用于文档化。它实际上不会自动开放端口到宿主机。使用EXPOSE可以为容器的使用者提供信息,说明容器意图打开哪些端口以供通信。但只是作为一种声明,并不意味着外部可以访问这些端口。Publish:当运行容器时,使用-p或--publish标志来映射和开放容器的端口到宿主机的端口。这样做实际上允许从宿主机或外部网络访问这个端口。例如,使用docker run -p 80:80 nginx,这条命令将容器内的80端口映射到宿主机的80端口上,使得宿主机的80端口可以接受来自外部的请求并转发给容器。总结来说,EXPOSE是在构建镜像时的声明,指明容器打算用哪些端口,而publish是在运行容器时实际执行的端口映射操作,使外部能够访问到容器的指定端口。