DispathcherServletHashMap的底层实现
JDK1.8之前
底层的实现是数组+链表 结合一起使用,也就是
散列表
拉链法:创建一个链表数组,数组中每一格就是一个链表。
若遇到哈希冲突,则将冲突的值写入到链表中即可。(数组+链表)
jdk1.8之后使用==(数组+红黑树)==
如何选用集合
主要根据集合的特点来选用,比如
我们需要根据键值获取到元素值时就选用 —> Map接口下的集合。
需要排序时选择 -----> TreeMap
不需要排序选择 -----> HashMap
需要保证线程安全就用 -----> ConcurrentHashMap
需要存放元素值时 ------> 就选择实现Collection接口的集合
需要保证元素唯一时选择实现set接口的集合比如:TreeSet HashSet
进程
对象的创建:
-
**类加载检查:**虚拟机遇到一条new指令时
①检测指令的参数是否能在常量池中定位到这个类的符号引用
②检查这个符号代表的类是否已被加载过
③如果没有执行相应的类加载过程。
-
**分配内存:**对象所需内存的大小在类的加载完成之后可确定。
为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来。分配方式有(指针碰撞,空闲列表)。选择分配方式由Java决定。而Java堆却由垃圾收集器是否带有压缩整理功能决定。
-
初始化零值: 内存分配后,虚拟机要将分配到的内存空间初始化为零值。这一阶段保证Java代码可以不赋初值就直接使用。
-
**设置对象头:**初始化零值之后,,虚拟机对对象设置类的实例信息。如何才能找到类的元数据信息,对象的哈希码,对象的GC分代年龄等信息。(这些信息会存放在对象中)
-
**执行init()方法:**完成以上工作后一个新到对象已经产生。对象创建才刚开始,init方法执行之后,所有字段都是
初始值
,执行init之后 对象按照程序员指定的值进行初始化。
线程的声明周期和状态转换
JVM
运行时数据区域(JDK1.8)
线程私有的:
- 程序计数器
- 虚拟机栈
- 本地方法栈
线程共享的:
- 堆
- 方法区
- 直接内存
程序计数器主要的两个作用
- 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制: 顺序执行,选择,循环,异常处理
- 在多线程的情况下,用于记录当前线程执行的位置,从而线程被切换回来的时候知道该线程运行到哪里了
Java虚拟机栈是由一个个栈帧组成,而每个栈帧都拥有:局部变量,操作数栈,动态链接口,方法出口信息。
Java有两种方式返回:(导致栈帧被弹出)
- Return 语句
- 抛出异常
本地方法栈
本地方法被执行的时候,在本地方法栈会创建一个栈帧,用于存放局部变量表,操作数栈,动态连接,出口信息
堆
Java堆还可以细分为:新生代和老年代。再细致一点有:Eden空间、From Survivor、To Survivor空间等
方法区(永久代)
方法区与Java堆一样,是各个线程共享的内存区域,储存虚拟机加载的类信息,常量,静态变量。(编译后的数据)
运行时常量池(属于方法区)
Class 文件中除了有类的版本,字段,方法,接口等描述信息外,还有常量池信息(用于存放编译期生成的各种字面量和符号引用)
直接内存
直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁的使用。而且也可能导致OutOfMemoryError 异常出现
JVM 加载类的过程
- 类的加载和检查
- 分配内存
- 初始化零值
- 设置对象头
- 执行init方法
堆空间的基本结构及分配策略:
eden区、s0区、s1区都属于新生代,tentired 区属于老年代。大部分情况,对象都会首先在 Eden 区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入 s0 或者 s1,并且对象的年龄还会加 1(Eden区->Survivor 区后对象的初始年龄变为1),当它的年龄增加到一定程度(默认为15岁),就会被晋升到老年代中。
另外长期存活的对象直接进入老年代
大多数情况下, 对象在新生代eden区分配。当eden区没有足够的空间进行分配时,虚拟机发起依次Minor GC(新生代垃圾收集
-
新生代(Minor GC):指发生新生代的垃圾收集动作,Minor GC非常频繁,回收速度比较快
-
老年代(Major GC/Full GC): 出现了Major GC经常会伴随至少一次的Minor GC(并非绝对),Major GC的速度一般会比Minor GC的慢10倍以上。
如何判断对象死亡
-
引用计数器:给对象添加引用计数器,每当有引用到它,计数器就加1;当引用失效,计数器就减1;任何时候计数器为0 该对象就不能被继续使用了
-
可达性分析算法:通过一些列的称为“GC Roots“的对象为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链的话,则证明此对象是不可用的。
引用
-
强引用:类似必不可少的生活用品,垃圾回收器绝不会回收它。当内存空间不足时,Java虚拟机宁愿抛出OutofMemoryError错误,使程序异常终止,也不会随意回收强引用
-
软引用:类似可有可无的生活用品。如果内存空间足够,垃圾收集器就不会收集它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就能被程序使用。软引用可用来实现内存敏感的告诉缓存
-
弱引用:类似可有可无的生活用品。弱引用和软引用的区别在于:弱引用只要在垃圾收集器扫描的时候,只要是扫描到就直接回收。但是垃圾回收线程是一个优先级很低的线程,不一定会很快扫描到。
使用软引用的情况较多,因为软引用能加速JVM对垃圾内存的回收速度
内存整理算法的使用
复制算法:(新生代)
为了解决效率问题,“复制”收集算法出现了。它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。
标记-清除 标记-整理 算法(老年代选择)
分代收集算法:根据对象不同将内存分为几块。一般Java分为新生代和老年代,这样我们就可用根据各个年代的特点选择合适的垃圾收集算法。
常见的垃圾收集器
Serial收集器:
单线程,在进行垃圾收集的时候必须暂停其他所有的工作线程,直到他收集结束。
缺点:给用户不良的体验
优点:简单高效,没有交互的开销,可用获得很高的单线程收集效率。对于运行在Client下 的虚拟机来说是个很不错的选择。
ParNew收集器:就是Serial的多线程版,除了使用多线程外其余的和serial收集器完全一样(控制参数,收集算法,回收策略)
Parallel Scavenge收集器
类似ParNew收集器,但是Parallel Scavenge关注的点是吞吐量。吞吐量:CPU中用于运行用户代码的时间与CPU总消耗时间的比值。(高效的利用CPU)
Class文件字节码结构组织示意图
-
**魔数:**确定这个文件是一个能被虚拟机接收的class文件
-
**Class文件版本:**Class文件的版本号,保证编译正常执行
-
**常量池:**常量池主要存放的两大常量:字面量和符号引用
-
**访问标志:**用于识别一些类或者接口层次的访问信息,包括:这个Class是类还是接口,是否为public或者abstract类型,是否声明为final等
-
**当前类索引,父类索引:**类索引确定这个类的全限定名,父类索引用于确定这个父类的全限定名,由于Java语言的单继承,所以父类索引只有一个,除了java.lang.Object之外,所有的java类都有父类,因此除了java.lang.Object外,所有Java类的父类索引都不为0
-
**接口索引集合:**用来描述这个类实现了那些接口,这些被实现的接口将按implements(如果这个类本身是接口的话则是extends)后的接口顺序从左到右排列在接口索引集合中。
-
**字段表集合:**描述接口或类中声明的变量。字段包含类级变量以及实例变量,但不包括在方法内部声明的局部变量。
-
**熟悉表集合:**在Class文件,字段表,方法表中都可以携带自己的属性表集合。
**类加载过程:**加载->连接->初始化。**连接过程可分为:**验证->准备->解析
加载过程主要完成以下三个步骤:
-
通过全类名获取定义此类的二进制字节流
-
将字节流所代表的静态存储结构转换为方法区的运行时数据结构
-
在内存中生成一个代表该类的class对象,作为方法区这些数据的访问入口
类加载器
1. **BootStrapClassLoader(启动类加载器):**最顶层的类加载器,由C++实现,负责加载%JAVA_HOME%/lib目录下的jar包和类或者被Xbootclasspath 参数指定的路径中的类
2. **ExtensionClassLoader(扩展类加载器):**主要负责加载目录%JRE_HOME%/lib/ext 目录下的jar包和类,或者被java.ext.dirs 系统变量所指定的路径下的jar包
3. **AppClassLoader(应用程序类加载器):**面向我们用户的类加载器,负责加载当前应用classpath下的所有jar包和类
双亲委派模型介绍:
每一个类都有一个对应它的类加载器。系统中的 ClassLoder 在协同工作的时候会默认使用 双亲委派模型 。即在类加载的时候,系统会首先判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载。加载的时候,首先会把该请求委派该父类加载器的 **loadClass()**
处理,因此所有的请求最终都应该传送到顶层的启动类加载器 **BootstrapClassLoader**
**中。当父类加载器无法处理时,才由自己来处理。**当父类加载器为null时,会使用启动类加载器 BootstrapClassLoader
作为父类加载器。
双亲委托模型带来的好处:
双亲委托模型保证了Javac程序的稳定运行,可以避免类的重复加载(JVM区分不同的类的方式不仅仅根据类名,相同的类文件被不同的类加载器加载产生的两个不同的类),也保证了Java的核心API不被篡改。如果不使用双亲委托模型,而是每个类加载自己的话会出现一些问题:编写一个java.lang.object类的话,那么程序运行的时候,系统就会出现多个不同object类
计算机网络
**OSI的体系结构:**应用层,表示层,会话层,运输层,网络层,数据链路层,物理层。
**TCP/IP的体系结构:**应用层(TELNET,FTP,SMTP),运输层(TCP/UDP),网络层IP,网络接口层
**五层协议的体系结构:**应用层,运输层,网络层,数据链路层,物理层
应用层:
通过进程间的交互来完成特定网络应用。
运输层:
主要的任务是负责向两台主机进程之间的通信提供通用的数据传输服务。(有以下两种协议)
-
传输控制协议(TCP):提供面向连接的,可靠的数据传输服务
-
用户数据协议(UDP):提供无连接的,尽最大努力数据传输服务(不保证数据传输可靠性)
网络层:
计算机网络中进行通信的两个计算机之间可能会经过很多个数据链路,也可能还要经过很多通信子网。网络层的任务就是选择合适的网间路由和交换结点,确保数据及时传送。
数据链路层:
通常称为链路层。两台主机之间的数据传输,总是在一段一段上的链路上传送的,这就需要专门的链路层协议。数据链路层将网络层交下来的IP数据报组装成帧,在两个相邻结点间的链路上传送帧。每一帧包括数据和必要的控制信息。(同步信息,地址信息,差错控制)
(不仅要检错,还要纠错)
物理层:
传送单位是比特。实现相邻计算机结点之间比特流的透明传送,尽可能屏蔽掉具体传输介质和物理设备差异。使其上面的数据链路层不必考虑网络的具体传输介质是什么。
七层体系结构图
TCP三次握手
-
客户端-发送带有SYN标志的数据包- 一次握手-服务端
-
服务端-发送带有SYN/ACK标志的数据包_二次握手-客户端
-
客户端-发送带有ACK标志的数据包-三次握手-服务端
为什么要进行三次握手
简历可靠的通信,三次握手的目的是双方确认自己与对方发送与接受是正常的。
-
第一次握手:Client什么都不能确认,Server确认了对方发送正常,自己接受正常。
-
第二次握手:Client确认了:自己发送,接受正常;对方发送,接受正常;
Server确认了:自己接收正常;对方发送正常
-
第三次握手:Client全部确认
Server全部确认
为什么要回传SYN、
接收端传回发送端所发送的SYN是为了告诉发送端,我们接收到的消息确实就是你所发送的信息。
断开一个连接需要四次挥手
-
客户端-发送一个FIN,用来关闭客户端到服务器的数据传送
-
服务器收到这个FIN,它发送回一个ACK,确认序号为收到的序号加1.和SYN一样。一个FIN将占用一个序号
-
服务器关闭与客户端的连接,发送一个FIN给客户端
-
客户端-发回ACK报文确认,并将确认序号设置为收到序号加1
为什么要四次挥手
举个例子:A 和 B 打电话,通话即将结束后,A 说“我没啥要说的了”,B回答“我知道了”,但是 B 可能还会有要说的话,A 不能要求 B 跟着自己的节奏结束通话,于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了”,A 回答“知道了”,这样通话才算结束。
TCP的概述
TCP把连接作为最基本的对象,每一条TCP连接都有两个端点,这种端点我们叫作套接字(socket),它的定义为端口号拼接到IP地址即构成了套接字,例如,若IP地址为192.3.4.16 而端口号为80,那么得到的套接字为192.3.4.16:80。
四次挥手
TCP/UDP的区别
**UDP:**在传输数据之前不需要先建立连接,远地的主机收到UDP报文后也不需要任何确认。虽然UDP不提供可靠的交付,但是却非常的高效(QQ语言,QQ视频,直播等等)
**TCP:**在传送数据时需要先建立连接,结束后要释放连接。TCP不提供广播,多播等服务。增加了许多开销。用于文件传输,发送和接收邮件,远程登录等场景。
**ARQ协议:自动重传请求,。**是OSI模型中数据链路层和传输层的错误纠正协议之一。通过使用确认和超时两个机制。如果发送方在发送后一段时间内没有收到确认帧,它通常回重写发送,ARQ包括停止等待ARQ协议和连续ARQ协议。
**停止等待ARQ协议:**每发完一个分组就停止发送,等待对方确认(回复ACK)。如果过了一段时间(超时时间后),还是没有收到 ACK 确认,说明没有发送成功,需要重新发送,直到收到确认后再发下一个分组;
**连续ARQ协议:**发送方维持一个发送窗口,凡位于发送窗口内的分组可以连续发送出去,而不需要等待对方确认。接收方一般采用累计确认,对按序到达的最后一个分组发送确认,表明到这个分组为止的所有分组都已经正确收到了。
打开一个网页,整个过程会使用哪些协议!
总体来说分为以下几步:
- DNS解析
- TCP连接
- 发送HTTP请求
- 服务器处理请求并返回HTTP报文
- 浏览器解析渲染页面
- 连接结束
DNS 解析过程
**DNS缓存:**浏览器缓存,系统缓存,路由器缓存,IPS服务器缓存,根域名服务器缓存,顶级域名服务器缓存,主域名服务器缓存。
TCP连接
HTTP协议是使用TCP作为其传输层协议的,当TCP出现瓶颈时,HTTP也会收到影响。
HTTPS协议
HTTP是包裹在TCP报文里面发送的,服务器端收到TCP报文的时会解包提取出HTTP报文。但是HTTP报文是明文,如果中间被截取的话会存在泄露的风险。所以在进入TCP之前加一次密SSL。从网络层级结构看它位于HTTP协议与TCP协议之间。
状态码
-
1xx:指示信息-表示请求已接收,继续处理
-
2xx:成功-表示请求已被接收,理解,接受
-
3xx:重定向-要完成请求必须进行更进一步的操作
-
4xx:客户端错误-请求有语法错误或请求无法实现
-
5xx:服务器错误-服务端未能实现合法的请求
HTTP长连接和短连接
HTTP/1.0默认使用短连接。客户端和服务器端每进行一次的HTTP操作,就建立一次连接。当客户端遇到包含其他的web资源。浏览器就会重写建立一个HTTP会话
而从HTTP/1.1起,默认使用长连接。会在响应头加入这行代码
Connection:keep-alive
当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。
HTTP协议的长连接和短连接,实质上是TCP协议的长连接和短连接
HTTP/1.1的优点
-
默认使用长连接;减少开销,有非流水线和流水线
-
错误状态响应码:新增了24个错误的状态响应码
-
缓存处理:引入更多的缓冲控制策略
-
带宽优化及网络连接的使用:HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
URI和URL的区别是什么
-
URI(Uniform Resource Identifier) 是统一资源标志符,可以唯一标识一个资源。
-
URL(Uniform Resource Location) 是统一资源定位符,可以提供该资源的路径。它是一种具体的 URI,即 URL 可以用来标识一个资源,而且还指明了如何 locate 这个资源。
数据结构
Queue(队列):
Java中的Queue继承了Collection接口,还添加额外的,添加,删除,查询操作
FIFO(先进先出),尾部添加,。头部删除
- 单队列:每次添加元素,都是添加到队尾,存在假溢出的问题,明明有位置却不能添加
- 循环队列(避免假溢出问题)
https://blog.csdn.net/u011240877/article/details/52860924
循环队队列中:rear = (rear-size)%size
Set(无序的):
继承于Collection接口,不允许出现重复元素,主要是HashSet和TreeSet两大实现类
在判断重复元素的时候,HashSet集合会调用hashCode()和equal()方法来实现;TreeSet集合会调用compareTo方法来实现。
HashSet是哈希结构:主要利用HashMap的key来存储元素,计算插入元素的hashcode来获取元素在集合中的位置;(获取HashSet中元素的时候只能通过遍历来获取)
拥有以下特点:
-
不允许重现重复的元素
-
允许插入null值
-
元素无序(添加顺序和遍历顺序不一样)
-
线程不安全,若2个线程同时操作HashSet,必须通过代码实现同步
TreeSet是红黑树结构,每一个元素都是树中的一个结点,插入的元素都会进行排序
基于Map来实现的,具有以下特点
-
对插入的元素进行排序,是一个有序的集合
-
底层使用红黑树结构,不是哈希结构
-
允许插入null值
-
不允许插入重复元素
-
线程不安全
List
可以精确的控制每个元素的插入位置,可以通过整数索引来访问元素。List允许重复元素。另外List是有序的集合而Set是无序的集合
-
**ArrayList:**一个动态数组。由数组实现,随机访问效率高,随机插入,删除效率低
-
**LinkedList:**双向链表。可被当做堆栈,队列或双端队列进行操作。访问效率低,但是插入,删除效率高
-
**Vector:**和ArrayList一样是一个动态数组,由数组实现。单ArrayList是非线程安全的,Vector是线程安全的。
-
**Stack:**是栈继承于Vector。他的特性:先进后出。
**顺序栈:(用数组来实现)**可以指定创建时候的大小,扩容的时候,是以size*2+1 的公式进行扩容的
链式栈:(用单链表来实现)
ArrayList源码学习
相当于动态数组。继承AbstarctList,实现了List,RandomAccess,Cloneable,java.io.Serializable
实现List->插入元素的时间复杂度O(n) ,取元素时间复杂度未O(1),添加,删除,修改,遍历
实现RandomAccess接口->支持快速随机访问。即可通过元素下标快速获取元素对象
实现Cloneable接口->覆盖了函数clone,能被克隆
实现java.io.Serializable->能通过序列化去传输
HashMap
JDK1.8之前是使用(数组+链表)来实现的通过key的 hashcode来计算hash值,当hashcode相同时。通过拉链法解决冲突
JDK1.8之前
HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前entry的next指向null),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度依然为O(1),因为最新的Entry会插入链表头部,急需要简单改变引用链即可,而对于查找操作来讲,此时就需要遍历链表,然后通过key对象的equals方法逐一比对查找.
ConcurrentHashMap使用分段锁->代替HashTable
二叉树
完全二叉树
1. 若设二叉树的高度为h,除第h层外,其他各层的结点数都达到了最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布。
满二叉树
1. 除了叶节点外每一个结点都有左右两个结点且叶子结点都处在最底层的二叉树
平衡二叉树
1. 又被称为AVL树,是一棵二叉排序树,具有以下性质:他是一棵空树或它的左右子树的高度差绝对不超过1,并且左右两颗子树都是平衡二叉树。
完全二叉树
叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树。
满二叉树
一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是(2^k) -1 ,则它就是满二叉树。
堆
每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
二叉查找树
-
若任意结点的左子树不空,则左子树上所有结点的值均小于它的根节点的值
-
若任意结点的右子树不空,则右子树上所有的结点的值均大于它根节点的值
-
任意结点是左,右子树也分别为二叉查找树
-
没有键值相等的结点
红黑树
-
每个结点非黑即红
-
根结点总是黑色的
-
每个叶子结点都是黑色的空结点(NIL结点)
-
如果结点是红色的,则它的子结点必须是黑色的(反之不一定)
-
从根结点到叶子结点或空子节点的每条路径,必须包含相同数目的黑色结点(黑色高度相同)
左旋示意图
http://dandanlove.com/2018/03/18/red-black-tree/
- 将y的左子节点赋给x的右子节点,并将x赋给y左子节点的父节点(y左子节点非空时)
- 将x的父节点p(非空时)赋给y的父节点,同时更新p的子节点为y(左或右)
- 将y的左子节点设为x,将x的父节点设为y
左旋示意图:对节点x进行左旋
* p p
* / /
* x y
* / \ / \
* lx y -----> x ry
* / \ / \
* ly ry lx ly
右旋示意图
- 将x的右子节点赋给y的左子节点,并将y赋给x右子节点的父节点(x右子节点非空时)
- 将y的父节点p(非空时)赋给x的父节点,同时更新p的子节点为x(左或右)
- 将x的右子节点设为y,将y的父节点设为x
右旋示意图:对节点y进行右旋
* p p
* / /
* y x
* / \ / \
* x ry -----> lx y
* / \ / \
* lx rx rx ry
**B树,(B-树)😗*为了磁盘或其他存储设备而设计的一种多叉平衡二叉树
特点:
· B-tree是一种多路搜索树(并不是二叉的),对于一棵M阶树:
· 定义任意非叶子结点最多只有M个孩子;且M>2;
· 根结点的孩子数为[2, M],除非根结点为叶子节点;
· 除根结点以外的非叶子结点的儿子数为[M/2, M];
· 非叶子结点的关键字个数=指向儿子的指针个数-1;
· 每个非叶子结点存放至少M/2-1(取上整)和至多M-1个关键字;
· 非叶子结点的关键字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];
· 非叶子结点的指针:P[1], P[2], …, P[M];其中P[1]指向关键字小于K[1]的子树,P[M]指向关键字大于K[M-1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树;
· 所有叶子结点位于同一层;
算法
KMP算法
用来解决字符串查找的问题。可以再一个字符串中查找一个字串出现的位置。KMP算法把字符串的时间复杂度缩小到O()m+n),而空间复杂度也只有O(m).因为暴力搜索的方法反复回溯主串。导致效率低下,而KMP算法可以利用已经匹配这个有效信息,保持主串上的指针不回溯。通过修改字串的指针,让模式串尽量地移动到有效的位置。
假设有一个主串 :BBC ABCDAB ABCDABCDABDE
子串:ABCDABD
部分匹配的前缀和后缀计算:
已知空格与D不匹配时,前面六个字符"ABCDAB"是匹配的。查表可知,最后一个匹配字符B对应的"部分匹配值"为2,因此按照下面的公式算出向后移动的位数:
移动位数 = 已匹配的字符数 - 对应的部分匹配值
·因为 6 - 2 等于4,所以将搜索词向后移动4位。
因为空格与C不匹配,搜索词还要继续往后移。这时,已匹配的字符数为2(“AB”),对应的"部分匹配值"为0。所以,移动位数 = 2 - 0,结果为 2,于是将搜索词向后移2位。
不调用内置函数将字符串转换为整数
调用以下方法可以判断是否是 数字
Character.isDigit(chars[i])
操作系统
进程管理,内存管理,虚拟内存
什么是操作系统
1. 管理计算机硬件与软件资源的程序,是计算机系统的内核与基石
2. 本质上是运行再计算机上的软件程序
3. 为用户提供一个与系统交互的操作界面
4. 操作系统分内核与外壳(外壳:围绕着内核的应用程序,内核:能操作硬件的程序’
内核复杂管理系统的进程,内存,设备驱动程序,文件和网络系统等等。决定着系统性能和稳定性。是连接应用程序和硬件的桥梁。
用户是通过操作系统来操作计算机硬件的
进程与线程
一个进程中可以有多个线程,多个线程共享进程的堆和方法区资源,但是每个线程有自己的程序计数器,虚拟机栈和本地方法栈
总结:
线程是进程划分成的更小的运行单位,一个进程在其执行的过程中可以产生多个线程。线程和进程最大的不同在于基本上各进程是独立的,而各线程则是不一定的,因为同一进程中的线程极有可能会相互影响。线程执行开销小,但不利于资源的管理和保护;而进程正相
进程通信的概念:
每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)
进程通信的七种方式
- 管道/匿名管道
- 有名管道(FIFO
- 信号
- 消息队列
- 共享内存
- 信号量
- 套接字(域,端口号,协议类型)
线程间的同步方式有哪些呢?
操作系统一般有下面三种线程同步的方式:
-
互斥量:采用互斥对象机制,互斥对象只有一个。保证公共资源不会被多个线程同时访问。比如java种的synchronized和各种Lock都是这种机制
-
信号量:运行同一时刻多个线程访问同一资源,但是需要控制同一式可访问此资源的最大线程数量
-
事件:通过通知操作的方式来保持多个线程同步,还可以方便的实现多线程优先级的比较操作
操作系统中进程调度算法
-
先到先服务调度算法(FCFS)
-
短作业优先调度算法(SJF)
-
时间片轮转调度算法
-
多级反馈队列调度算法
-
优先级调度
常见的集中内存管理机制
l 块式管理
l 页式管理
l 段式管理
l 段页式管理‘
什么是虚拟内存
这个在我们平时使用电脑特别是 Windows 系统的时候太常见了。很多时候我们使用点开了很多占内存的软件,这些软件占用的内存可能已经远远超出了我们电脑本身具有的物理内存。为什么可以这样呢? 正是因为 虚拟内存 的存在,通过 虚拟内存 可以让程序可以拥有超过系统物理内存大小的可用内存空间。另外,虚拟内存为每个进程提供了一个一致的、私有的地址空间,它让每个进程产生了一种自己在独享主存的错觉(每个进程拥有一片连续完整的内存空间)。这样会更加有效地管理内存并减少出错。
Mysql数据库
一些常用的命令
查看所有存储引擎: show engines;
查看当前默认存储引擎: show variavles like ‘%storage_engine%’
查看表的存储引擎: show table status like “table_name”
默认版本:InnoDB
事务的四大特性
- 原子性
- 一致性
- 隔离性
- 持久性
事务所造成的四大事件
-
**脏读:**当一个事务正在访问数据并堆数据进行了修改吗,而这种修改还没有提交到数据库中,这时另外一个事务页访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据“,根据”脏数据“所能左的操作可能是不正确的
-
**丢失修改:**指一个事务读取一个数据时候,另外一个事务也访问了该数据。那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样的第一个事务内的修改结果就被丢失,因此称为修饰修改。例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1 ,最终结果A=19,事务1的修改被丢失
-
**不可重复读:**指一个事务内多次读同一数据。这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样,这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读
-
**幻读:**幻读与不可重复读类似/它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读
表级锁: Mysql中锁定 粒度最大 的一种锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM和 InnoDB引擎都支持表级锁。
行级锁: Mysql中锁定 粒度最小 的一种锁,只针对当前操作的行进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。
l RXC方案存储高价值的数据,如:账户,订单,交易等等
l Replication方案存储低价值数据,如:权限,通知,日志等等
l 用MyCat或JDBC-Sharding切分数据管理集群
常见字符集
-
ASCII字符集:基于罗马字母表的一套字符集,它采用1个字节的低7位标志字符,高位始为0
-
LATIN1字符集:相对于ASCII字符集左了扩展,仍然使用一个字节表示字符,但启用了高位,扩展了字符集的表示范围
-
GBK字符集:支持中文,字符有一字节编码和两字节编码方式
-
UTF8字符集:Unicode字符集的一种,是计算机科学领域里的一项业界标准,支持了所有国家的文字字符,utf-8采用1-4个字节表示字符
Mysql优化:https://segmentfault.com/a/1190000006158186
一条SQL语句在MySQL中如何执行的
l **连接器:**进行用户的校验,查询对应用户权限,后续只要连接不断开,即使管理员修改了用户的权限,用户也是不受影响的
l **查询缓存(MySQL8.0版本后移除):**主要用来执行select语句以及该语句的结果集。以key-value的形式缓存在内存中,key是查询预计,Value是结果集。
l **分析器:**分析SQL语句是来干嘛的。①词法分析。②语法分析
l **优化器:**执行认为最优的方案
l **执行器:**校验用户权限。然后返回接口执行结果
数据库基本设计规范
-
所有表必须使用Innodb存储引擎
-
数据库和表的字符集统一使用UTF8
-
所有表和字段都需要添加注释
-
尽量控制单表数据量的大小,建议控制在500万以内
-
谨慎使用MySQL分区表
-
尽量做到冷热数据分离,减小表的宽度
-
禁止在表中建立预留字段
-
精致在数据库中存储图片,文件等大的二进制数据
-
禁止在线上做数据库压力测试
-
禁止从开发环境,测试环境直接连接生成环境数据库
一个 SQL 执行的很慢,我们要分两种情况讨论:
1、大多数情况下很正常,偶尔很慢,则有如下原因
(1)、数据库在刷新脏页,例如 redo log 写满了需要同步到磁盘。
(2)、执行的时候,遇到锁,如表锁、行锁。
2、这条 SQL 语句一直执行的很慢,则有如下原因。
(1)、没有用上索引:例如该字段没有索引;由于对字段进行运算、函数操作导致无法用索引。
(2)、数据库选错了索引。
索引的基本知识:
-
索引可以加快数据库的检索速度
-
表经常进行 insert/update/delete操作就不要建立索引了,索引会降低插入,删除,修改等维护任务的速度
-
索引需要占物理和数据空间
MySQL的基本存储结构是页记录都存在页里面
而各个数据页之间可以组成一个双向链表
而每个数据页中的记录又可以组成一个单向链表
当我们执行select * from user where username=’chenbin’的时候
① 首先会定位到记录所在的页(遍历双向链表,找到所在的页)
② 从所在的页内中查找对应的记录(根据主键查询,遍历所在页的单链表)
很慢!!!!
添加索引之后结构变为如下
实际上以上的索引建立底层都是一棵B+树 .
l B+树是一棵平衡树,如果我们对这个可树增删改查的化肯定会破坏它的结构
l 要维持平衡树,就必须做额外的工作。正因为这些额外的工作开销,导致索引会降低增删改的速度
!!!!还有一种哈希索引
- 本质上就是把键值换算成新的哈希值,根据这个哈希值来定位。
Mysql如何为表添加索引
Redis
Redis简单来说就是一个数据库,不过redis是存在内存中的,因此读写速度非常快。广泛应用于缓存方向。可以用来做分布式。(支持事务,持久化,LUA脚本,LRU驱动时间,多种集群方案)
高性能缓存
高并发(把数据库的一部分请求转移到缓存中,不需要经过数据库)
为什么使用redis而不用map/guava做缓存
原因如下:~~~
l 缓存分为本地缓存和分布式缓存,以java为例。使用自带的map/guava实现的是本地缓存,主要的特点的轻量快速。声明周期随着jvm的销毁而结束。并且在多实例的情况下,每个实例都需要各自保存一份缓存,缓存不具有一致性。使用redis之类发分布式缓存,爱多实例的情况下。各实例共用一份缓存数据,缓存具有一致性。
l Redis可以用几十G内存来做缓存,Map不行。一般jvm最多分几个G
l Redis的缓存可以持久化,Map是内存对象,程序已重启数据就没了
l Redis可以实现分布式的缓存,Map只能存在创建它的程序里
l Redis可以处理每秒百万级的并发,是专业的缓存服务,Map只是一个普通的对象
l Redis缓存有过期机制,Map本身无此功能
l Redis有丰富的API,Map自身的api太简单了
Redis的文件时间处理器结构包含的四部分
- 多个socket
-
IO多路复用程序
-
文件时间分派器
-
事件处理器(连接应答处理器,命令请求处理器,命令恢复处理器)
多个socket可能会并发产生不同的操作,每个操作对应不同的文件事件,但是IO多路复用程序程序会监听多个socket,会将socket产生的事件放入队列中排队,事件分派其每次从队列中取出一个事件,把该事件交给对应的事件处理器进行处理
Redis常见的数据结构
String
常用命令:set get decr incr mget
String数据结构是简单的key-value类型,value页可以是数字
应用:常规技术:微博数,粉丝数
Hash
常用命令:hget hset hgetall
Hash 是一个string类型的filed和value的映射。Hash特别适合用于存储对象。可以直接仅仅修改对象中的某个字段的值。可以用来存储用户信息,商品信息等等。
List
常用命令: lpush rpush
List就是链表,最重要的数据结构之一。应用:关注列表,粉丝列表,消息列表功能
Redis list的实现为一个双向链表,实现简单的高性能分页,做类似微博那种下拉不断分页的对象。性能高
Set
对外提供的功能类似list 特殊之处在于set可以自动排重。基于set轻易实现交集,并集,差集的操作
比如:微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis可以非常方便的实现如共同关注,共同粉丝,共同喜好等功能。
Sinterstore key1 key2 key3
Sorted Set
Zadd zrange zrem zcard
和set相比,sorted set增加了一个权重参数score,使用集合中的元素能够按score进行有序排序
举例:直播系统中,实时排行信息包含直播间的在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)。
定期删除+惰性删除
定期删除:redis默认每隔100ms就随机抽取一些设置了过期时间的key,如果过期就删除。
惰性删除:定期删除可能 会导致很多过期key到了时间并没有被删除掉。假如过期key,靠定期删除没有被删除掉,就可以通过惰性删除来删除了
Redis提供了8种淘汰策略:
-
Volatile-lru:从已设置过期时间的数据集种挑选最近最少使用的数据淘汰
-
Volatile-ttl:从已设置过期时间的数据集种挑选将要过期的数据淘汰
-
Volatile-random:从已设置过期时间的数据集种任意选择数据淘汰
-
Allkeys-lru:当内存不足以容纳新写入数据时,在键空间种,移除最近最少使用的key
-
Allkeys-random:从数据集中任意选择数据淘汰
-
No-eviction:禁止驱逐数据,也就是说当内存不足时,写入操作会报错
-
Volatile-lfu:从已设置过期时间的数据集中挑选最不经常使用的数据淘汰
-
Allkeys-lfu:当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的key
Redis事务
Redis事务提供了一种将多个命令请求打包,然后一次性,按顺序的执行多个命令的机制,并且执行期间,服务器不会终端事务而改去执行其他客户端的命令请求,会将事务中的所有命令都执行完毕,然后再去处理其他客户端的命令请求。
缓存雪崩(可以通过限流降级的方式来解决)
缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉
缓存穿透:
大量的请求的key不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层。
Spring
Spring模块集成:核心容器,数据访问/集成,Web,AOP(面向切面编程),工具,消息和测试模块。
Spring一些重要的模块
-
Spring Core:基础,可以说Spring其他所有的功能都需要依赖于该类库。主要提供IOC依赖注入功能
-
Spring Aspects:该模块与AspectJ的集成提供支持
-
Spring AOP:提供了面向切面的编程实现
-
Spring JDBC:Java数据库连接
-
Spring JMS:Java消息服务
-
Spring ORM:用于支持Hibernate等ORM工具
-
Spring Web:为创建Web应用程序提供支持
-
Spring Test:提供了对Junit和TestNG测试的支持
注解解释
@Controller+@ResponseBody=@RestController
@ResponseBody:注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到HTTP响应(Response)对象的body中,通常用来返回JSON或者XML数据。返回JSON数据的情况比较多
==IOC:==一种设计思想,将原本在程序中手动创建对象的控制权,交由Spring框架来管理。
Ioc容器是Spring用来实现IOC的载体,IOC容器实际上就是各Map(key,value),map中存放各种对象
IOC就像是一个工厂一样,当我们需要创建一个对象的时候。只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。
Ioc Container容器的好处:
① 自动对代码进行初始化,只需要维护一个Configuration(可以是一段xml代码),而不用每次初始化一辆车都要新手去写那一大段初始化代码。
② 我们在创建实例的时候不需要了解其中的细节。
IOC容器创建过程
Ioc Container在进行这个工作的时候是反过来的,从最上层开始往下找依赖关系,到达最底层之后再往上一步一步new(优点像深度遍历)
IOC容器可以直接隐藏具体的创建细节,在我们看来就像是一个工厂
Spring IOC的初始化过程:
Spring IOC分析:https://javadoop.com/post/spring-ioc
**AOP:**能够将哪些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理,日志管理,权限控制等)封装起来。减少系统的重复代码,减低耦合度。有利于未来的可扩展性和可维护性。
Spring中bean的作用域
-
Singleton:唯一bean实例,Spring中的bean默认是单例的
-
Prototype:每次请求都会创建一个bean实例
-
Request:每一次HTTP请求都会产生一个bean实例,该bean仅在当前Http request内有效
-
Session:每一次HTTP请求都会产生一个bean实例,该bean仅在当前HTTP Session内有效
-
Global-session:全局session作用域,
一般使用@Autowired注解自动装配bean,要想把类标识成可用于@Autowired 注解自动装配的bean的类。采用以下注解可实现:
-
@Component:通用的注解,可标注为任意类为Spring 组件,如果一个bean不知道属于那一层,可以使用@Component注解标注。
-
@Repository: 对应持久层Dao层,主要用于数据库的操作
-
@Service:对应服务层,主要涉及一些复杂的逻辑,需要用到Dao层
-
@Controller:对应MVC的控制层,主要用户接受用户请求并调用Service层返回数据给前端页面
Spring中的bean生命周期
-
Bean容器找到配置文件中的Spring Bean定义
-
Bean容器利用Java Reflection API 创建一个Bean实例
-
如果涉及到一些属性值 利用set()方法设置一些属性值
-
如果Bean实现了BeanNameAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例
-
与上面类似如果实现了*.Aware接口,就调用相应的方法
-
如果有和这个Bean的spring容器相关的BeanPostProcessor对象,执行postProcessBeforeInitialization()方法
-
如果Bean实现了InitializingBean接口,执行afterPropertieSet()方法
-
如果Bean在配置文件中定义包含init-method属性。执行指定的方法
-
如果有和加载这个Bean的spring容器相关的BeanPostProcessor对象,执行postProcessAfterInitialization()方法
-
当要销毁Bean的时候,如果Bean实现了DisposableBean接口,执行destory()方法
-
当要销毁Bean的时候,如果Bean在配置文件中定义包含destory-method属性,执行指定的方法。
SpringMVC工作原理
看图自己理解
- 浏览器(用户) 首先发起request 请求url。请求交给了前端控制器(DispatcherServlet) 接收用户的请求和响应
- 前端控制器交给请求查找 handler ----> 处理器映射器 handlerMapping
- 返回一个执行链 给前端控制器
- 请求适配器执行handler 处理器适配器 handlerAdapter 去执行Handler
- Handler处理器就是我们服务器写的controller 类。返回对应的ModelAndView
- 处理器适配器 将返回的 ModelAndView直接返回给前端控制器DispathcherServlet
- DispathcherServlet请求 进行视图解析 --> 交给视图解析器解析并返回View页面 ,并返回给DispatcherServlet
- 视图渲染 ,将模型数据填充到request域中,并response给客户端
流程说明*(重点!
-
客户端(浏览器)发送请求,直接请求到DispatcherServlet
-
DispathcerServlet根据请求信息调用HandlerMapping,解析请求对应的handler
-
解析到对应的handler(也就是我们平常所说的controller控制器)后,开始由HandlerAdapter适配器处理。
-
HandlerAdapter会根据Handler来调用真正的处理器开始处理请求,并处理相应的业务逻辑
-
处理器处理完业务后,会返回一个ModelAndView对象,Model是返回的数据对象,View是个逻辑上的View。
-
ViewResolver会根据逻辑View查找实际的View
-
DispaterServlet把返回的Model传给View(视图渲染)
-
把View返回给请求者(浏览器)
Spring框架中用到的设计模式
- 工厂设计模式:Spring使用工厂模式通过BeanFactory,ApplicationContext创建bean对象
- 代理设计模式:SpringAOP功能的实现
- 单例设计模式:Spring中的Bean默认都是单例的
- 包装器设计模式:我们的项目需要链接多个数据库,而且不同的客户每次访问中根据需要回去访问不同的数据库,这种模式可以让我们根据客户需求能够切换不同的数据源
- 观察者模式:Spring事件驱动模型就是观察者模式很经典的一个应用
- 适配器模式:SPringAOP的增强或通知使用到了适配器模式,Spring MVC中也是用到了适配器模式适配Controller
@Transational(rollbackFor=Exception.class)
当该注解作用于类上时,该类的所有public方法将都具有该类型的事务属性。同时我们也可以在方法级别来使用该注解来覆盖类级别定义。如果类或者方法上加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚
在@Transational如果不配置rollback属性,那么事务只会在遇到RuntimeException的时候才回滚。
Mybatis
面试题引出
① #{}和 ${}的区别是什么?
-
是 P r o p e r t i e s 的 占 位 符 , 它 可 以 用 于 标 签 属 性 值 和 s q l 内 部 。 属 于 静 态 文 本 替 换 。 比 如 : {} 是Properties的占位符,它可以用于标签属性值和sql内部。属于静态文本替换。比如: 是Properties的占位符,它可以用于标签属性值和sql内部。属于静态文本替换。比如:{driver}会被静态替换为com.mysql.jdbc.Driver.
-
#{}是sql的参数占位符,Mybatis会将sql中的#{}替换为?号。在sql执行之前会使用PreparedStatement的参数设置方法。按序给sql的?号占位符设置参数值。比如ps.setInt(0,parameterValue),#{item.name}的取值方式为使用反射出参数对象中获取name属性值,相当于param.getItem.getName()
② Xml映射文件中,除了常见的select|insert|update|delete标签之外,还有哪些标签?
-
还由很多标签,,,,
-
加上动态sql的9个标签:trim|where|set|foreach|if|choose|when|otherwise|bind等。
③ 最佳实践中,通常一个xml映射文件,都会写一个Dao接口与之对应,请问。这个Dao接口与之对应,请问这个Dao的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?
-
Dao接口就是人们常说的Mapper接口接口的权限名,就是映射文件中的namespace的值。接口的方法名,就是映射文件中的Mappedstatement的id值,接口内的参数。就是传递给sql的参数。
-
当调用接口方法时。接口全限名+方法名拼接字符串作为key值。可唯一定位一个MappedStatement。
-
举例:com.mybatis.mappers.StudentDao.findStudentById.可唯一找到一个namespace为com.mybatis.mappers.StudentDao下面id=findStudentById的MapperStatement。
-
Dao接口里的方法,是不能重载的,因为是全限定名+方法名的保存和寻找策略
-
Dao接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理Dao接口生成proxy对象,代理对象proxy会拦截接口方法,转而执行MappedStatement所代表的sql,然后sql执行结果返回。
④ Mybatis是如何进行分页的?分页插件的原理是什么?
-
Mybatis使用RowRounds对象进行分页,它是针对ResultSet结果执行的内存分页,而非物理分页,可以在sql内直接书写带有物理分页的参数来完成分页功能,也可以使用分页插件来完成物理分页。
-
例如:
映射文件:<select id="findAllUsers" resultType="User">
select id,name,gender from t_user
</select>
映射接口:public List<User> findAllUsers(RowBounds rowBounds);
- 测试方法
- select _ from student,拦截 sql 后重写为:select t._ from (select * from student)t limit 0,10
⑤ Mybatis动态sql是做什么的?都有哪些动态sql?能简述一下动态sql的执行原理不
-
Mybatis动态sql可以在xml映射文件内,以标签的形式编写动态sql。完成逻辑判断和动态拼接sql的功能。
-
Mybatis提供了九种动态sql标签:trim|where|set|foreach|if|choose|when|otherwise|bind
-
Mybatis的xml映射文件中,不同的xml映射文件,id是否可以重复
-
不同的xml映射文件,如果配置了namespace,那么id可以重复,如果没有配置namespace,那么id不能重复。毕竟namspace不是必须的,只是最佳实践而已
-
Namespace+id 作为Map<String,MappedStatement>的key使用,如果没有namespace只剩下id,那么id重复会导致数据相互覆盖。有了namespace,自然id就可以重复了
⑥ Mybatis如何执行批处理?
- 使用BatchExecutor完成批处理
⑦ Mybatis有哪些Exector执行器?他们之间的区别是什么?
-
Mybatis有三种基本的处理器:SimpleExecutor,ReuseExecutor,BatchExecutor
-
SimpleExecutor:每次执行update或select,就开启一个Statement对象,用完立刻关闭Statement对象
-
ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String,Statement>内。供下一次使用。(重复使用Statement对象
-
BatchExecutor:执行update(没有select,jdbc的批处理不支持select),所有sql都添加到批处理中,等待统一执行(executorBatch(),它缓存了多个Statement对象。每个Statement对象都是addBatch()完毕后,等待逐一执行executorBatch()批处理。与jdbc批处理相同
-
作用范围:Executor的这些特点 ,都严格限制在SqlSession生命周期范围内
⑧ Mybatis映射文件中,如果A标签通过了include引用了B标签的内容。请问,B标签能否定义在A标签的后面,还是说必须定义在A标签的前面?
-
虽然Mybatis的解析xml映射文件是按照顺序解析的。但是,被引用的B标签可以定义在任何地方。Mybatis都可以正确识别到
-
原理:Mybatis解析A标签,发现A标签引用了B标签,但B标签尚未解析到,尚不存在,此时,Mybatis会将A标签未识别到的B标记为为解析状态。如何继续解析余下的标签,包含B标签,待所有标签解析完毕。Mybatis会重新解析那些被标记为为解析的标签,B标签已经存在。A标签也就可以正常的解析了。
⑨ 简述Mybatis的xml映射文件和Mybatis内部数据结构之间的映射关系
- Mybatis将所有xml配置信息都封装到All-in-one重量级对象Configuration内部。在XML映射文件中标签会被解析为ParameterMap对象,其每个子元素会被解析为Paeameter对象。标签会被解析为ResultMap对象,其每个子元素会被解析为ResultMapping对象。每一个 ,,,标签均会被解析为MappedStatament对象,标签内的sql会被解析为BoundSql对象
认证授权
认证(Authentication):验证你身份的凭据,通过这个凭据,系统得知你就是你,也就是说系统存在你这个用户。所以,Authentication被称为身份/用户验证(你是谁?)
授权(Authorization):发生在Authentication(认证之后)。主要掌握我们访问系统的权限。比如有些特点的资源只能具有特定的权限的人才能访问比如admin(你有权限干什么)
① 什么是Cookie?Cookie的作用是什么?如何在服务端使用Cookie?
-
Cookies和Session都是用来跟踪浏览器用户身份的会话方式,但两者的应用场景不太一样。
-
Cookie:Cookies是某些网站为了辨别用户身份而存储在用户本地终端上的数据(通常经过加密)。简单来说:Cookies存放在客户端,一般用来保存用户信息
Cookie的一些应用案例
-
我们在Cookie中保存已经登录过的用户信息,下次访问网站的时候页面可以自动帮你登录的一些基本信息给填了。除此之外,Cookie还能保存用户首选项,主题和其他设置信息
-
使用Cookie保存Session或者token。向后端发送请求的时候带上Cookie,这样后端就能取到Session或者token了。这样就能记录用户当前的状态。因为HTTP协议是无状态的
-
Cookie还可以用来记录和分析用户行为。举个简单的例子你在网上购物的时候,因为HTTP协议是没有状态的。如果服务器想要获取你在某个页面停留状态或者看了那些商品,一种常用的方式就是将这些信息存放在Cookie
② Cookie和Session有什么区别?如何使用Session进行身份验证?
-
Session的主要作用就是通过服务端记录用户的状态。
-
Cookie 数据保存在客户端(浏览器端),Session 数据保存在服务器端。相对来说 Session 安全性更高。如果使用 Cookie 的一些敏感信息不要写入 Cookie 中,最好能将 Cookie 信息加密然后使用到的时候再去服务器端解密。
③ 如果没有cookie的话Session还能用吗?
-
一般是通过Cookie来保存SessionID,假如你使用了Cookie保存SessionID的方法的话,如果客户端禁用了Cookie,那么Session就无法正常工作
-
但是,我们可以将SessionID放在url里https://javaguide.cn/?session_id=xxx。, 这种方案的话可行。但是安全性和用户体验感降低。
④ 为什么Cookie无法防止CSRF攻击,而token可以?
- CSRF:一般翻译为跨站请求伪造。
- 例子:小壮登录了某网上银行,他来到了网上银行的帖子区,看到一个帖子下面有一个链接写着“科学理财,年盈利率过万”,小壮好奇的点开了这个链接,结果发现自己的账户少了10000元。这是这么回事呢?原来黑客在链接中藏了一个请求,这个请求直接利用小壮的身份给银行发送了一个转账请求,也就是通过你的 Cookie 向银行发出请求。
进行Session认证的时候,我们一般使用Cookie来存储SessionId。登录成功之后会生成一个cookie存放在客户端。客户端每次访问的时候都会带上这个cookie来访问。
-
利用token的话。我们一般会将token存放在local storage中,然后我们在前端通过某些方式(get post)给每个请求加上token,这样就不会有csrf漏洞的问题。
-
需要注意的是XSS :无论Cookie还是token都是无法避免的
暑期在中国科学院软件应用技术研究所学习的内容。
之前用Typora写了之后上传到GitHub
现在搬运过来csdn了