一、自我介绍
二、项目介绍
三、技术面试
1、Java基础
1,说一说jdk,jre,jvm
-
JDK (Java Development Kit):
-
是 Java 开发工具包,包含 JRE + 开发工具(如编译器
javac
、调试器jdb
、文档生成器javadoc
等)。 -
开发者用它编写、编译和调试 Java 程序。
-
-
JRE (Java Runtime Environment):
-
是 Java 运行时环境,包含 JVM + 核心类库(如
java.lang
、java.util
等)。 -
用户用它运行已编译的 Java 程序(
.class
或.jar
文件)。
-
-
JVM (Java Virtual Machine):
-
是 Java 虚拟机,负责 执行字节码(
.class
文件),实现跨平台特性(“一次编译,到处运行”)。 -
它通过 类加载器 加载字节码,由 解释器/JIT 编译器 转换为机器码执行,并管理内存(堆、栈等)。
-
2,你知道第三方的jar包中包括什么东西吗?(不了解,没回答上)
一个 JAR(Java Archive)包本质是一个 ZIP 压缩文件,包含以下内容:
-
编译后的
.class
文件:-
第三方库的核心代码(如工具类、接口实现)。
-
-
资源文件:
-
配置文件(如
.properties
、.xml
)、图片、国际化资源等。
-
-
元数据文件:
-
META-INF/MANIFEST.MF
:定义 JAR 的版本、入口类、依赖等信息。
-
-
依赖库(可选):
-
如果是一个“Fat JAR”(如 Spring Boot 项目),可能包含其他依赖的 JAR。
-
示例:
解压 commons-lang3-3.12.0.jar
后,会看到 org/apache/commons/lang3/StringUtils.class
等文件。
3,Java中的反射有了解吗?(没怎么用过,没回答上)
反射(Reflection)是 在运行时动态获取类的信息并操作对象 的机制,核心类在 java.lang.reflect
包中。
核心功能:
-
获取类信息:
Class<?> clazz = Class.forName("com.example.User");
-
创建对象:
Object obj = clazz.getDeclaredConstructor().newInstance();
-
调用方法:
Method method = clazz.getMethod("setName", String.class); method.invoke(obj, "Alice");
-
访问字段(甚至私有字段):
Field field = clazz.getDeclaredField("name"); field.setAccessible(true); // 突破私有权限 String name = (String) field.get(obj);
应用场景:
-
框架(如 Spring 的依赖注入、MyBatis 的结果映射)。
-
动态代理(如 AOP 实现)。
缺点:
-
性能低:反射操作比直接调用慢(需 JVM 动态解析)。
-
安全问题:可能破坏封装性(如访问私有字段)。
2、JVM相关
1、你知道垃圾回收算法吗?介绍一下Java的垃圾回收算法。
-
标记-清除(Mark-Sweep):
-
步骤:
-
标记所有活动对象。
-
清除未标记的对象。
-
-
缺点:内存碎片化。
-
-
复制算法(Copying):
-
步骤:
-
将内存分为两块(From 和 To)。
-
将 From 区的存活对象复制到 To 区,清空 From 区。
-
-
优点:无碎片。
-
缺点:内存利用率 50%。
-
应用场景:新生代(Eden 和 Survivor 区)。
-
-
标记-整理(Mark-Compact):
-
步骤:
-
标记所有活动对象。
-
将存活对象向一端移动,清理边界外的内存。
-
-
优点:无碎片、内存利用率高。
-
缺点:移动对象成本高。
-
应用场景:老年代。
-
-
分代收集(Generational):
-
原理:根据对象存活周期划分内存(新生代、老年代)。
-
新生代:频繁 GC,使用复制算法。
-
老年代:较少 GC,使用标记-清除或标记-整理。
-
现代 GC 算法:
-
G1(Garbage-First):将堆划分为多个 Region,优先回收价值高的 Region。
-
ZGC:低延迟,通过染色指针和读屏障实现并发标记。
3、数据库相关
1,你用过什么数据库?
2,说一说MyISAM和InnoDB的区别。
MyISAM:
不支持事务,但是每次查询都是原子的; 支持表级锁,即每次操作是对整个表加锁; 存储表的总行数; 一个MYISAM表有三个文件:索引文件、表结构文件、数据文件; 采用非聚集索引,索引文件的数据域存储指向数据文件的指针。辅索引与主索引基本一致,但是辅索引 不用保证唯一性。
InnoDb:
支持ACID的事务,支持事务的四种隔离级别; 支持行级锁及外键约束:因此可以支持写并发; 不存储总行数;一个InnoDb引擎存储在一个文件空间(共享表空间,表大小不受操作系统控制,一个表可能分布在多 个文件里),也有可能为多个(设置为独立表空,表大小受操作系统文件大小限制,一般为2G),受操 作系统文件大小的限制; 主键索引采用聚集索引(索引的数据域存储数据文件本身),辅索引的数据域存储主键的值;因此从辅 索引查找数据,需要先通过辅索引找到主键值,再访问辅索引;最好使用自增主键,防止插入数据时, 为维持B+树结构,文件的大调整。
特性 | MyISAM | InnoDB |
---|---|---|
事务支持 | 不支持 | 支持(ACID 兼容) |
锁机制 | 表级锁 | 行级锁(并发性能更高) |
外键 | 不支持 | 支持 |
崩溃恢复 | 较差(需手动修复) | 支持自动恢复(redo log) |
存储结构 | 三个文件(.frm , .MYD , .MYI ) | 所有表存储在共享表空间或独立文件(.ibd ) |
适用场景 | 读多写少(如日志系统) | 高并发、事务处理(如电商订单) |
3,什么叫回表?
当使用 非聚簇索引(二级索引)查询时,若所需字段不在索引中,需 根据主键值回到主索引(聚簇索引)查找完整数据,这个过程称为回表。
4,怎么排查一个MySQL的慢查询?
-
开启慢查询日志:
SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 2; -- 超过 2 秒的查询记录 SET GLOBAL slow_query_log_file = '/path/to/slow.log';
-
分析日志:
-
使用
mysqldumpslow
工具:mysqldumpslow -s t /path/to/slow.log
-
第三方工具:Percona Toolkit 的
pt-query-digest
。
-
-
EXPLAIN 分析执行计划:
EXPLAIN SELECT * FROM orders WHERE user_id = 100;
-
关注
type
(扫描方式)、key
(使用的索引)、rows
(扫描行数)。
-
-
优化索引或 SQL:
-
添加缺失索引。
-
避免
SELECT *
,减少数据传输。
-
5,用什么命令来查询一个sql语句走了哪些索引,是否走索引这些信息?
1.使用 EXPLAIN
命令:
EXPLAIN SELECT * FROM user WHERE age > 20;
输出中的关键字段:
-
possible_keys
:可能使用的索引。 -
key
:实际使用的索引。 -
type
:-
index
:全索引扫描。 -
ref
:使用非唯一索引查找。 -
range
:范围扫描。
-
-
rows
:预估扫描行数。
示例:
若 key
为 NULL
,说明未使用索引。
6,解释一下最左前缀原则
定义:
在使用 联合索引(如 INDEX (a, b, c)
)时,查询条件必须 从最左列开始,且 不能跳过中间列。
有效场景:
-
WHERE a = 1 AND b = 2
→ 使用索引。 -
WHERE a = 1 AND c = 3
→ 仅使用a
列索引。
无效场景:
-
WHERE b = 2
→ 无法使用索引。 -
WHERE b = 2 AND c = 3
→ 同上。
原理:
索引的存储结构按 (a, b, c)
排序,类似电话簿按“姓氏-名字-中间名”排序。跳过最左列时,无法利用有序性。
4、redis相关
1,redis有哪些常用的数据结构?
-
String:
-
存储文本、数字(如计数器
INCR
)。
-
-
List:
-
双向链表,支持队列(
LPUSH
/RPOP
)或栈(LPUSH
/LPOP
)。
-
-
Hash:
-
键值对集合(如存储用户信息
HSET user:1000 name "Alice"
)。
-
-
Set:
-
无序唯一集合(如共同好友
SINTER
)。
-
-
Sorted Set:
-
带分数的有序集合(如排行榜
ZADD
)。
-
-
其他:
-
Bitmaps(位操作)、HyperLogLog(基数统计)、Streams(消息队列)。
-
2,在哪些项目中使用到了redis?
-
缓存:
缓存数据库查询结果(如商品详情),减少 DB 压力。 -
会话管理:
存储用户登录状态(Session)。 -
分布式锁:
通过SET key value NX EX
实现互斥操作。 -
限流:
计数器(如每分钟允许 100 次请求)。 -
分布式唯一id
3,你项目中的商城的分布式id是怎么做的?
使用的redis的incr制作的分布式id,先创建唯一key值,然后将值设为0,在插入商品时使用reids自增属性incr来进行商品id自增
方案 | 优点 | 缺点 |
---|---|---|
UUID | 简单、全球唯一 | 无序、长度长,不适合主键 |
数据库自增 | 有序、简单 | 性能低、单点故障 |
Redis INCR | 性能高、有序 | 依赖 Redis 可用性 |
雪花算法 | 高性能、趋势递增、去中心化 | 依赖系统时钟(时钟回拨问题) |
5、linux相关
1,如何判断一个端口在监听状态?
# 查看 TCP/UDP 监听端口
netstat -tuln | grep :8080
# 或
ss -tuln | grep :8080
# 或
lsof -i :8080
-
-t
:TCP 协议。 -
-u
:UDP 协议。 -
-l
:仅显示监听状态的端口。 -
-n
:以数字形式显示地址和端口
2,如何查询一个进程的id号?
# 根据进程名查找
ps aux | grep nginx
# 或使用 pgrep
pgrep -f nginx
# 或使用 pidof(仅适用于进程名)
pidof nginx
-
ps aux
:显示所有进程详细信息。 -
pgrep -f
:按完整命令行匹配。
6、操作系统相关
1,说一说进程和线程
特性 | 进程 | 线程 |
---|---|---|
资源分配 | 独立内存空间、文件句柄等 | 共享进程资源(内存、文件) |
切换开销 | 高(需切换内存空间) | 低(共享地址空间) |
通信方式 | 管道、信号、共享内存、消息队列、套接字 | 共享变量(需同步机制) |
容错性 | 进程崩溃不影响其他进程 | 线程崩溃可能导致整个进程终止 |
2,协程有了解过吗?(没听过)
-
定义:用户态的轻量级线程,由程序控制调度(而非操作系统)。
-
优点:
-
切换成本极低(无内核切换)。
-
适合高并发 IO 密集型任务(如网络服务器)。
-
-
示例:
-
Python 的生成器:通过
yield
挂起协程。 -
Go 的 goroutine:由 Go 运行时调度,配合通道(channel)通信。
-
3,进程间的通信方式有没有了解?
回答到了消息队列,共享内存,tcp等远程调用,但是面试官更想了解的是操作系统相关的
-
管道(Pipe):
-
单向通信,用于父子进程。
-
示例:
ls | grep .txt
。
-
-
命名管道(FIFO):
-
有名称的管道,可用于无亲缘关系的进程。
-
-
信号(Signal):
-
异步通知(如
kill -9 PID
发送 SIGKILL)。
-
-
共享内存:
-
多个进程访问同一块内存(需同步机制,如信号量)。
-
-
消息队列:
-
内核维护的链表,进程通过消息类型读写。
-
-
套接字(Socket):
-
跨网络通信(如 TCP/UDP)。
-
4,说一说什么是信号量
-
定义:用于控制多个进程/线程对共享资源的访问的计数器。
-
操作:
-
P(Proberen,尝试):若信号量 > 0,减 1;否则阻塞。
-
V(Verhogen,增加):信号量加 1,唤醒等待进程。
-
-
示例:
sem_t sem;
sem_init(&sem, 0, 1); // 初始值 1(二进制信号量,即互斥锁)
sem_wait(&sem); // P 操作
// 临界区代码
sem_post(&sem); // V 操作
7、docker和k8s相关
1,docker和k8s有了解过吗?
-
Docker:
-
容器化平台,将应用及其依赖打包成镜像,实现 一次构建,到处运行。
-
核心概念:镜像(Image)、容器(Container)、仓库(Registry)。
-
-
Kubernetes(K8s):(没回答上)
-
容器编排系统,管理容器化应用的部署、扩展、自愈等。
-
核心概念:Pod(最小调度单元)、Service(网络访问)、Deployment(副本管理)。
-
2,dockerfile有写过吗?(没有写过)
3,k8s面试官强烈推荐了解使用
8、算法相关
1,描述一下快速排序
步骤:
-
选择基准值(pivot):通常选第一个元素或随机元素。
-
分区(Partition):
-
将数组分为两部分,左边 ≤ pivot,右边 ≥ pivot。
-
-
递归排序:对左右子数组重复上述过程。
示例:
void quickSort(int[] arr, int low, int high) {
if (low < high) {
int pivot = partition(arr, low, high);
quickSort(arr, low, pivot - 1);
quickSort(arr, pivot + 1, high);
}
}
int partition(int[] arr, int low, int high) {
int pivot = arr[high];
int i = low - 1;
for (int j = low; j < high; j++) {
if (arr[j] < pivot) {
i++;
swap(arr, i, j);
}
}
swap(arr, i + 1, high);
return i + 1;
}
数组 [5, 3, 8, 6, 2]
,选择 pivot=5:
-
分区后 →
[3, 2, 5, 8, 6]
。 -
递归排序
[3, 2]
和[8, 6]
。
2,快速排序的时间复杂度是多少?
-
平均情况:O(n log n) → 每次分区大致平衡。
-
最坏情况:O(n²) → 每次分区极不平衡(如数组已排序且选第一个元素为 pivot)。
-
优化方法:
-
随机选择 pivot。
-
三数取中法(选头、中、尾的中位数)。
-
四、你有什么问题?
1、贵公司招聘实习生,更想要招到什么样的人才?
面试官:精通底层原理的人才,不仅要涵盖项目技术,更希望具备丰富的计算机原理,操作系统,数据结构与算法等底层知识兼备的人才。
2、贵公司目前的项目发展方向是什么?
面试官:我们公司目前是做云计算,云服务器方向发展。
3、实习生一般负责什么样的任务?
面试官:实习生一般一进来需要先花一段时间属性公司业务,公司框架,然后进行一些环境的熟悉与部署,偶尔会分发一些能够三四天完成的小模块。
4、本次面试感觉我怎么样?请客观评价一下我
面试官:对知识的掌握还比较表面,不够深入,希望继续往底层多下功夫,Linux,git等多用命令行,少用图形化
5、公司目前使用的语言选型是哪些?
面试官:c++,python,PHP,go