一、Java基础
1.1 面向对象三大特性
封装:将数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。通过private、protected、public等访问修饰符实现数据隐藏,提高代码的安全性和可维护性。
继承:从已有类得到继承信息创建新类的过程。子类继承父类的属性和方法,可以实现代码复用。Java支持单继承,通过extends关键字实现。
多态:允许不同子类型的对象对同一消息作出不同的响应。实现方式包括方法重写(Override)和接口实现。多态提高了代码的扩展性和灵活性。
1.2 重载与重写区别
重载:发生在本类中,方法名相同但参数列表不同(参数类型、个数、顺序不同),与返回值类型无关。重载是编译时多态。
重写:发生在父类与子类之间,方法名相同且参数列表必须相同,返回值类型相同或为子类,访问权限不能比父类更低。重写是运行时多态。
1.3 接口与抽象类区别
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 继承方式 | 单继承 | 多继承接口 |
| 构造器 | 可以有 | 不能有 |
| 成员变量 | 可以有普通成员变量 | 只能是常量 |
| 方法修饰符 | public、protected、default | 只能是public |
| 方法实现 | 可以有抽象方法和具体方法 | JDK8前只能是抽象方法 |
1.4 final、finally、finalize
final:修饰类、变量、方法。修饰类表示不可继承,修饰变量表示常量,修饰方法表示不可重写。
finally:异常处理中的代码块,无论是否发生异常都会执行,通常用于释放资源。
finalize:Object类中的方法,垃圾回收器在回收对象前调用,用于清理资源。不推荐使用,因为执行时机不确定。
1.5 ==和equals区别
==:比较基本数据类型时比较值,比较引用类型时比较内存地址。
equals:Object类中默认使用==比较,可以重写用于比较对象内容。重写equals时必须重写hashCode方法,以保证两个相等的对象具有相同的hashCode值。
1.6 String、StringBuilder、StringBuffer
String:不可变,线程安全,适合少量字符串操作。
StringBuilder:可变,非线程安全,单线程下性能高。
StringBuffer:可变,线程安全,适合多线程场景。
1.7 自动拆装箱
装箱:将基本类型转换为包装类对象,如int转Integer。
拆箱:将包装类对象转换为基本类型,如Integer转int。
实现原理是javac编译器的语法糖,底层通过Integer.valueOf()和Integer.intValue()方法实现。自动拆装箱会带来性能开销,在循环中应避免使用。
二、集合框架
2.1 ArrayList和LinkedList区别
ArrayList:基于动态数组实现,随机访问快(O(1)),增删慢(需要移动元素,O(n)),适合查多改少的场景。
LinkedList:基于双向链表实现,增删快(O(1)),随机访问慢(需要遍历,O(n)),适合频繁插入删除的场景。
2.2 HashMap底层原理
JDK8前:数组 + 链表。通过key的hashCode计算数组下标,解决hash冲突使用链表。
JDK8起:数组 + 链表 + 红黑树。当链表长度超过8且数组长度大于64时,链表转为红黑树,查询性能从O(n)提升到O(logn)。
扩容机制:默认初始容量16,负载因子0.75。当元素数量达到容量*负载因子时,扩容为原来的2倍,重新计算hash分布。
2.3 HashMap和HashTable区别
| 特性 | HashMap | HashTable |
|---|---|---|
| 线程安全 | 非线程安全 | 线程安全(synchronized) |
| 性能 | 高 | 低 |
| 允许null | key和value都允许null | key和value都不允许null |
| 初始容量 | 16 | 11 |
| 扩容 | 2倍 | 2倍+1 |
2.4 ConcurrentHashMap线程安全实现
JDK7:分段锁(Segment),每个Segment继承ReentrantLock,不同Segment可以并发操作。
JDK8:synchronized + CAS + volatile。使用Node数组+链表/红黑树结构,锁粒度更细,只锁住链表头节点。
2.5 集合遍历方式
- Iterator迭代器:支持遍历过程中删除元素
- for-each循环:语法糖,底层使用Iterator
- 普通for循环:适合ArrayList等有索引的集合
- forEach方法:JDK8引入,使用Lambda表达式
三、多线程
3.1 线程创建方式
- 继承Thread类:重写run方法,调用start()启动
- 实现Runnable接口:实现run方法,作为Thread构造参数
- 实现Callable接口:有返回值,通过FutureTask获取结果
- 线程池:Executor框架,推荐使用
3.2 线程状态
- NEW:新建状态,未调用start()
- RUNNABLE:可运行状态,在JVM中执行
- BLOCKED:阻塞状态,等待获取锁
- WAITING:等待状态,无限期等待
- TIMED_WAITING:超时等待状态
- TERMINATED:终止状态
3.3 sleep和wait区别
| 特性 | sleep | wait |
|---|---|---|
| 所属类 | Thread | Object |
| 释放锁 | 不释放 | 释放 |
| 唤醒方式 | 时间到自动唤醒 | notify/notifyAll |
| 使用位置 | 任意位置 | 同步代码块中 |
3.4 synchronized和ReentrantLock
synchronized:JVM层面实现,自动释放锁,不可中断,非公平锁。
ReentrantLock:Java代码实现,需要手动释放锁,可中断,支持公平锁和非公平锁,支持尝试获取锁和超时获取锁。
3.5 volatile关键字
作用:
- 保证可见性:一个线程修改volatile变量,其他线程立即可见
- 禁止指令重排序:通过内存屏障实现
- 不保证原子性:复合操作仍需加锁
应用场景:状态标志位、双重检查锁单例模式。
3.6 线程池核心参数
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize, // 核心线程数
maximumPoolSize, // 最大线程数
keepAliveTime, // 空闲线程存活时间
TimeUnit, // 时间单位
workQueue, // 任务队列
threadFactory, // 线程工厂
rejectionHandler // 拒绝策略
);
拒绝策略:
- AbortPolicy:抛出异常(默认)
- CallerRunsPolicy:由调用线程执行
- DiscardPolicy:直接丢弃
- DiscardOldestPolicy:丢弃队列最老的任务
3.7 死锁产生条件及避免
产生条件:
- 互斥条件:资源独占
- 请求和保持:持有资源并请求其他资源
- 不可剥夺:资源不能被强制剥夺
- 循环等待:形成等待环路
避免方法:
- 按顺序获取锁
- 设置超时时间
- 使用tryLock尝试获取锁
- 避免嵌套锁
四、JVM
4.1 JVM内存结构
程序计数器:线程私有,记录当前线程执行的字节码行号。
虚拟机栈:线程私有,存储局部变量表、操作数栈、动态链接、方法出口等信息。
本地方法栈:为Native方法服务。
堆:线程共享,存放对象实例,是垃圾回收的主要区域。
方法区:线程共享,存储类信息、常量、静态变量、即时编译器编译后的代码。JDK8后称为元空间(Metaspace)。
4.2 垃圾回收算法
标记-清除:标记存活对象,清除未标记对象。产生内存碎片。
复制算法:将内存分为两块,每次使用一块,存活对象复制到另一块。适合新生代。
标记-整理:标记存活对象,向一端移动,清理边界外内存。适合老年代。
分代收集:新生代使用复制算法,老年代使用标记-清除或标记-整理。
4.3 垃圾收集器
Serial:单线程,新生代收集器,适合客户端应用。
ParNew:Serial的多线程版本,配合CMS使用。
Parallel Scavenge:吞吐量优先,新生代收集器。
Serial Old:Serial的老年代版本。
Parallel Old:Parallel Scavenge的老年代版本。
CMS:并发标记清除,低停顿,但会产生内存碎片。
G1:面向服务端,将堆划分为多个Region,可预测停顿时间。
ZGC:JDK11引入,低停顿,支持TB级堆内存。
4.4 类加载过程
- 加载:通过类全限定名获取二进制字节流,将静态存储结构转化为方法区的运行时数据结构,生成Class对象。
- 验证:确保Class文件符合规范,不会危害虚拟机安全。
- 准备:为类变量分配内存并设置初始值(零值)。
- 解析:将符号引用转为直接引用。
- 初始化:执行类构造器<clinit>()方法,为静态变量赋值。
4.5 双亲委派模型
工作原理:类加载器收到加载请求时,先委托父加载器加载,父加载器无法完成时自己加载。
优势:
- 避免类重复加载
- 保护程序安全,防止核心类被篡改
- 保证类的唯一性
打破双亲委派:通过重写loadClass方法,如JDBC、Tomcat等场景。
五、MySQL
5.1 InnoDB和MyISAM区别
| 特性 | InnoDB | MyISAM |
|---|---|---|
| 事务支持 | 支持 | 不支持 |
| 外键支持 | 支持 | 不支持 |
| 锁粒度 | 行级锁 | 表级锁 |
| 崩溃恢复 | 支持 | 不支持 |
| 索引结构 | 聚簇索引 | 非聚簇索引 |
| 全文索引 | 不支持(5.6前) | 支持 |
5.2 索引类型
聚簇索引:索引和数据存储在一起,InnoDB的主键索引就是聚簇索引,叶子节点存储行数据。
非聚簇索引:索引和数据分开存储,MyISAM使用非聚簇索引,叶子节点存储数据地址。
覆盖索引:查询的字段都在索引中,无需回表查询。
联合索引:在多个列上建立索引,遵循最左前缀原则。
5.3 最左前缀原则
联合索引(a, b, c)中,查询条件必须包含最左列a才能使用索引:
- WHERE a = 1 AND b = 2:使用a、b列索引
- WHERE a = 1 AND c = 3:只使用a列索引
- WHERE b = 2 AND c = 3:不使用索引
5.4 索引失效场景
- 对索引列进行运算或函数操作
- 使用不等于(!=、<>)查询
- 使用or连接条件,且or前后条件有未索引列
- 使用like以通配符%开头
- 类型转换导致索引失效
- 联合索引不满足最左前缀原则
5.5 事务特性(ACID)
原子性:事务要么全部成功,要么全部失败。
一致性:事务执行前后数据库处于一致状态。
隔离性:多个事务并发执行互不干扰。
持久性:事务提交后对数据库的修改永久保存。
5.6 事务隔离级别
读未提交:可能发生脏读、不可重复读、幻读。
读已提交:避免脏读,可能发生不可重复读、幻读。
可重复读:避免脏读、不可重复读,可能发生幻读(InnoDB通过MVCC避免幻读)。
串行化:避免所有并发问题,性能最低。
5.7 MVCC实现原理
多版本并发控制:通过版本链和Read View实现。
隐藏字段:DB_TRX_ID(事务ID)、DB_ROLL_PTR(回滚指针)。
Read View:事务开启时生成,决定能看到哪些版本的数据。
版本链:通过DB_ROLL_PTR指向Undo Log中的旧版本数据,形成版本链。
5.8 锁机制
行锁:锁住单行数据,InnoDB通过索引实现行锁。
表锁:锁住整张表,MyISAM使用表锁。
间隙锁:锁住索引记录之间的间隙,防止幻读。
意向锁:表级锁,表示事务想在表中加行锁,用于快速判断锁冲突。
六、Redis
6.1 数据类型及应用场景
String:缓存、计数器、分布式锁。
Hash:存储对象信息,如用户信息。
List:消息队列、最新列表。
Set:共同好友、抽奖。
ZSet:排行榜、延迟队列。
6.2 持久化机制
RDB:定时快照,文件小恢复快,但可能丢失数据。
AOF:记录写命令,数据安全但文件大恢复慢。
混合持久化:RDB + AOF,结合两者优点。
6.3 缓存问题
缓存穿透:查询不存在的数据,解决方案:布隆过滤器、缓存空值。
缓存击穿:热点key过期瞬间大量请求,解决方案:互斥锁、永不过期。
缓存雪崩:大量key同时过期,解决方案:随机过期时间、集群部署。
6.4 内存淘汰策略
- noeviction:不淘汰,写入报错
- allkeys-lru:淘汰最近最少使用的key
- allkeys-random:随机淘汰
- volatile-lru:淘汰设置了过期时间的最近最少使用的key
- volatile-ttl:淘汰即将过期的key
6.5 集群模式
主从复制:一主多从,主节点写,从节点读,实现读写分离。
哨兵模式:监控主从节点,自动故障转移。
Cluster模式:数据分片,每个节点存储部分数据,支持水平扩展。
七、消息队列
7.1 RabbitMQ核心概念
Exchange:接收生产者消息,根据路由规则转发到队列。
Queue:存储消息,消费者从队列获取消息。
Binding:Exchange和Queue的绑定关系。
路由模式:
- Direct:精确匹配routing key
- Topic:模糊匹配routing key
- Fanout:广播到所有绑定队列
- Headers:匹配header属性
7.2 Kafka核心概念
Topic:消息主题,逻辑上的消息分类。
Partition:Topic的分区,每个分区有序,提高并发处理能力。
Producer:消息生产者。
Consumer:消息消费者,通过Consumer Group实现负载均衡。
Broker:Kafka服务器节点。
ZooKeeper:管理Kafka集群元数据。
7.3 消息可靠性保证
生产者确认:消息发送后等待Broker确认。
消息持久化:消息写入磁盘,防止丢失。
消费者确认:消费者处理完消息后手动确认,确保消息不丢失。
事务消息:支持分布式事务。
八、Spring框架
8.1 IOC和DI
IOC(控制反转):将对象的创建和管理交给容器,降低耦合度。
DI(依赖注入):通过构造函数、setter方法或注解注入依赖对象。
8.2 Bean生命周期
- 实例化
- 属性赋值
- 初始化(@PostConstruct)
- 使用
- 销毁(@PreDestroy)
8.3 AOP实现原理
JDK动态代理:基于接口实现,要求被代理类实现接口。
CGLIB动态代理:基于继承实现,通过生成子类实现代理,可以代理没有接口的类。
通知类型:
- @Before:前置通知
- @AfterReturning:返回通知
- @AfterThrowing:异常通知
- @After:最终通知
- @Around:环绕通知
8.4 事务传播机制
- REQUIRED:默认,存在事务则加入,不存在则新建
- REQUIRES_NEW:新建事务,挂起当前事务
- SUPPORTS:存在事务则加入,不存在则以非事务执行
- NOT_SUPPORTED:以非事务执行,挂起当前事务
- MANDATORY:必须存在事务,否则抛出异常
- NEVER:必须不存在事务,否则抛出异常
- NESTED:嵌套事务
8.5 Spring Boot自动配置原理
通过@SpringBootApplication注解触发自动配置:
- 扫描META-INF/spring.factories文件
- 加载AutoConfiguration类
- 根据条件注解(@ConditionalOnClass、@ConditionalOnBean等)决定是否创建Bean
- 通过@EnableAutoConfiguration启用自动配置
九、分布式
9.1 分布式事务解决方案
2PC(两阶段提交):协调者询问参与者是否可以提交,所有参与者同意后提交。存在同步阻塞问题。
TCC(Try-Confirm-Cancel):Try阶段预留资源,Confirm阶段确认提交,Cancel阶段取消操作。需要业务代码实现补偿逻辑。
本地消息表:将分布式事务拆分为本地事务和异步消息,通过消息队列保证最终一致性。
Saga模式:将长事务分解为一系列本地事务,每个事务有对应的补偿操作。
9.2 分布式锁实现
Redis分布式锁:使用setnx命令,设置过期时间防止死锁,通过Lua脚本保证原子性。
ZooKeeper分布式锁:创建临时有序节点,最小节点获取锁,监听前一个节点释放锁。
数据库分布式锁:基于唯一索引或乐观锁实现。
9.3 服务注册与发现
Eureka:AP架构,保证高可用,通过心跳检测服务状态。
Nacos:支持服务注册发现和配置管理,支持CP和AP模式切换。
Consul:CP架构,支持服务发现、健康检查、KV存储。
9.4 服务熔断与降级
Hystrix:通过断路器模式,当服务调用失败达到阈值时熔断,直接返回降级结果。
Sentinel:流量控制、熔断降级、系统负载保护。
降级策略:返回默认值、缓存数据、返回友好提示。
十、设计模式
10.1 单例模式
饿汉式:类加载时创建实例,线程安全但可能浪费资源。
懒汉式:第一次使用时创建实例,需要加锁保证线程安全。
双重检查锁:使用volatile和synchronized保证线程安全。
静态内部类:利用类加载机制保证线程安全,推荐使用。
枚举:最安全的方式,防止反射和序列化破坏单例。
10.2 工厂模式
简单工厂:一个工厂类根据参数创建不同产品,违反开闭原则。
工厂方法:每个产品对应一个工厂类,符合开闭原则。
抽象工厂:创建产品族,适合产品族扩展。
10.3 代理模式
静态代理:代理类和被代理类实现相同接口,代码冗余。
动态代理:运行时生成代理类,JDK动态代理基于接口,CGLIB基于继承。
应用场景:日志记录、事务管理、权限控制、远程调用等。
10.4 观察者模式
定义:一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会收到通知。
实现方式:使用Java内置的Observable和Observer,或自定义观察者接口。
应用场景:事件驱动架构、消息通知、GUI事件处理。
10.5 策略模式
定义:定义一系列算法,封装每个算法,使它们可以相互替换。
优点:避免使用多重条件判断,提高代码的可扩展性和可维护性。
应用场景:支付方式选择、排序算法、数据校验规则等。
十一、网络编程
11.1 TCP和UDP区别
| 特性 | TCP | UDP |
|---|---|---|
| 连接方式 | 面向连接 | 无连接 |
| 可靠性 | 可靠,不丢包不重复 | 不可靠,可能丢包 |
| 传输方式 | 字节流 | 数据报 |
| 速度 | 慢 | 快 |
| 应用场景 | 文件传输、网页浏览 | 视频直播、DNS |
11.2 TCP三次握手
- 客户端发送SYN=1,seq=x
- 服务端发送SYN=1,ACK=1,seq=y,ack=x+1
- 客户端发送ACK=1,seq=x+1,ack=y+1
为什么三次握手:防止已失效的连接请求报文段突然传送到服务端,导致错误连接。
11.3 TCP四次挥手
- 客户端发送FIN=1,seq=u
- 服务端发送ACK=1,seq=v,ack=u+1
- 服务端发送FIN=1,ACK=1,seq=w,ack=u+1
- 客户端发送ACK=1,seq=u+1,ack=w+1
TIME_WAIT状态:等待2MSL时间,确保最后一个ACK报文到达,防止旧连接的数据包影响新连接。
11.4 HTTP和HTTPS区别
| 特性 | HTTP | HTTPS |
|---|---|---|
| 协议 | 应用层协议 | HTTP + SSL/TLS |
| 端口 | 80 | 443 |
| 安全性 | 明文传输 | 加密传输 |
| 证书 | 不需要 | 需要CA证书 |
| 性能 | 高 | 低(加密解密开销) |
11.5 HTTP状态码
- 200:请求成功
- 301:永久重定向
- 302:临时重定向
- 400:客户端错误
- 401:未授权
- 403:禁止访问
- 404:资源不存在
- 500:服务器内部错误
- 503:服务不可用
十二、算法与数据结构
12.1 排序算法
快速排序:分治思想,平均时间复杂度O(nlogn),最坏O(n²),不稳定。
归并排序:分治思想,时间复杂度O(nlogn),稳定,需要额外空间。
堆排序:构建大顶堆,时间复杂度O(nlogn),不稳定。
冒泡排序:相邻元素比较交换,时间复杂度O(n²),稳定。
12.2 查找算法
二分查找:要求有序数组,时间复杂度O(logn)。
哈希查找:通过哈希函数定位,平均时间复杂度O(1),需要处理哈希冲突。
12.3 链表操作
反转链表:使用三个指针prev、curr、next,逐个节点反转。
检测环:快慢指针,快指针每次走两步,慢指针每次走一步,相遇则有环。
合并有序链表:双指针比较,将较小节点加入新链表。
12.4 树操作
二叉树遍历:
- 前序遍历:根左右
- 中序遍历:左根右
- 后序遍历:左右根
- 层序遍历:使用队列
二叉搜索树:左子树所有节点值小于根节点,右子树所有节点值大于根节点。
平衡二叉树:AVL树、红黑树,保证树高度平衡,提高查询效率。
12.5 动态规划
核心思想:将问题分解为子问题,保存子问题结果避免重复计算。
解题步骤:
- 定义dp数组含义
- 确定递推公式
- 初始化dp数组
- 确定遍历顺序
- 举例推导dp数组
经典问题:斐波那契数列、爬楼梯、背包问题、最长公共子序列、编辑距离等。
总结
阿里Java开发岗面试涵盖的知识点非常广泛,从Java基础到分布式系统,从数据库到中间件,从设计模式到算法数据结构。准备面试时不仅要掌握理论知识,更要结合实际项目经验,理解技术原理和设计思想。建议按照知识体系系统复习,多刷题多总结,提高解决问题的能力和代码实现能力。
若内容若侵犯到您的权益,请发送邮件至:platform_service@jienda.com我们将第一时间处理!
所有资源仅限于参考和学习,版权归JienDa作者所有,更多请访问JienDa首页。





