” 本贴一问一答,以问促学,对答案有疑问建议新建问题.
# 1. 问:为何 JWT 可以防止被篡改?
JSON Web Token 由三部分组成,这些部分由点 (.) 分隔,分别是:头 (Header)、有效载荷 (Playload)、签名 (Signature);
- Header:对 TokenUtil.header(含有加密算法)进行 Base64Url 编码得到 jwt 的第一部分;
- Playload:存放有效信息的地方,Base64Url 编码得到第二部分;
- Signature:是整个数据的认证信息。一般根据前两步的数据,然后通过 header 中声明的加密方式结合密钥 secret 加密,然后就构成了 jwt 的第 3 部分
此时 signature 字段就是关键了,能被解密出明文的,只有 header 和 payload 假如黑客 / 中间人串改了 payload,那么服务器可以通过 signature 去验证是否被篡改过在服务端在执行一次 signature = 加密算法 (header + “.” + payload, 密钥);, 然后对比 signature 是否一致,如果一致则说明没有被篡改。
# 2. 二叉排序树和堆的区别?
- 定义
- 二叉排序树:每个结点的值均大于其左子树上所有结点的值,小于其右子树上所有结点的值
- 堆:是一个完全二叉树,并且每个结点的值都大于或等于其左右孩子结点的值
- 并未规定左右子节点之间的大小关系
- 用途
- 二叉排序树是为了实现动态查找而设计的数据结构,它是面向查找操作的,在二叉排序树中查找一个结点的平均时间复杂度是 O (log n);
- 堆是为了实现排序而设计的一种数据结构,它不是面向查找操作的,因而在堆中查找一个结点需要进行遍历,其平均时间复杂度是 O (n)。
# 3. 为什么使用 B + 树?
https://www.bilibili.com/video/BV1yB4y1v7Jy/?spm_id_from=pageDriver&vd_source=640552a4a073abb93a3b74142c217ac3
# 笔记:
首先默认读者已了解 B 树、B + 树 的基本知识
在解释为什么会有 B + 树 之前,需要一些预备知识:
# 关于磁盘

磁盘读取时,系统将数据逻辑地址传给磁盘,磁盘的控制电路会解析出物理地址(哪个磁道,哪个扇区),于是磁头需要前后移动到相应的磁道 —— 寻道,消耗的时间叫 —— 寻道时间,磁盘旋转将对应的扇区转到磁头下(磁头找到对应磁道的对应扇区),消耗的时间叫 —— 旋转时间,这一系列操作是非常耗时。为了尽量减少 I/O 操作,计算机系统一般采取预读的方式,预读的长度一般为页(page)的整倍数。
# 关于页
计算机系统是分页读取和存储的,一般一页为 4KB(8 个扇区,每个扇区 125B,8*125B=4KB),每次读取和存取的最小单元为一页,而 ** 磁盘预读时通常会读取页的整倍数。** 由于磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),所以即使只需要读取一个字节,磁盘也会读取一页的数据。
# 总结
B 树是多叉树,提高了查找效率。B + 树在多叉树的基础上,令非叶子结点只存储记录和指针,不存储具体的数据,使一次 IO 读取的一个结点可以包含更多的叶子节点,也就能够包含更多数据。【参考资料】
- https://blog.csdn.net/xd_1437/article/details/103253632
- https://zhuanlan.zhihu.com/p/37549063
- https://www.cnblogs.com/wkfvawl/p/11733057.html
# 4、为什么 Java 中全局变量不一定初始化,局部变量必须初始化?
这个问题涉及到 JVM 类加载和字节码执行两个阶段,这两个阶段是依次执行的。JVM 类加载是 JVM 利用类加载器将 class 文件加载到 JVM 的过程,涉及 “加载”、“验证”、“” 准备 “、“” 解析 “和” 初始化 “。** 类的成员变量初始化 — 在 JVM 类加载阶段完成 **** 静态成员变量 会被初始化两次,第一次在 “准备” 阶段,先进行一次初始化,系统附上默认值;第二次在 “初始化” 阶段,根据代码中的赋值情况再进行一次初始化。** 非静态成员变量 ** 仅 “初始化” 阶段赋值。根据代码中的赋值情况,代码不赋值直接赋默认值,有赋值则等于代码中的赋值。对象实例化后,该变量随 java 对象分配到 java 堆中。方法区的局部变量没有初始化 原因就是类方法中的代码,是在字节码执行的时候,才会被运行到,此时局部变量是存储在虚拟机栈 - 栈帧中的局部变量表中。局部变量定义了但是没有赋值是不能使用的。_可能的原因如下,当我们新建一个对象时,Java 会在 Heap 中申请一块内存区域用以存放类的数据。而成员变量就是类的数据,也是放在这块内存区域中的。只需要 JVM 在申请内存的时候顺便把整块区域都置为零即可完成初始化,方便快捷。__而对于方法的局部变量,是在线程的 Stack 中,当然 Stack 他也可以帮我们初始化,不过有一个问题。对于有些局部变量,在方法的一开始是没有的,有些在循环中的局部变量是要反复的声明多次的。有些局部变量作用域结束后,另一个局部变量又会占用这个局部变量的位置。总结:从技术上来讲,局部变量一般来说总量大,生命周期短,JVM 进行初始话开销较大;从业务上讲,局部变量一般用于实际问题下的运算,很少会用到默认值,赋值意义不大;从编程思想上讲,局部变量不初始化,而是报错提醒,更有助于程序员减少开发过程中出现缺陷的可能。
# 5. 为什么枚举类的构造器必须私有?
Java 中的枚举类为何不能有 public 构造器_BUAA 海海的博客 - CSDN 博客