大家好,我是二哥呀。
尊嘟假嘟?果然评论区抱着怀疑态度的牛友发问了:“两三个月实习顶多存两三万吧 ?武理奖学金十几万?”于是赛文回复到:“还有学校补贴和导师横向,实习了半年多,存了五六万吧。”
厉害啊,我的赛文。我只能说,优秀的人总是能给人榜样的力量。
我把这条帖子同步到公众号上来,也是希望给关注二哥的同学们一点点鼓励和启发~
现在的互联网充斥着太多劝退、唱衰的声音,导致很多同学刷多了就容易陷入 emo、自我怀疑的情绪当中,然后就失去了学习的热情和动力。
但我一直给大家强调的是,不管任何时候,大环境好与坏,达尔文的进化理论一直都是存在且合理的,“优胜劣汰,适者生存。”
假如我有躺的资本,我为什么要努力?事实是我没有,所以除了努力,别无他法。但我也不会盲目地去卷,周末就该好好休息,所以周六的时候我去看了蔡姐的演唱会,周日的时候我躺在阴凉的草坪下,没有人能叫醒我(😁)。
周一我又元气满满地跑来公司上班,做好分内的事情,摸鱼的时候就顺带给大家更新一下八股~
(该摆则摆,该学就学,别学不好,睡不好,吃不好,玩不好,那算玩完)
好,这次我们就以《Java 面试指南-腾讯面经》同学 25 的 后台开发实习一面为例,来看看鹅厂的面试官都喜欢问哪些八股,25 届、26 届的同学可以拿来作为参考。
让天下所有的面渣都能逆袭 😁
JDK 8 中 HashMap 的数据结构是数组
+链表
+红黑树
。
三分恶面渣逆袭:JDK 8 HashMap 数据结构示意图
HashMap 的核心是一个动态数组(Node[] table
),用于存储键值对。这个数组的每个元素称为一个“桶”(Bucket),每个桶的索引是通过对键的哈希值进行哈希函数处理得到的。
当多个键经哈希处理后得到相同的索引时,会发生哈希冲突。HashMap 通过链表来解决哈希冲突——即将具有相同索引的键值对通过链表连接起来。
不过,链表过长时,查询效率会比较低,于是当链表的长度超过 8 时(且数组的长度大于 64),链表就会转换为红黑树。红黑树的查询效率是 O(logn),比链表的 O(n) 要快。数组的查询效率是 O(1)。
由于 HashMap 不是线程安全的,所以就有了 ConcurrentHashMap。
在 JDK 8 及以上版本中,ConcurrentHashMap 的实现进行了优化,不再使用分段锁,而是使用了一种更加精细化的锁——桶锁,以及 CAS 无锁算法。每个桶(Node 数组的每个元素)都可以独立地加锁,从而实现更高级别的并发访问。
初念初恋:JDK 8 ConcurrentHashMap
同时,对于读操作,通常不需要加锁,可以直接读取,因为 ConcurrentHashMap 内部使用了 volatile 变量来保证内存可见性。
对于写操作,ConcurrentHashMap 使用 CAS 操作来实现无锁的更新,这是一种乐观锁的实现,因为它假设没有冲突发生,在实际更新数据时才检查是否有其他线程在尝试修改数据,如果有,采用悲观的锁策略,如 synchronized 代码块来保证数据的一致性。
Java 内存模型(Java Memory Model)是一种抽象的模型,简称 JMM,主要用来定义多线程中变量的访问规则,用来解决变量的可见性、有序性和原子性问题,确保在并发环境中安全地访问共享变量。
深入浅出 Java 多线程:Java内存模型
JMM 定义了线程内存和主内存之间的抽象关系:线程之间的共享变量存储在主内存
(Main Memory)中,每个线程都有一个私有的本地内存
(Local Memory),本地内存中存储了共享变量的副本,用来进行线程内部的读写操作。
Java 提供了多种 IO 模型来处理输入和输出操作,包括传统的阻塞 IO、非阻塞 IO 和异步 IO。
BIO(Blocking I/O):采用阻塞式 I/O 模型,线程在执行 I/O 操作时被阻塞,无法处理其他任务,适用于连接数较少的场景。
NIO(New I/O 或 Non-blocking I/O):采用非阻塞 I/O 模型,线程在等待 I/O 时可执行其他任务,通过 Selector 监控多个 Channel 上的事件,适用于连接数多但连接时间短的场景。
AIO(Asynchronous I/O):使用异步 I/O 模型,线程发起 I/O 请求后立即返回,当 I/O 操作完成时通过回调函数通知线程,适用于连接数多且连接时间长的场景。
二哥的 Java 进阶之路:IO 分类
目前来说,JVM 的垃圾收集器主要分为两大类:分代收集器和分区收集器,分代收集器的代表是 CMS,分区收集器的代表是 G1 和 ZGC。
三分恶面渣逆袭:HotSpot虚拟机垃圾收集器
MySQL 的默认存储引擎是 InnoDB,它采用的是 B+树索引。B+树的非叶子节点只存储键值,不存储数据,而叶子节点存储了所有的数据,并且构成了一个有序链表。
用户1260737:B+树
这样做的好处是,非叶子节点上由于没有存储数据,就可以存储更多的键值对,树就变得更加矮胖了,于是就更有劲了,每次搬的砖也就更多了(😂)。
由此一来,查找数据进行的磁盘 IO 就更少了,查询的效率也就更高了。
再加上叶子节点构成了一个有序链表,范围查询时就可以直接通过叶子节点间的指针顺序访问整个查询范围内的所有记录,而无需对树进行多次遍历。
事务是一个或多个 SQL 语句组成的一个执行单元,这些 SQL 语句要么全部执行成功,要么全部不执行,不会出现部分执行的情况。事务是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。
事务的主要作用是保证数据库操作的一致性,即事务内的操作,要么全部成功,要么全部失败回滚,不会出现中间状态。这对于维护数据库的完整性和一致性非常重要。
事务具有四个基本特性,也就是通常所说的 ACID 特性,即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。
三分恶面渣逆袭:事务四大特性
TCP 是面向连接的,而 UDP 是无连接的。
三分恶面渣逆袭:TCP 和 UDP 区别
可以这么形容:TCP 是打电话,UDP 是大喇叭(😂)。
三分恶面渣逆袭:TCP 和 UDP 比喻
在数据传输开始之前,TCP 需要先建立连接,数据传输完成后,再断开连接。这个过程通常被称为“三次握手”。
UDP 是无连接的,发送数据之前不需要建立连接,发送完毕也无需断开连接,数据以数据报形式发送。
在此基础上,我们可以得出:TCP 是可靠的,它通过确认机制、重发机制等来保证数据的可靠传输。而 UDP 是不可靠的,数据包可能会丢失、重复、乱序。
TCP 的连接建立和断开过程分别被称为“三次握手”和“四次挥手”。
TCP(传输控制协议)的三次握手机制是一种用于在两个 TCP 主机之间建立一个可靠的连接的过程。这个机制确保了两端的通信是同步的,并且在数据传输开始前,双方都准备好了进行通信。
三分恶面渣逆袭:TCP 三次握手示意图
那我再说一下三次握手 🤝 的过程:
①、第一次握手:SYN(最开始都是 CLOSE,之后服务器进入 LISTEN)
②、第二次握手:SYN + ACK
③、第三次握手:ACK
TCP 连接的断开过程称为四次挥手(Four-Way Handshake)。它的目的是在客户端和服务器之间优雅地关闭连接,确保所有数据都被可靠传输。
三分恶面渣逆袭:TCP 四次挥手
第一次挥手(FIN):客户端向服务器发送一个 FIN(结束)报文,表示客户端没有数据要发送了,但仍然可以接收数据。客户端进入 FIN-WAIT-1 状态。
第二次挥手(ACK):服务器接收到 FIN 报文后,向客户端发送一个 ACK 报文,确认已接收到客户端的 FIN 请求。服务器进入 CLOSE-WAIT 状态,客户端进入 FIN-WAIT-2 状态。
第三次挥手(FIN):服务器向客户端发送一个 FIN 报文,表示服务器也没有数据要发送了。服务器进入 LAST-ACK 状态。
第四次挥手(ACK):客户端接收到 FIN 报文后,向服务器发送一个 ACK 报文,确认已接收到服务器的 FIN 请求。客户端进入 TIME-WAIT 状态,等待一段时间以确保服务器接收到 ACK 报文。服务器接收到 ACK 报文后进入 CLOSED 状态。客户端在等待一段时间后也进入 CLOSED 状态。
进程说简单点就是我们在电脑上启动的一个个应用,比如我们启动一个浏览器,就会启动了一个浏览器进程。进程是操作系统资源分配的最小单位,它包括了程序、数据和进程控制块等。
线程说简单点就是我们在 Java 程序中启动的一个 main 线程,一个进程至少会有一个线程。当然了,我们也可以启动多个线程,比如说一个线程进行 IO 读写,一个线程进行加减乘除计算,这样就可以充分发挥多核 CPU 的优势,因为 IO 读写相对 CPU 计算来说慢得多。线程是 CPU 分配资源的基本单位。
三分恶面渣逆袭:进程与线程关系
进程间通信(IPC,Inter-Process Communication)的方式有管道、信号、消息队列、共享内存、信号量和套接字。
编程十万问:进程间通信