最近我去了一家领先的IT服务公司的Java软件架构师职位的面试,这里分享一下面试中的题目。
你知道ClassNotFoundException和NoClassDefFoundError有什么不同吗?
当你尝试在运行时使用 Class.forName()
或 loadClass()
方法加载类时,会出现 ClassNotFoundException
。如果请求的类未在类路径中找到,就会抛出此异常。如果类路径中没有相应的 JAR 文件,也会抛出此异常。这是一个已检查的异常(checked exception)。
找不到类的错误发生在程序编译成功,但在运行时找不到该类的情况下。编译项目之后,如果你删除了一个 .class 文件,你就会遇到这个错误。
2. 在 Java 里有哪些不同的方法可以创建线程的方法?
有多种方式可以创建线程——使用 Runnable 或 Callable 接口,通过继承 Thread 类的方式来创建线程,或者使用 Executor 框架/线程池。面试官进一步询问是否有其他方法来创建线程?答案是可以通过响应式编程来创建线程。我们可以使用 Spring Web Flux 库来实现响应式代码。如果你还有其他方法,请在评论区分享。
3. 解释一下有哪些不同的加密类型?
基本上有两种类型的加密。对称加密——使用单一密钥进行加密和解密。对称加密通常用于可以在发送方和接收方之间安全共享密钥的情况。它通常用于加密大量数据或静态数据。常见的对称加密方法包括AES、DES等。
不对称加密:使用两个不同的密钥——私钥和公钥。用公钥加密的数据只能用对应的私钥解密。一种常见的不对称加密算法是RSA。
4. ConcurrentHashMap 和 HashMap 有什么区别?
ConcurrentHashMap 将映射分成多个段,并且在写入时只锁定特定的段。因此,这允许多个线程同时写入不同的部分,从而提升了并发性。
在 ConcurrentHashMap 中的读取操作是非阻塞的并且不需要锁。这意味着多个线程可以安全地同时读取该映射,无需等待锁,这在读取操作占主导地位的情况下显著提高了性能。volatile 机制确保了内存的即时可见性,也就是说当一个线程写入一个值时,该值会立即对其他所有线程可见。因此,读取操作总是能够看到最新的值。
5. 当你在浏览器中打开一个网站时,请求会经历哪些过程?请一步一步解释。
我请你看看网上的不同博客。
当你在浏览器输入网址时发生了什么?例如,当你输入类似 "https://www.linkedin.com" 的网址时。简单说一下:
URL 解析
DNS 解析 — 从域名获取服务器地址
TLS 握手(如果使用 HTTPS)→ 它会加密数据
发送 HTTP 请求
服务器处理请求 → 可能涉及应用负载均衡器或网关
接收 HTTP 响应
解析并显示 HTML、CSS 和 JavaScript
加载额外资源(CSS、JS、图片)
显示最终的网页给用户。
6. 2PC 和 Saga模式之间有什么区别?
2PC 用于即时事务处理。你可以无缝地提交或回滚整个事务。当所有参与者都同意时,事务就会被提交;如果有任何一个不同意,事务就会被回滚。这适用于涉及服务较少的场景。由于存在单点故障,它的容错性较差,容易发生故障。
相反,SAGA 模式可以用于跨越多个不同服务的长运行事务。在这里,每个动作都有一个补偿动作。例如——创建订单,补偿动作是——撤销订单。相比两阶段提交(2PC),它更可扩展且容错性更好,因为它不需要全局锁来协调。例如,存在三个服务:订单、库存和支付。如果支付请求失败,它将相应地取消库存和订单。它将事务拆分为多个步骤。
你可以看看https://medium.com/javarevisited/difference-between-saga-pattern-and-2-phase-commit-in-microservices-e1d814e12a5a了解更多详情。
7 什么是 CQRS 模式?
CQRS(命令查询责任隔离)是一种设计模式,用于将系统的读和写操作分开。它将处理命令(会改变应用程序状态的动作)的责任和查询(仅用于检索数据而不做修改的操作)的责任分开。
这完全取决于需求,非常主观。例如,将在命令服务端发生一些事件,比如创建产品、更新产品数量、更新产品价格和删除产品。这些事件将发布到Kafka主题。查询服务会消费这些事件,并且有自己的存储(可能是Elastic Search),以业务需求的形式存储数据。由于它是基于事件的,因此可以回溯时间并查看随着时间所做的更改。这也有助于轻松地回滚更改。请参阅这篇文章以获取更多详细信息。
CQRS 设计模式在微服务架构中本文将探讨微服务架构中的 CQRS 设计模式…medium.com8. 分布式系统中都用到了哪些设计模式?
服务发现与注册,负载均衡,客户端侧负载均衡,熔断器,API网关,同步通信(REST),异步通信(例如Kafka和ActiveMQ这样的消息系统),流量控制。你应该熟悉这些模式,以便能够回答相关问题。
9. 我有一个员工类,里面包含200多个字段。在序列化这对象时,我只想序列化其中的10个字段。有什么好的方法吗?
使用 Externalizable(Externalizable 接口)提供了更多的控制权。我们可以编写序列化的具体逻辑。在这里,我们只需要序列化所需的 10 个字段即可。
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
public class Employee implements Externalizable {
// 50个字段(为了简单起见,我们只显示6个)
private String name;
private int age;
private String department;
private String address;
private double salary;
private String nonSerializedField; // 不会被序列化的字段
// 构造函数
public Employee() {
// Externalizable 所需的无参数构造函数
}
public Employee(String name, int age, String department, String address, double salary, String nonSerializedField) {
this.name = name;
this.age = age;
this.department = department;
this.address = address;
this.salary = salary;
this.nonSerializedField = nonSerializedField;
}
// Getter 和 Setter 方法
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getDepartment() {
return department;
}
public String getAddress() {
return address;
}
public double getSalary() {
return salary;
}
public String getNonSerializedField() {
return nonSerializedField;
}
// Externalizable 方法
@Override
public void writeExternal(ObjectOutput out) throws IOException {
// 手动写入5个选择的字段
out.writeObject(name);
out.writeInt(age);
out.writeObject(department);
out.writeObject(address);
out.writeDouble(salary);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
// 手动读取5个选择的字段
name = (String) in.readObject();
age = in.readInt();
department = (String) in.readObject();
address = (String) in.readObject();
salary = in.readDouble();
}
@Override
public String toString() {
return "雇员{" +
"name='" + name + '\'' +
", age=" + age +
", department='" + department + '\'' +
", address='" + address + '\'' +
", salary=" + salary +
", 未序列化字段='" + nonSerializedField + '\'' +
'}';
}
}
10. 在实施灾难恢复计划时应该考虑哪些关键点?
这个问题非常主观。你可以在网上找到很多相关文章。这里只提供一个简单的概述。
决定一下关键指标吧。
- 恢复点目标 (RPO):定义了你能承担的数据丢失量。它指的是从最近一次数据备份点到系统发生故障的时间点之间最大的数据丢失时间间隔(例如,如果 RPO 为 4 小时,则表示你能接受 4 小时内数据的丢失)。
- 恢复时间目标 (RTO):定义了在故障后需要多快恢复运行。例如,如果 RTO 为 2 小时,则系统必须在 2 小时内恢复运行。
定期对数据库进行备份和快照。使用多个副本。不同的云服务商提供类似的数据库管理服务。
将您的系统和数据分散到不同的地理区域,以确保即使某个区域遭遇自然灾害或类似事件,另一个区域的服务依然可以使用。
识别可能出现的重大故障,并测试系统是否能应对这样的故障。
故障切换机制:
故障转移确保当一个系统或数据中心发生故障时,另一个系统就会接手,以减少最少的停机时间。
- 冷切换:备用系统平时保持离线状态,仅在主系统出现故障时才会启动。这个过程需要时间,因为服务和系统需要手动逐个启动。
- 暖切换:备用系统运行但仅启用少量必需的服务,并准备在一些手动干预下迅速接管。
- 热切换:备用系统与主系统并行运行。切换过程自动发生且几乎瞬间完成,无需人工干预。
- 适用场景:例如,主-主和主-从数据库复制,以及负载均衡器。
自动化与编排:
灾难恢复必须自动化,以减少人为干预,缩短恢复时间。使用如基础设施即代码(IaC)和编排平台之类的工具可以确保您的灾难恢复流程自动启动。
- 基础设施作为代码(IaC):使用如 Terraform、CloudFormation 或 Ansible 这样的 IaC 工具定义基础设施,以便在发生灾难时可以快速重建基础设施。
- 自动故障切换测试:定期使用工具如 AWS 故障注入模拟器来测试灾难恢复流程,以确保故障切换按预期工作。
- 自动恢复剧本:使用 Runbooks(AWS Systems Manager)或自定义脚本来自动化故障切换、恢复及回滚操作。
11. 你将如何为你的项目构建现代的CI/CD流水线?
构建图像:我们可以使用 GitHub 操作在新代码被合并到目标分支时运行构建。此 GitHub 操作将自动启动一个构建任务,用于构建 Docker 图像。
推送图像:从 Github Action 作业将图像推送到您的私有 Docker 仓库,例如 AWS ECR。
环境方面配置:Helm 模板用于根据参数化的输入值动态生成 Kubernetes 配置文件。这使得在不同的环境(如开发、预演和生产)中自定义部署变得容易,而无需每次都手动修改配置文件。Helm 允许你为图表打版本号,从而更轻松地管理变更和回退。
Argo CD:Argo CD 是一个用于使用 GitOps 方法管理 Kubernetes 应用程序的强大工具。它简化了部署流程,提高了可见性,并增强了应用程序更新的可靠性并简化了回滚流程。它还提供了一个友好的用户界面,让你可以看到各种组件的部署状态,甚至还可以查看日志。
12. TCP 和 UDP 之间有什么区别?
TCP 是一种面向连接的协议,确保数据包按顺序传输、错误检查和必要时重传,提供可靠的数据传输服务。虽然TCP速度较慢,但适合需要准确性的场合,如文件传输(FTP)、电子邮件(SMTP)和网页浏览(HTTP)。
UDP(用户数据报协议)是一个无连接的协议,在不建立连接的情况下发送数据包,因此更快,但可靠性较差,因为它不保证数据包的传递或顺序。UDP适用于速度至关重要,且能够容忍少量数据丢失的应用程序,比如视频流媒体、VoIP通话和在线游戏。
我已经分享了面试中遇到的主要技术问题。希望这能帮助你为下回面试做好更好的准备。
共同学习,写下你的评论
评论加载中...
作者其他优质文章