一、Java基础面试专题
1. 如何理解面向对象和面向过程?
面向对象和面向过程是两种基本的编程范式,它们代表了解决问题的不同思维方式和组织代码的不同方法。理解这两种范式的本质和区别,有助于我们在实际开发中灵活运用。
面向过程编程(Procedural Programming)
核心概念:面向过程编程是一种以过程(即函数或方法)为中心的编程思想,强调的是"做什么"和"怎么做"。
主要特点:
- 步骤分解:将问题分解为一系列顺序执行的步骤或函数
- 线性思维:程序执行遵循自上而下的线性流程
- 数据与行为分离:数据结构和操作数据的函数是分离的
- 直接思维:更接近人类解决问题的自然思维方式
- 全局数据:常使用全局变量在函数间共享数据
由于篇幅限制,只能给大家展示小部分内容,
全套面试笔记及答案【点击此处】即可免费获取
2. 接口和抽象类的区别,如何选择?
接口和抽象类是Java中两种重要的抽象机制,它们都用于定义抽象概念和规范,但在本质、语法特性和使用场景上有明显区别。深入理解这些区别,对于软件设计至关重要。
接口(Interface)的基本概念
接口是一种完全抽象的类型,它定义了一组方法签名,但不提供任何实现。实现接口的类必须提供接口中所有方法的具体实现。
3. 如何理解Java中的多态?
多态是面向对象编程的三大核心特性之一(另外两个是封装和继承),是Java语言的重要特性。深入理解多态,对编写灵活、可扩展的代码至关重要。
多态的基本概念
多态(Polymorphism)从字面理解是"多种形态",在面向对象编程中,指的是同一个操作作用于不同的对象,可以有不同的解释和执行结果。具体来说,多态允许不同类的对象对同一消息做出响应,即同一方法调用在不同情况下有不同的行为。
多态的本质:同一种类型的引用可以指向不同的具体对象,从而产生不同的行为。
4. Java中有了基本类型为什么还需要包装类?
Java同时提供基本数据类型和对应的包装类,这一设计看似冗余,实则各有用途。深入理解两者的区别和联系,对于编写高效、正确的Java代码至关重要。
Java基本类型与包装类的概述
基本数据类型:
Java有8种基本数据类型:byte、short、int、long、float、double、char和boolean。
这些类型不是对象,而是直接代表值本身。
包装类:
对应的,Java为每种基本类型提供了包装类:Byte、Short、Integer、Long、Float、Double、Character和Boolean。
这些类位于java.lang包中,是对基本类型的对象封装。
5. 用浮点数表示金额为什么会出错
浮点数精度问题是计算机科学中的经典问题,在处理金融数据时尤为关键。深入理解这一问题,有助于开发可靠的金融系统。
浮点数表示原理
计算机中的浮点数(如Java中的float和double)采用IEEE 754标准表示,由三部分组成:
- 符号位(Sign):表示正负,1位
- 指数位(Exponent):表示数值范围,float为8位,double为11位
- 尾数位(Mantissa):表示精度,float为23位,double为52位
浮点数的值计算公式:(-1)^sign × (1.fraction) × 2^(exponent-bias)
IEEE 754格式:
- float:1位符号 + 8位指数 + 23位尾数 = 32位
- double:1位符号 + 11位指数 + 52位尾数 = 64位
二、集合类面试专题
1. Java中的集合类有哪些?如何分类的?
Java集合框架全景图
Java集合框架主要分为三大类:
- Collection接口:单列数据集合
- Map接口:键值对集合
- 工具类:Collections和Arrays
Collection接口继承体系
Collection
├── List (有序、可重复)
│ ├── ArrayList
│ ├── LinkedList
│ ├── Vector
│ └── Stack
├── Set (无序、不可重复)
│ ├── HashSet
│ │ └── LinkedHashSet
│ ├── SortedSet
│ │ └── TreeSet
│ └── NavigableSet
└── Queue (队列)
├── Deque
│ ├── ArrayDeque
│ └── LinkedList
├── PriorityQueue
└── BlockingQueue (线程安全)
2. 你能说出几种集合的排序方式?
集合排序方式全景图
Java中集合排序主要分为四大类:
- 内置自然排序:实现Comparable接口
- 比较器排序:使用Comparator接口
- Stream API排序:Java 8+的流式排序
- 工具类排序:使用Collections和Arrays工具类
各种排序方式详解
(1) 内置自然排序(Comparable)
实现原理:
- 对象实现
Comparable<T>
接口 - 重写
compareTo()
方法定义自然顺序 - 集合调用
Collections.sort()
或对象的sort()
方法
3. HashMap的数据结构是怎样的?
HashMap整体结构
JDK8后的HashMap结构:
- 数组(哈希桶):主干是Node<K,V>[]数组
- 链表:哈希冲突时形成链表
- 红黑树:链表长度≥8时转换为红黑树
[数组索引] → Node → Node → Node
↓ ↓ ↓
链表 红黑树 链表
核心数据结构
(1) Node节点(链表节点)
static class Node<K,V> implements Map.Entry<K,V> {
final int hash; // 哈希值
final K key; // 键
V value; // 值
Node<K,V> next; // 下一个节点
}
4. HashMap是如何扩容的
扩容触发条件
两个核心条件:
- 容量阈值:当元素数量 > 容量 × 负载因子(默认0.75)
- 默认初始容量16,首次扩容阈值为12(16×0.75)
- 树化检查:当链表长度 ≥ 8且数组长度 < 64时
- 优先选择扩容而非树化,因为扩容可能分散节点
扩容过程详解(JDK8)
(1) 扩容准备阶段
- 计算新容量:当前容量 × 2(保持2的幂次方)
- 计算新阈值:新容量 × 负载因子
- 创建新数组:Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]
5. HashMap的初始容量是多少?
基本概念
- 初始容量(Initial Capacity):
- 定义:HashMap在创建时内部数组(桶)的默认大小。
- 默认值:16(JDK 1.8+)。
- 注意:实际容量一定是2的幂次方(即使构造函数传入非2的幂,也会通过
tableSizeFor()
调整为最近的2的幂)。
设计原理
- 为什么默认是16?
- 平衡性能与内存:较小的初始容量节省内存,但频繁扩容;较大的容量减少扩容但浪费内存。16是经验值,适合大多数场景。
- 哈希冲突优化:容量为2的幂时,
(n-1) & hash
可高效计算索引(等价于取模运算,但更快)。
关键逻辑
- 扩容机制
- 当元素数量超过
容量*负载因子(默认0.75)
时触发扩容(如默认容量16,阈值12)。 - 扩容代价高(重建哈希表),故预判数据量时可指定初始容量。
- 当元素数量超过