面试真实经历某商银行大厂Java问题和答案总结(三)

作者:360_go_php日期:2025/10/15

1. 深拷贝和浅拷贝

​编辑

问:什么是深拷贝和浅拷贝?它们之间有什么区别?

浅拷贝(Shallow Copy)

  • 浅拷贝指的是复制对象的引用,而不是复制对象本身。也就是说,对于对象中的引用类型字段,浅拷贝只是复制了这些字段的引用,而没有复制字段引用的对象本身。
  • 如果原始对象和拷贝对象中有相同的引用类型字段,修改其中一个字段会影响到另一个对象。​编辑

深拷贝(Deep Copy)

  • 深拷贝指的是复制对象本身,并且递归地复制对象中的所有引用类型字段,确保两个对象中的引用类型字段互不影响。
  • 修改原始对象的引用类型字段,不会影响到深拷贝对象的相应字段。

区别

  • 浅拷贝只复制基本数据类型和对象的引用地址,引用类型字段指向同一内存地址。
  • 深拷贝会为所有字段分配新的内存空间,确保两个对象互不影响。​编辑
1// 浅拷贝示例  
2class Person implements Cloneable {  
3    String name;  
4    int age;
5
6    public Person(String name, int age) {  
7        this.name = name;  
8        this.age = age;  
9    }
10
11    @Override  
12    protected Object clone() throws CloneNotSupportedException {  
13        return super.clone();  
14    }  
15}
16
17// 深拷贝示例  
18class Address {  
19    String city;
20
21    public Address(String city) {  
22        this.city = city;  
23    }  
24}
25
26class PersonWithAddress implements Cloneable {  
27    String name;  
28    int age;  
29    Address address;
30
31    public PersonWithAddress(String name, int age, Address address) {  
32        this.name = name;  
33        this.age = age;  
34        this.address = address;  
35    }
36
37    @Override  
38    protected Object clone() throws CloneNotSupportedException {  
39        PersonWithAddress person = (PersonWithAddress) super.clone();  
40        person.address = new Address(this.address.city); // 创建新的Address对象,实现深拷贝  
41        return person;  
42    }  
43}  
44

2. HashMap

问:HashMap 的底层是如何实现的?​编辑

:HashMap 底层是基于数组和链表(或红黑树,在 JDK 1.8 以后)实现的。它的实现分为以下几部分:

  1. 数组:HashMap 使用一个数组来存储键值对。数组的每个位置是一个桶(bucket),每个桶存储链表或红黑树(取决于元素的数量)。
  2. 哈希冲突:如果两个键的哈希值相同,会发生哈希冲突。此时,HashMap 会在相应桶中使用链表或者红黑树来解决冲突。​编辑
  3. 扩容机制:当 HashMap 中的元素个数达到负载因子与容量的乘积时,会触发扩容。扩容时,HashMap 会将容量扩大为原来的两倍,并重新计算所有元素的哈希值,确保哈希桶的均匀分布。

问:HashMap 的线程不安全性是如何产生的?如何解决?

:HashMap 在多线程环境下线程不安全。主要原因有以下几点:

  1. 在扩容时,数组和链表的操作是非原子的:在扩容时,可能会发生线程之间的竞争条件,导致数据丢失或覆盖。
  2. 数据读写不加锁:在多线程环境下,一个线程可能在另一个线程更新值的同时读取到过期或不一致的值。

解决方法

  • 使用 Collections.synchronizedMap 方法来包装 HashMap,使其线程安全。
  • 使用 ConcurrentHashMap 替代 HashMap,它提供了更高效的并发支持。
1// 使用 ConcurrentHashMap 替代 HashMap  
2ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();  
3concurrentMap.put("key", 1);  
4

问:HashMap 和 Hashtable 有什么区别?

  1. 线程安全Hashtable 是线程安全的,而 HashMap 不是。Hashtable 的所有方法都使用 synchronized 关键字进行同步。
  2. 性能:由于 Hashtable 存在同步机制,它在单线程环境下性能较差,而 HashMap 更高效。
  3. null 键和值HashMap 允许一个 null 键和多个 null 值,而 Hashtable 不允许 null 键和值。
  4. 扩容Hashtable 默认的初始容量为 11,增长因子为 2;而 HashMap 默认的初始容量为 16,负载因子为 0.75。

问:HashMap 如何处理哈希冲突?

:当多个键具有相同的哈希值时,HashMap 会使用链表或者红黑树来处理哈希冲突。

  1. 链表法:哈希冲突时,HashMap 会将这些冲突的键值对以链表的形式挂在同一个桶中。
  2. 红黑树:当某个桶中的元素超过 8 个时,HashMap 会将链表转换为红黑树,以提高查询效率。

问:HashMap 中 get 方法的时间复杂度是多少?

:在最优情况下,get 方法的时间复杂度是 O(1),即常数时间。哈希冲突的情况下,如果链表很长或者转为红黑树,时间复杂度分别是 O(n) 和 O(log n)。

问:HashMap 中的 hashCode 方法和 equals 方法是如何影响哈希表的性能的?

HashMap 使用 hashCode 方法来计算键的哈希值,并根据哈希值将键值对放入不同的桶中。如果两个键具有相同的 hashCode 值,它们就会被放入同一个桶中,这可能会导致哈希冲突。equals 方法用于比较两个键是否相等,当哈希值相同时,equals 方法决定它们是否属于同一个键。

为了保证 HashMap 的性能,hashCode 方法应尽可能均匀地分布哈希值,减少哈希冲突。equals 方法应准确地判断对象是否相等。

3. ConcurrentHashMap

问:ConcurrentHashMap 是如何保证线程安全的?

ConcurrentHashMap 通过以下几种方式保证线程安全:

  1. 分段锁:将哈希表分成多个段(Segment),每个段可以独立加锁,这样多个线程可以同时操作不同的段,提高并发性能。
  2. CAS 操作ConcurrentHashMap 使用 CAS(Compare-And-Swap)操作来保证对单个元素的更新是原子的,避免了使用传统的锁机制。

问:ConcurrentHashMap 中的 putIfAbsent 方法是如何工作的?

putIfAbsent 方法会将指定的键值对放入 ConcurrentHashMap 中,只有在该键不存在的情况下才会插入。如果键已经存在,它会返回该键的当前值,而不会进行任何插入操作。这是原子操作,可以确保并发环境下不会插入重复的键值对。

1ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();  
2map.putIfAbsent("key", 1);  
3

4. HashSet

问:HashSetHashMap 有什么区别?

HashSet 是基于 HashMap 实现的,HashSet 中的元素就是 HashMap 中的键,且没有值。HashSet 保证元素的唯一性,而 HashMap 保证键的唯一性。

5. Java 命名规则

问:Java 的命名规则是什么?

  1. 类名:类名应使用驼峰命名法(PascalCase),首字母大写,且每个单词的首字母大写,如 MyClass
  2. 方法名:方法名应使用驼峰命名法(camelCase),首字母小写,如 calculateTotal().
  3. 变量名:变量名应使用驼峰命名法(camelCase),首字母小写,如 totalAmount
  4. 常量名:常量名应使用全大写字母,并且单词之间用下划线分隔,如 MAX_VALUE

6. Java 异常

Java 中的异常用于处理程序运行时出现的错误情况。通过异常机制,可以捕捉错误,避免程序因异常崩溃,并且能够提供更好的错误处理和调试机制。Java 异常分为两大类:检查异常(Checked Exception)和非检查异常(Unchecked Exception)

1. 异常的分类

  • 检查异常(Checked Exception)
    - 编译时异常,程序在编译时会检查到这些异常,必须显式地处理。
    - 例如:IOExceptionSQLException
    - 必须在方法中声明抛出异常,或者在代码中捕获异常。
  • 非检查异常(Unchecked Exception)
    - 运行时异常,不要求程序员在编译时进行处理,通常表示程序逻辑错误。
    - 例如:NullPointerExceptionArrayIndexOutOfBoundsExceptionArithmeticException
    - 这些异常可以在运行时发生,但不要求显式地捕获或声明抛出。

2. 异常的层次结构

异常类 Throwable 是所有异常的父类,分为两类:

  • Error:表示 JVM 自身的错误或资源耗尽问题,如 OutOfMemoryError
  • Exception:用于表示程序中的异常,进一步分为:
    - Checked Exception:编译时异常。
    - Unchecked Exception:运行时异常。

3. 常见异常类型

  • NullPointerException:尝试访问一个为 null 的对象或数组。
    java   String str = null;   System.out.println(str.length());  // 会抛出 NullPointerException  
  • ArrayIndexOutOfBoundsException:访问数组时使用了非法的索引。
    java   int[] arr = new int[5];   System.out.println(arr[10]);  // 会抛出 ArrayIndexOutOfBoundsException  
  • ArithmeticException:算术运算错误,比如除以零。
    java   int result = 10 / 0;  // 会抛出 ArithmeticException  
  • ClassNotFoundException:当应用程序试图通过字符串名称加载类时,指定的类无法找到。
    java   try {       Class.forName("com.example.MyClass");   } catch (ClassNotFoundException e) {       e.printStackTrace();   }  
  • IOException:处理输入输出时的异常。
    java   try {       FileReader file = new FileReader("nonexistentfile.txt");   } catch (IOException e) {       e.printStackTrace();   }  
  • SQLException:数据库操作异常。
    java   try {       Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/db", "user", "pass");   } catch (SQLException e) {       e.printStackTrace();   }  

4. 如何处理异常

Java 提供了 try-catch 语句来捕获并处理异常。处理异常时,可以捕获多个异常类型,并在代码中提供适当的处理逻辑。

try-catch

  • try 块:用来包围可能抛出异常的代码。
  • catch 块:捕获并处理异常。
1try {  
2    int[] arr = new int[5];  
3    System.out.println(arr[10]);  // 可能会抛出 ArrayIndexOutOfBoundsException  
4} catch (ArrayIndexOutOfBoundsException e) {  
5    System.out.println("Array index out of bounds!");  
6} catch (Exception e) {  
7    System.out.println("Some other exception occurred.");  
8}  
9

finallyfinally 块中的代码总是会执行,通常用于清理工作,如关闭文件或数据库连接等,不论是否有异常发生。

1try {  
2    // 执行可能抛出异常的代码  
3} catch (Exception e) {  
4    // 异常处理  
5} finally {  
6    // 清理工作,例如关闭文件流  
7}  
8

5. 抛出异常

有时候你需要在方法中主动抛出异常,使用 throw 关键字:

1if (age < 0) {  
2    throw new IllegalArgumentException("Age cannot be negative");  
3}  
4

当你需要声明方法可能抛出异常时,使用 throws 关键字:

1public void readFile(String filename) throws IOException {  
2    FileReader file = new FileReader(filename);  
3    // 处理文件操作  
4}  
5

6. 自定义异常

可以通过继承 ExceptionRuntimeException 类来创建自己的异常类。例如,创建一个自定义的 InvalidAgeException

1public class InvalidAgeException extends Exception {  
2    public InvalidAgeException(String message) {  
3        super(message);  
4    }  
5}  
6

使用自定义异常:

1public class AgeValidator {  
2    public void validate(int age) throws InvalidAgeException {  
3        if (age < 0) {  
4            throw new InvalidAgeException("Age cannot be negative");  
5        }  
6    }  
7}
8
9public class Test {  
10    public static void main(String[] args) {  
11        AgeValidator validator = new AgeValidator();  
12        try {  
13            validator.validate(-1);  
14        } catch (InvalidAgeException e) {  
15            System.out.println(e.getMessage());  
16        }  
17    }  
18}  
19

7. 异常链

异常链是指一个异常可以在被捕获时抛出另一个异常,从而将原始异常信息传递下去。可以通过 Throwable 类的 initCause 方法来实现:

1try {  
2    // 模拟一个抛出异常的操作  
3    throw new NullPointerException("Null pointer exception");  
4} catch (NullPointerException e) {  
5    throw new RuntimeException("Runtime exception caused by NullPointerException", e);  
6}  
7

总结

  • 检查异常:必须处理或声明,不处理会导致编译错误。
  • 非检查异常:通常表示程序中的逻辑错误,编译时不会强制要求处理。
  • 使用 try-catch 处理异常,确保程序能够优雅地处理错误。
  • finally 块用于执行清理工作。
  • 可以使用 throwthrows 来手动抛出和声明异常。


面试真实经历某商银行大厂Java问题和答案总结(三)》 是转载文章,点击查看原文


相关推荐


Swift 基础语法全景(一):从变量到类型安全
unravel20252025/10/14

常量与变量:let vs var 声明语法 // 常量:一次赋值,终身不变 let maximumLoginAttempts = 10 // 最大尝试次数,业务上不允许修改 // 变量:可反复写入 var currentAttempt = 0 // 当前尝试次数,失败+1 延迟赋值 只要「第一次读取前」完成初始化即可,不必一行写完。 var randomGenerator = SystemRandomNumberGenerator() let


Linux挂载NTFS分区指南
依赖倒不置2025/10/12

在使用Windows和Linux双系统的电脑上,通常我们可能也需要在Linux上访问NTFS分区。虽然Linux用户一般来说不一定会有分区的习惯,但是在双系统情况下,两个系统复用NTFS分区也是很常见的。 今天,就来讨论双系统复用分区的场景下,如何正确地在Linux挂载NTFS分区。 1,Linux的NTFS驱动 事实上,老版本的Linux内核并不原生支持读取NTFS分区,尤其是NTFS是Windows系统专有的文件系统,而不是开放的。因此很长一段时间,都是使用ntfs-3g这个开源驱动实现在L


老题新解|十进制转二进制
程序员莫小特2025/10/10

《信息学奥赛一本通》第160题:十进制转二进制 题目描述 给定一个十进制整数 n n n,请将其转换为对应的二进制表示,并输出。 输入格式 输入包含一行,一个整数 n


单机已达上限?PerfTest 分布式压测登场,轻松模拟百万用户洪峰
Go有引力2025/10/9

前言 在前一篇文章中,我们详细介绍了 perftest 的单机压测能力,展示了它如何通过极简的命令行实现对 HTTP/1.1、HTTP/2、HTTP/3 与 WebSocket 的高性能测试。然而,当业务系统庞大、服务部署分布式、网络链路复杂时,单机的压测能力显然无法满足真实生产环境的模拟需求。 幸运的是,perftest 不止于单机。它同样支持 分布式集群压测,通过 Collector + Agent 的架构,让你轻松在多台机器上同时发起测试,实现百万乃至千万级并发的性能评估。 为什么选择分


Webpack实战笔记:从自动构建到本地服务器搭建的完整流程
XiangCoder2025/10/7

作为前端开发者,Webpack 是绕不开的构建工具。最近系统学习了 Webpack 的自动构建和服务器搭建,整理了一套从基础到实战的操作笔记,包含具体案例和踩坑经验,适合新手跟着实操。 前言:为什么需要 Webpack 构建工具? 刚开始写前端项目时,我们习惯在 index.html 里手动引入各种 js、css 文件。但随着项目变大,会遇到两个核心问题: 资源越来越多,手动管理引入路径容易出错(尤其是加哈希值优化缓存时); 开发时需要频繁刷新页面看效果,效率太低。 Web


Android系统模块编译调试与Ninja使用指南
龙之叶2025/10/6

模块编译调试方法 (此处举例framework、installd、SystemUI等模块的编译调试,其他类似) 1. Framework模块编译 Android系统代码的framework目录内,一共有3个模块单独编译:framework、services、framework-res.apk。 注意:偶尔会有改了代码但没检测到,编译结束后产物并未更新的情况,所以可以考虑先剪切掉原先的产物或者检查产物更新时间确保已经重新编译。 步骤: 完成根目录下 source build/envsetup.sh


精选 4 款开源免费、美观实用的 MAUI UI 组件库,助力轻松构建美观且功能丰富的应用程序!
追逐时光者2025/10/5

前言 一套优秀的 UI 组件库不仅能大幅提升开发效率,还能显著增强应用的视觉体验与交互质感。今天大姚给大家分享 4 款开源免费、美观实用的 MAUI UI 组件库,助你轻松打造专业级的跨平台应用程序。 .NET MAUI介绍 .NET 多平台应用 UI (.NET MAUI) 是一个跨平台框架,用于使用 C# 和 XAML 创建本机移动和桌面应用。使用 .NET MAUI,可从单个共享代码库开发可在 Android、iOS、macOS 和 Windows 上运行的应用。 .NET开源免费的跨平


医院病人信息管理系统 Web 版开发实战(一)
FPGA+护理+人工智能2025/10/3

医院病人信息管理系统Web版开发实战 文章目录 医院病人信息管理系统Web版开发实战一、概述二、项目结构搭建2.1 Flask 项目结构原理2.2 创建项目文件夹2.3 初始化 Flask 应用 三、路由设计3.1 Flask 路由机制原理3.2 定义核心路由3.3 数据存储函数 四、模板开发4.1 Jinja2 模板引擎原理4.2 基础模板设计4.3 首页模板(index.html)4.4 添加病人模板(add.html)4.5 详情和编辑模板 未完待续


Java-Spring入门指南(十一)代理模式与Spring AOP实战
珹洺2025/10/2

Java-Spring入门指南(十一)代理模式与Spring AOP实战 前言一、代理模式1.1 静态代理步骤1:定义租房接口(抽象行为)步骤2:实现房东类(真实对象)步骤3:编写中介类(代理对象)步骤4:测试静态代理静态代理的优缺点 1.2 动态代理 二、AOP是什么?2.1 AOP是什么?2.2 AOP有什么用?2.3 AOP的使用场景是什么?2.4 AOP的核心特点是什么? 三、AOP在Spring中的应用3.1 准备依赖3.2 配置Spring的AOP命名空间3.3 定义


(14)ASP.NET Core2.2 中的日志记录
老葱头蒸鸡2025/10/2

1.前言 ASP.NET Core支持适用于各种内置和第三方日志记录提供应用程序的日志记录API。本文介绍了如何将日志记录API与内置提供应用程序一起使用。 2.添加日志提供程序 日志记录提供应用程序显示或存储日志。例如,控制台提供应用程序在控制台上显示日志,Azure Application Insights提供应用程序将这些日志存储在Azure Application Insights中。要添加提供应用程序,请在Program.cs中调用提供程序的Add{provider name}扩

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0