文章目录
提问问题
- 介绍一下反射?有哪些常见操作?有什么优缺点?
- 堆是唯⼀可分配对象的地⽅吗?栈中的对象和堆中的对象内存分布有啥区别?
- 为什么 Java 对象占⽤的内存⼤⼩是 8 的整数倍
- 介绍一下锁升级过程?
- 如何实现乐观锁?
- 创建线程的⽅法?
- 线程池的常⽤参数?
- 为什么要⽤⾃增的主键?
- 由联合索引,联合索引列(如 where a=,b=,c=)如何确定是 a b c 的顺序?
- 介绍一下覆盖索引?
- union /union all 的区别,使用场景?
- ⼆叉树的遍历⽅式?根据前中后,怎么能复原⼆叉树?为什么前后不⾏?
- 五层⽹络模型是哪五层,七层网络模型呢?数据链路层的作⽤?
- 简述一下三次握⼿和四次挥⼿?为什么要等 2MSL?
- 大数据思路题:⼀个很⼤的数据⽂件,如何找其中最⾼频的 10 个?
- 算法题:反转链表
问题1
反射是Java编程语言的一个高级特性,它允许程序在运行时检查或修改其结构。通过反射,可以动态地加载类,创建对象,调用对象的方法,访问或修改字段,以及查询类的成员和继承结构等。
反射机制主要通过java.lang.reflect
包中的类和接口实现,包括Class
,Field
,Method
和Constructor
等。
反射的常见操作包括:
Class.forName("com.example.MyClass")
:动态加载类。myObject.getClass().getMethod("myMethod", parameterTypes)
:获取方法对象,用于后续调用。myObject.getClass().getField("myField")
:获取字段对象,用于读取或修改字段值。myClass.getConstructor(parameterTypes).newInstance(initialArgs)
:创建类的实例。
使用反射的好处包括:
- 可以在编译时不知道具体类名,运行时动态加载和使用类。
- 可以访问和修改私有成员,突破了Java的封装性。
- 可以动态调用方法,实现泛型编程和回调函数。
- 可以创建对象,而无需显式地指定类。
缺点:性能开销较大,代码可读性降低,以及安全性问题
问题2
在Java中,堆不是唯一可以分配对象的地方。
除了堆之外,还有几种情况下对象会被分配在其他地方:
栈(Stack):方法的局部变量通常是在栈上分配的,如果这些局部变量是对象引用,那么它们指向的对象仍然是在堆上分配的。
方法区(Method Area):这是一块被所有线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量等。在Java 8之前,字符串常量池也在方法区,但从Java 8开始,字符串常量池移到了堆内存中。
直接内存(Direct Memory):NIO库中可以使用堆外内存(也称为直接内存),这种内存直接分配在堆之外,可以通过
ByteBuffer.allocateDirect()
方法分配。
栈中的对象和堆中的对象内存分布的区别主要在于它们的生命周期和访问方式:
-
栈上的对象通常是局部变量,它们的生命周期与方法的执行时间相同。当方法执行完毕后,栈帧被弹出,栈上的局部变量随之销毁。
-
堆上的对象则有自己独立的生命周期,它们的创建和销毁由垃圾回收器控制。对象在堆上分配后,即使方法执行完毕,只要还有引用指向这个对象,它就不会被垃圾回收器回收。
栈上的局部变量访问速度通常比堆上的对象快,因为它们直接存放在CPU的寄存器或最近的缓存中。而堆上的对象则可能需要通过多级缓存和内存层次结构进行访问,速度相对较慢。
问题3
Java对象占用的内存大小通常是8的倍数,这是为了保持内存对齐并提高性能,Java虚拟机通常将对象的大小对齐到8字节边界。
因为Java对象在内存中的布局包括对象头、实例数据和对齐填充。对象头包含了对象的元数据(如哈希码、GC分代年龄、锁状态标志、线程持有的锁等),而实例数据则是对象实际存储的数据。
问题4
锁升级过程是指在Java中,当一个线程尝试获取一个已经被其他线程持有的锁时,它可能会经历从无锁到偏向锁、轻量级锁再到重量级锁的升级过程。这个过程是为了在不同的并发场景下平衡锁的开销和性能。
问题5
实现乐观锁通常使用版本号或时间戳来记录数据的变更。
当读取数据时,同时读取版本号或时间戳;当更新数据时,比较版本号或时间戳是否发生变化。如果没有变化,则进行更新并将版本号或时间戳加一;如果发生变化,则表示数据在读取和更新之间被其他线程修改过,此时放弃更新并抛出异常。
问题6
创建线程的方法常见的有以下几种,包括使用Thread类的构造函数直接创建线程,使用Runnable接口封装任务然后创建Thread对象,以及使用Callable接口封装任务并通过Executor框架提交任务。
问题7
线程池的常用参数包括核心线程数、最大线程数、存活时间、工作队列等。
核心线程数是线程池维护的最小线程数,即使没有任务执行也不会关闭;
最大线程数是线程池允许的最大线程数;
存活时间是空闲线程在被销毁之前等待新任务的最长时间;
工作队列是存储待执行任务的队列。
问题8
使用自增的主键是为了确保每个记录的唯一性和一致性。自增主键可以自动生成唯一的标识符,避免了手动输入主键可能导致的重复和错误。
ID 如果是随机的,可能导致分⻚操作,会造成内节点的更改,也许会导致更多的分⻚操作,带来⼤量 的 IO,影响性能。如果是⾃增的主键 ID,可以保证增加记录的时候是按照⻚⾯的顺序往后添加的。
问题9
在数据库中,联合索引(也称为复合索引)是在两个或多个列上建立的索引。索引列的顺序非常重要,因为它决定了数据库如何使用该索引来加速查询。通常情况下,数据库查询优化器会根据以下规则来确定是否使用联合索引以及如何使用联合索引:
最左前缀原则:数据库查询优化器使用联合索引时,会从索引的最左边的列开始匹配条件,直到遇到不符合索引列的条件为止。这意味着,如果查询条件中包含了联合索引的前导列,那么这个索引可能会被使用。如果查询条件跳过了前导列,则索引可能不会被使用。
索引列的选择性:选择性高(即唯一值多)的列放在索引的前面更有利于索引的使用,因为这样可以更快地缩小搜索范围。
查询条件的顺序:在编写查询时,应确保WHERE子句中的条件按照联合索引的列顺序出现。这样可以最大程度地利用索引。
索引覆盖扫描:如果一个查询只需要访问索引中的列,而不需要访问表中的数据行,这种情况称为索引覆盖扫描。在这种情况下,索引的列顺序应该与查询中SELECT子句中列出的列顺序相匹配。
索引列的函数和计算:在索引列上应用函数或者进行计算会导致索引失效,因为查询优化器无法利用索引来加速处理。
ORDER BY子句:当使用ORDER BY子句对结果进行排序时,如果排序的顺序与索引列的顺序一致,并且排序的方向(升序或降序)也与索引定义的方向相匹配,那么索引可能会被用于排序操作,从而提高效率。
问题10
覆盖索引(Covering Index)是指索引包含了查询所需的所有字段。当一个查询只需要访问索引而不需要访问表数据时,就可以使用覆盖索引来加速查询。
问题11
UNION和UNION ALL的区别在于UNION会去除重复的结果行,而UNION ALL则保留所有结果行,包括重复的。
使用场景取决于是否需要去重。
问题12
二叉树的遍历方法主要有前序遍历、中序遍历,后序遍历,层次遍历。
根据前中后序遍历的结果,可以通过递归或迭代的方式重建二叉树。前序和中序遍历的组合可以唯一确定一棵二叉树,但后序和中序遍历的组合不能唯一确定一棵二叉树,因为后序遍历的最后一部分无法确定左子树和右子树的边界。
问题13
五层网络模型包括物理层、数据链路层、网络层、传输层和应用层。
七层网络模型包括物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。
数据链路层的作用是实现相邻节点间的可靠数据传输,包括帧同步、流量控制、差错检测和纠正等。
问题14
TCP连接的建立和终止过程分别称为三次握手(Three-way Handshake)和四次挥手(Four-way Handshake)。
三次握手的过程如下:
- 客户端发送一个SYN(同步序列编号)标志的数据包到服务器,以便开始一个新的连接。
- 服务器收到SYN后,回应一个SYN/ACK(同步应答)标志的数据包以确认连接请求,并通知客户端准备好发送数据。
- 客户端收到SYN/ACK后,发送一个ACK(确认)标志的数据包作为响应,至此三次握手完成,连接建立。
四次挥手的过程如下:
- 当客户端完成数据传输后,向服务器发送一个带有FIN(结束)标志的数据包,请求关闭连接。
- 服务器收到FIN后,发送一个ACK标志的数据包作为响应,此时服务器进入CLOSE_WAIT状态,等待所有的数据传输完成。
- 服务器发送完所有数据后,向客户端发送一个带有FIN标志的数据包,请求关闭连接。
- 客户端收到FIN后,发送一个ACK标志的数据包作为响应,并进入TIME_WAIT状态等待足够的时间确保服务器收到最后的ACK。
2MSL是指从发送端到接收端,任何TCP报文段可能生存的最长时间。在四次挥手过程中,客户端在发送完最后一个ACK后,进入TIME_WAIT状态,这个状态需要保持2MSL的时间。这样做的目的有两个:
确保最后一个ACK报文段能够到达服务器。因为网络中可能存在延迟,如果客户端立即关闭连接,服务器可能会重新发送FIN报文段,而客户端由于已经关闭了连接,将无法回应这个FIN报文段,从而导致连接无法正确关闭。
使旧的重复的连接请求在网络中消失。如果客户端在2MSL时间内重启,它将重新监听相同的端口并开始新的连接。这样,任何旧的、重复的FIN或ACK报文段都将在2MSL时间后过期并被丢弃,避免它们干扰新的连接。
三次握手用于建立连接,而四次挥手用于安全地关闭连接。等待2MSL时间是为了确保连接的正确关闭和网络中旧连接请求的清理,从而维护网络的稳定性和可靠性。
问题15
对于大数据思路题,可以采用MapReduce编程模型(用于处理和生成大型数据集的分布式算法)来处理大文件。
首先对文件进行分块处理,每个分块由一个Map任务处理,计算每个单词的出现次数;然后将各Map任务的输出结果合并并排序,得到全局出现次数最高的10个单词及其频率。
问题16
使用双指针法,用一个临时变量指针做跳板,更换节点的next指向,具体看反转链表
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode cur = head;
ListNode temp = null;
while (cur != null) {
temp = cur.next;// 保存下一个节点
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
写在最后
PS:以上是网络上收集的一些常见的问题以及自己对答案搜索整理;一次整理基本上就是面试一次的题量,适合对自己的知识的查缺补漏
面试一般根据岗位要求或者简历上写的来进行扩展提问,也有些是直接问公司常用到的相关方面的技术问题,无论怎么准备都祝大家能拿到心怡的offer!