[精华集锦] 20+ 互联网大厂Java面试题全面整理总结


每年金三银四都是招聘季,今年虽然有疫情影响,但相信也还是有不少人出来面试。我发现很多候选人,聊起架构、框架侃侃而谈,但一写代码,就暴露真实水平。说白了,还是基本功不够扎实。在我看来,所有计算机基本功中,最核心的一定是数据结构与算法。也因此,所有大厂面试,都必考算法题。


这里,小编特意精心整理了: [精华集锦] 20+ 互联网大厂Java面试题全面整理总结, 分享给大家~~~

核心篇

【BAT 面试题宝库附详尽答案解析】分布式事务实现原理

【BAT 面试题宝库附详尽答案解析】图解分布式一致性协议 Paxos 算法

【BAT 面试题宝库附详尽答案解析】聚合索引和辅助索引有什么区别?

【BAT 面试题宝库附详尽答案解析】数据库事务隔离级别(脏读、幻读、不可重复读)

【BAT 面试题宝库附详尽答案解析】redis的持久化方式RDB和AOF的区别

BAT  大厂Java 面试题集锦之核心篇附参考答案

阿里大厂流出的数百道 Java 经典面试题

面试官问:请介绍一下MySQL数据库的锁机制?

  • 数据结构与算法 (技术基础)

  • 网络通信:TCP/IP, HTTP

  • 操作系统: 文件, shell, CPU, IO, epoll, 非阻塞IO,

  • 进程/线程/协程,锁

  • HashMap, ConcurrentHashMap实现原理, 链表, 红黑树

  • git

  • Maven/Gradle

  • 缓存:各种缓存, redis zset与跳跃表

  • 高并发,高可用,降级,限流,容灾,弱依赖

  • 分布式系统,分布式事务,分布式锁,分布式消息,分布式框架

  • 架构设计, clean code, DDD, API 设计

  • 数据库分库分表, 索引与B+树,SQL调优, WAL, 2阶段提交, CAP与BASE

  • class 字节码,序列化, JVM, 内存模型, GC

  • 多线程与并发编程

  • Java, Kotlin, JavaScript,  编程语言

  • Spring, SpringBoot, Spring MVC, MyBatis

  • OOP ,  FP, 响应式

  • 大数据, 分布式文件存储,MapReduce, Hadoop, Spark, Storm, Flink, AI

  • 微服务,大数据,云计算,AI 算法.

  • .........


  • 首先我们需要明白一个事实,招聘的一个很关键的因素是在给自己找未来的同事,同级别下要找比自己优秀的人,面试是一个双向选择的过程,也是一个将心比心去沟通的过程。

  • 就像我们有的人感觉自己很牛逼,但是拿不到offer,而其他的人菜的一笔,却可以拿到offer,我们称之为玄学offer,遇到这种情况大家也不要感觉到有什么不可描述的心情,一切随缘即可!

大厂 Java 高频面试题

ThreadLocal的原理和实现

ThreadLoal 变量,线程局部变量,同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本。ThreadLocal 变量通常被private static修饰。当一个线程结束时,它所使用的所有 ThreadLocal 相对的实例副本都可被回收。

一个线程内可以存在多个 ThreadLocal 对象,所以其实是 ThreadLocal 内部维护了一个 Map ,这个 Map 不是直接使用的 HashMap ,而是 ThreadLocal 实现的一个叫做 ThreadLocalMap 的静态内部类。而我们使用的 get()、set() 方法其实都是调用了这个ThreadLocalMap类对应的 get()、set() 方法。

ThreadLocal为什么要使用弱引用和内存泄露问题

Map中的key为一个threadlocal实例. 这个Map的确使用了弱引用,不过弱引用只是针对key.每个key都弱引用指向threadlocal.假如每个key都强引用指向threadlocal,也就是上图虚线那里是个强引用,那么这个threadlocal就会因为和entry存在强引用无法被回收!造成内存泄漏 ,除非线程结束,线程被回收了,map也跟着回收。

虽然上述的弱引用解决了key,也就是线程的ThreadLocal能及时被回收,但是value却依然存在内存泄漏的问题。当把threadlocal实例置为null以后,没有任何强引用指向threadlocal实例,所以threadlocal将会被gc回收.map里面的value却没有被回收.而这块value永远不会被访问到了. 所以存在着内存泄露,因为存在一条从current thread连接过来的强引用.只有当前thread结束以后, current thread就不会存在栈中,强引用断开, Current Thread, Map, value将全部被GC回收.所以当线程的某个localThread使用完了,马上调用threadlocal的remove方法,就不会发生这种情况了。

另外其实只要这个线程对象及时被gc回收,这个内存泄露问题影响不大,但在threadLocal设为null到线程结束中间这段时间不会被回收的,就发生了我们认为的内存泄露。最要命的是线程对象不被回收的情况,这就发生了真正意义上的内存泄露。比如使用线程池的时候,线程结束是不会销毁的,会再次使用,就可能出现内存泄露。

HashSet和HashMap

HashSet的value存的是一个static finial PRESENT = newObject()。而HashSet的remove是使用HashMap实现,则是map.remove而map的移除会返回value,如果底层value都是存null,显然将无法分辨是否移除成功。

Boolean占几个字节

未精确定义字节。Java语言表达式所操作的boolean值,在编译之后都使用Java虚拟机中的int数据类型来代替,而boolean数组将会被编码成Java虚拟机的byte数组,每个元素boolean元素占8位。



数据库索引

为什么选择B+树作为索引结构?

  • Hash索引:Hash索引底层是哈希表,哈希表是一种以key-value存储数据的结构,所以多个数据在存储关系上是完全没有任何顺序关系的,所以,对于区间查询是无法直接通过索引查询的,就需要全表扫描。所以,哈希索引只适用于等值查询的场景。而B+ 树是一种多路平衡查询树,所以他的节点是天然有序的(左子节点小于父节点、父节点小于右子节点),所以对于范围查询的时候不需要做全表扫描

  • 二叉查找树:解决了排序的基本问题,但是由于无法保证平衡,可能退化为链表。

  • 平衡二叉树:通过旋转解决了平衡的问题,但是旋转操作效率太低。

  • 红黑树:通过舍弃严格的平衡和引入红黑节点,解决了 AVL旋转效率过低的问题,但是在磁盘等场景下,树仍然太高,IO次数太多。

  • B+树:在B树的基础上,将非叶节点改造为不存储数据纯索引节点,进一步降低了树的高度;此外将叶节点使用指针连接成链表,范围查询更加高效。

B+树的叶子节点都可以存哪些东西

可能存储的是整行数据,也有可能是主键的值。B+树的叶子节点存储了整行数据的是主键索引,也被称之为聚簇索引。而索引B+ Tree的叶子节点存储了主键的值的是非主键索引,也被称之为非聚簇索引.

覆盖索引

指一个查询语句的执行只用从索引中就能够取得,不必从数据表中读取。也可以称之为实现了索引覆盖。

查询在什么时候不走(预期中的)索引

  1. 模糊查询 %like

  2. 索引列参与计算,使用了函数

  3. 非最左前缀顺序

  4. where对null判断

  5. where不等于

  6. or操作有至少一个字段没有索引

  7. 需要回表的查询结果集过大(超过配置的范围)

数据库优化指南

  1. 创建并使用正确的索引

  2. 只返回需要的字段

  3. 减少交互次数(批量提交)

  4. 设置合理的Fetch Size(数据每次返回给客户端的条数)

Java平台结构

[图来自: Kotlin从入门到进阶实战 (陈光剑)]

JVM 内存模型 & 类加载机制 & GC 原理

运行时数据区域

  1. 程序计数器:程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。是线程私有”的内存。

  2. Java虚拟机栈:与程序计数器一样,Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧 ,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

  3. 本地方法栈:本地方法栈(Native Method Stack)与虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。

  4. Java堆:对于大多数应用来说,Java堆是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。

JVM 内存模型

分代回收

HotSpot JVM把年轻代分为了三部分:1个Eden区和2个Survivor区(分别叫from和to)。一般情况下,新创建的对象都会被分配到Eden区(一些大对象特殊处理),这些对象经过第一次Minor GC后,如果仍然存活,将会被移到Survivor区。对象在Survivor区中每熬过一次Minor GC,年龄就会增加1岁,当它的年龄增加到一定程度时,就会被移动到年老代中。

因为年轻代中的对象基本都是朝生夕死的,所以在年轻代的垃圾回收算法使用的是复制算法,复制算法的基本思想就是将内存分为两块,每次只用其中一块,当这一块内存用完,就将还活着的对象复制到另外一块上面。复制算法不会产生内存碎片。

在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。

常见的垃圾回收机制

  1. 引用计数法:引用计数法是一种简单但速度很慢的垃圾回收技术。每个对象都含有一个引用计数器,当有引用连接至对象时,引用计数加1。当引用离开作用域或被置为null时,引用计数减1。虽然管理引用计数的开销不大,但这项开销在整个程序生命周期中将持续发生。垃圾回收器会在含有全部对象的列表上遍历,当发现某个对象引用计数为0时,就释放其占用的空间。

  2. 可达性分析算法:这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。

G1和CMS的比较

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

禅与计算机程序设计艺术

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值