垃圾回收机制
首先我们要知道知道被称为垃圾
垃圾:就是这个对象不再有其他的对象的引用,就说这个对象时无用的,此对象就称为垃圾,其占用的内存也要被销毁
判断是否为垃圾的算法
引用计数法
引用计数法就是给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加 1;当引用失效,计数器就减 1;任何时候计数器为 0 的对象就是不可能再被使用的,可以当做垃圾收集。
可达性分析算法
从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的。
垃圾回收算法
标记清除算法。从名字可以看到其分为两个阶段:标记阶段和清除阶段。一种可行的实现方式是,在标记阶段,标记所有由 GC Root 触发的可达对象。此时,所有未被标记的对象就是垃圾对象。之后在清除阶段,清除所有未被标记的对象。标记清除算法最大的问题就是空间碎片问题。如果空间碎片过多,则会导致内存空间的不连续。虽说大对象也可以分配在不连续的空间中,但是效率要低于连续的内存空间。
复制算法。复制算法的核心思想是将原有的内存空间分为两块,每次只使用一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中。之后清除正在使用的内存块中的所有对象,之后交换两个内存块的角色,完成垃圾回收。该算法的缺点是要将内存空间折半,极大地浪费了内存空间。
标记压缩算法。标记压缩算法可以说是标记清除算法的优化版,其同样需要经历两个阶段,分别是:标记结算、压缩阶段。在标记阶段,从 GC Root 引用集合触发去标记所有对象。在压缩阶段,其则是将所有存活的对象压缩在内存的一边,之后清理边界外的所有空间。
对比一下这三种算法,可以发现他们都有各自的优点和缺点。
标记清除算法虽然会产生内存碎片,但是不需要移动太多对象,比较适合在存活对象比较多的情况。而复制算法虽然需要将内存空间折半,并且需要移动存活对象,但是其清理后不会有空间碎片,比较适合存活对象比较少的情况。而标记压缩算法,则是标记清除算法的优化版,减少了空间碎片。
(1)强引用:如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题
(2)软引用:如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它,如果内存空间不足,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用
(3)弱引用:一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存
(4)虚引用:如果一个对象经持有虚引用,那么他就和没有任何引用一样,在任何实收都可能被垃圾回收期回收
JVM
运行时数据区域包括五个部分:程序计数器、方法区、堆、栈、本地方法栈
程序计数器:程序计数器是CPU的寄存器,他保存的当前程序执行的地址。如果线程执行的是非native方法,则程序计数器中保存的是当前需要执行的指令的地址;如果线程执行的是native方法,则程序计数器中的值是undefined。由于程序计数器中存储的数据所占空间的大小不会随程序的执行而发生改变,因此,对于程序计数器是不会发生内存溢出现象(OutOfMemory)的。
本地方法栈:它为执行本地方法服务
栈:存放局部变量、栈帧。对于基本数据类型的变量,则直接存储它的值,对于引用类型的变量,则存的是指向对象的引用,由于每个线程正在执行的方法可能不同,因此每个线程都会有一个自己的Java栈,互不干扰。
堆:存放成员变量
抽象类与接口区别
相同点:都不能进行实例化
不同点:
抽象类:含有抽象方法的类一定是抽象类,抽象类可以没有抽象方法
抽象类中有变量、方法,构造函数,但是构造函数不能实例,主要是被子类调用
抽象类的抽象方法修饰权限只能是public或protected,默认是public
子类继承抽象类,必须重写抽象类中的所有抽象方法,如果不全部进行重写,子类必须是抽象类
接口:接口中可以包含方法、变量,变量被隐士的定义为public static final,方法为public abstract
接口支持多继承,多实现
jdk1.8的时候,加入了default方法,定义了default方法可以不被子类实现,但只能被实现的子类的对象调用,如果子类实现了多个接口,并且这些接口都有共同的一个默认的方法,那么子类必须重写这个方法。static方法,成为静态方法,这个方法只能通过接口名点这个方法调用
异常
Error(错误)
定义:Error 类及其子类。程序中无法处理的错误,表示运行应用程序中出现了严重的错误。
特点:此类错误一般表示代码运行时 JVM 出现问题。通常有 Virtual MachineError(虚拟机运行错误)、NoClassDefFoundError(类定义错误)等。比如 OutOfMemoryError:内存不足错误;StackOverflowError:栈溢出错误。此类错误发生时,JVM 将终止线程。
这些错误是不受检异常,非代码性错误。因此,当此类错误发生时,应用程序不应该去处理此类错误。按照Java惯例,我们是不应该实现任何新的Error子类的!
Exception(异常)
运行时异常(RuntimeException)
特点:Java 编译器不会检查它。也就是说,当程序中可能出现这类异常时,倘若既"没有通过throws声明抛出它",也"没有用try-catch语句捕获它",还是会编译通过。比如NullPointerException空指针异常、ArrayIndexOutBoundException数组下标越界异常、ClassCastException类型转换异常、ArithmeticExecption算术异常。此类异常属于不受检异常,一般是由程序逻辑错误引起的,在程序中可以选择捕获处理,也可以不处理。虽然 Java 编译器不会检查运行时异常,但是我们也可以通过 throws 进行声明抛出,也可以通过 try-catch 对它进行捕获处理。如果产生运行时异常,则需要通过修改代码来进行避免。例如,若会发生除数为零的情况,则需要通过代码避免该情况的发生!
编译时异常
特点: Java 编译器会检查它。如果程序中出现此类异常,比如 ClassNotFoundException(没有找到指定的类异常),IOException(IO流异常),要么通过throws进行声明抛出,要么通过try-catch进行捕获处理,否则不能通过编译。在程序中,通常不会自定义该类异常,而是直接使用系统提供的异常类。该异常我们必须手动在代码里添加捕获语句来处理该异常。
重写与重载的区别
- 相同:
- 两者都是有多态的作用
- 都可以让同名方法,有不同的实现
- 并且让java自动根据情况调用
- 让程序员更多的关注业务逻辑,而不是实现细节
- 区别:
- 确定方法的方式
- 重载根据参数
- 重写根据调用对象的真实数据类型
- 范围不同
- 重载是同类的同名方法
- 重写是不同类
- 实现不同
- 重载 两同三不同
方法名相同、类相同
参数个数不同、参数顺序不同、参数类型不同
- 重写 两同一不同,两小一大
方法名相同、参数相同、不同的类
返回的数据类型变小、异常变小
权限符可以变大
run和start的区别
1) start:
用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法 run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。
2) run:
run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。
总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。这两个方法应该都比较熟悉,把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用 run()方法,这是由jvm的内存机制规定的。并且run()方法必须是public访问权限,返回值类型为void。
final、finally、finalize
final表示最终、最后的意思
(1)final修饰基本数据类型,这个变量就会变为一个常量,存储到方法区的常量池中
(2)final修饰引用数据类型,表示引用数据类型的地址是不能变的,但是它里面的值是可以改变的
(3)final修饰类,表示这个类是不能被继承的,是一个最终的类
(4)final修饰方法,表示这个方法是不能被重写的
finally是try...catch中的,finally放在catch后面,无论如何都会被执行,除非遇到System.exit(0),它经常被用来关闭IO流
finalize是Object中的方法,表示在垃圾回收器将对象从内存中清理出去
string、stringbuffer、stringbuilder
string可以理解为是线程安全的,查看它的内部代码,就会发现它是被final修饰的,我们都知道,被final修饰的变量是不可变的
当你对string产生一个新的对象时,他就会在内存中开辟一个新的空间来存储,然后指向这个新的对象,所以,进行大量的拼接操作时,就会产生很多的对象,影响工作的性能
Stringbuffer是线程安全的,Stringbuilder是线程不安全的,他们两个都是在自己的对象本身上进行修改的,不能产生新的内存
IOC、AOP
IOC控制反转
控制反转的一种方法是DI(依赖注入),以前我们都是自己创建对象,“反转”就是将对象的控制权交给容器,通过容器来实现对象的管理,我认为所谓的控制反转就是就对象的控制权转移,从代码本身移动到容器中
AOP面向切面编程
AOP是OOP的延续,AOP可以对业务逻辑的各个部分进行隔离,从而使业务逻辑之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
散布在各个代码中,其实是针对同一个功能的代码就是切面
就是不改变源代码,增加业务逻辑
典型的装饰者模式,给代码家外挂
AOP实现的原理
利用继承方式,在子类实现增强功能
利用接口形式,使用代理方式实现增强功能
advice(通知)
@Before 前置通知
@After 后置通知
@AfterReturning 返回通知
@AfterThrowing 异常通知
@Around 环绕通知
AOP的应用场景:
1.日志处理
2.用户登录
3.权限(Authentication )
4.性能优化(Performance optimization)
5.事务(Transactions )
6.记录跟踪 优化 校准(logging, tracing, profiling and monitoring)
7.调试(Debugging)
8.懒加载(Lazy loading)
9.错误处理(Error handling)
10.资源池(Resource pooling)
11.同步(Synchronization)
JSP动作
JSP动作包括:
jsp:include:在页面被请求的时候引入一个文件。
jsp:useBean:寻找或者实例化一个JavaBean。
jsp:setProperty:设置JavaBean的属性。
jsp:getProperty:输出某个JavaBean的属性。
jsp:forward:把请求转到一个新的页面。
jsp:plugin:根据浏览器类型为Java插件生成OBJECT或EMBED标记。
GET和POST的区别
1. GET在浏览器回退时是无害的,而POST会再次提交请求。
2. GET产生的URL地址可以被Bookmark,而POST不可以。
3. GET请求会被浏览器主动cache,而POST不会,除非手动设置。
4. GET请求只能进行url编码,而POST支持多种编码方式。
5. GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
6. get方式提交数据的大小(一般来说1024字节),http协议并没有硬性限制,而是与浏览器、服务器、操作系统有关,而POST理论上来说没有大小限制,http协议规范也没有进行大小限制,但实际上post所能传递的数据量根据取决于服务器的设置和内存大小。
7. 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
8. GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
9. GET参数通过URL传递,POST放在Request body中。
单例模式
指一个类只能有一个实例,且该类能自行创建一个实例
三个必须的条件
(1)通过一个私有的变量存储唯一实例
(2)构造方法私有,这样创建对象的权限才能控制在自己手里,从而其他类不能实例
(3)定义一个公开的静态方法,使得外部的使用者访问这个类的实例
实现方法
饿汉模式和懒汉模式
饿汉模式:有点饿了,所以就需要先进行实例化
优点:它的线程安全,获取对象时不需要加锁
缺点:不是延时加载
懒汉模式:自己太懒了,不想实例,就交给static方法去实例
优点:通过加锁线程安全,延时加载
缺点:因为通过加锁,影响了并发度
list、set、map
集合分为两大类
单列集合、双列集合
单列集合(Collection)又分为List(ArrayList、LinkedList、Vector)和Set(HashSet、LinkedHashSet、TreeSet)
双列集合(Map)又分为HashMap、TreeMap、Hashtable、Properties
ArrayList线程是不安全的、Vector线程是安全的、LinkedList线程是不安全的、Map里面的Hashtable、Properties线程是安全的
ArrayList底层是以数组实现的,就意味着它查询快,增删慢
LinkedList实现了双向链表和双端队列的特点
可以添加任意元素,包括null
线程是不安全的,没有实现同步
vector底层也是以数组实现的,只是它和ArrayList的扩容机制不一样,它的默认扩容是一倍的增长,ArrayList是50%的增长,这样来说,ArrayList更节省内存空间
HashSet
它的底层是数组+链表+红黑树(jdk1.8以后)生成的
LinkedHashSet继承自HashSet,它的添加、删除、查询等方法都是直接用的HashSet的,唯一的不同就是它使用LinkedHashMap存储元素
(1)LinkedHashSet的底层使用LinkedHashMap存储元素。(数组+双向链表+红黑树)
(2)LinkedHashSet是有序的,它是按照插入的顺序排序的。
(3)LinkedHashSet是不支持按访问顺序对元素排序的,只能按插入顺序排序
TreeSet 是一个有序的集合(红黑树),它的作用是提供有序的Set集合。它继承于AbstractSet抽象类,实现了NavigableSet<E>, Cloneable,
和HashMap一样,Hashtable 也是一个散列表,它存储的内容是键值对(key-value)映射。
LinkedHashMap是HashMap的子类,与HashMap有着同样的存储结构,但它加入了一个双向链表的头结点,将所有put到LinkedHashmap的节点一一串成了一个双向循环链表,因此它保留了节点插入的顺序,可以使节点的输出顺序与输入顺序相同。
LinkedHashMap可以用来实现LRU算法
LinkedHashMap同样是非线程安全的,只在单线程环境下使用。
TreeMap是一个有序的key-value集合,它是通过红黑树实现的。
表示一个持久的集,可以存在流中或者从流中加载。用来读取Java的配置文件,在Java中为.properties为后缀名的文本文件。
是 Hashtable子类,map集合方法都可以用。
常用的几个聚合函数
max()
min()
avg()
count()
sum()
左连接 右连接 自然连接
左连接:左表中的左右数据都显示,右表中没有的数据以null显示
右连接:右表中的左右数据都显示,左表中没有的数据以null显示
自然连接会自动判断,以两个表中相同的字段为连接条件,返回查询结果。
死锁产生的原因和解决方法
死锁:是由于两个或两个以上的线程互相持有对方需要的资源,导致这些线程处于等待状态,无法执行
四个必要条件
互斥性:线程对资源的占有是排他性的,一个资源只能被一个线程占有,直到释放
请求和保持条件:一个线程对请求占有资源发生阻塞时,对已经获得的资源不释放
不剥夺:一个线程在释放资源之前,其他的线程无法博导占用
循环等待:发生死锁时,线程进入死循环,永久阻塞
产生死锁的原因
1.竞争不可抢占性资源
2.竞争可消耗资源引起死锁
3.进程推进顺序不当
避免死锁的方法
破坏“请求和保持”条件
破坏“不可抢占”条件
破坏“循环等待”条件
==和equals的区别
==是运算符,比较的是两个基本数据类型的值是否相同
比较对象或者引用数据类型的时候比的是地址是否相同
equals是Object的方法,用于比较两个对象是否相同,String和Integer重写了equals方法,所以,默认比较的是引用类型的值是否相同。
如果是其他引用数据类型,分情况来说,没有重写equals方法,那么equals就和==相同,比较的是地址是否相同。如果重写了equals方法,那么比较的就是两个对象的值是否相同。
23种设计模式
单例模式:某个类只能有一个实例,提供一个全局的访问点。
简单工厂:一个工厂类根据传入的参量决定创建出那一种产品类的实例。
工厂方法:定义一个创建对象的接口,让子类决定实例化那个类。
抽象工厂:创建相关或依赖对象的家族,而无需明确指定具体类。
建造者模式:封装一个复杂对象的构建过程,并可以按步骤构造。
原型模式:通过复制现有的实例来创建新的实例。
适配器模式:将一个类的方法接口转换成客户希望的另外一个接口。
组合模式:将对象组合成树形结构以表示“”部分-整体“”的层次结构。
装饰模式:动态的给对象添加新的功能。
代理模式:为其他对象提供一个代理以便控制这个对象的访问。
亨元(蝇量)模式:通过共享技术来有效的支持大量细粒度的对象。
外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。
桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化。
模板模式:定义一个算法结构,而将一些步骤延迟到子类实现。
解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。
策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。
状态模式:允许一个对象在其对象内部状态改变时改变它的行为。
观察者模式:对象间的一对多的依赖关系。
备忘录模式:在不破坏封装的前提下,保持对象的内部状态。
中介者模式:用一个中介对象来封装一系列的对象交互。
命令模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。
访问者模式:在不改变数据结构的前提下,增加作用于一组对象元素的新功能。
责任链模式:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。
迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。
反射
反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个类,都能够通过调用它的方法改变它的属性。
通过反射可以降低代码的耦合度,还有动态代理的实现
JDBC连接数据库时使用Class.forName()通过反射加载数据库的驱动程序
Spring框架中也有很多用到反射,通过xml配置模式装载bean的过程
九大内置对象
内置对象名称 | 真实类型 | 功能 |
---|---|---|
request | HttpServletRequest | 1.接收参数 2.操作属性 3.跳转页面 4.得到session对象 |
session | HttpSession | 1.操作属性 2.得到application对象 |
application | ServletContext | 1.操作属性 2.获得应用程序级初始化参数 |
config | ServletConfig | 1.获得Servlet初始化对象 2.得到application对象 |
response | HttpServletResponse | 1.得到out对象 2.可以跳转(重定向)页面 3.向浏览器发送cookie |
pageContent | pageContent | 1.操作属性(4个范围) 2.得到其他的8个内置对象 |
out | PrintWriter | 重写了print方法 |
Exception | Throwable | 处理异常 |
page | Object | 相当于this |
堆内存溢出
1.JVM内存划分为堆内存和非堆内存,堆内存分为年轻代(Young Generation)、老年代(Old Generation),非堆内存就一个永久代(Permanent Generation)。
2.年轻代又分为Eden和Survivor区。Survivor区由FromSpace和ToSpace组成。Eden区占大容量,Survivor两个区占小容量,默认比例是8:1:1。
3.堆内存用途:存放的是对象,垃圾收集器就是收集这些对象,然后根据GC算法回收。
4.非堆内存用途:永久代,也称为方法区,存储程序运行时长期存活的对象,比如类的元数据、方法、常量、属性等。
在JDK1.8版本废弃了永久代,替代的是元空间(MetaSpace),元空间与永久代上类似,都是方法区的实现,他们最大区别是:元空间并不在JVM中,而是使用本地内存。
元空间有注意有两个参数:
MetaspaceSize :初始化元空间大小,控制发生GC阈值
MaxMetaspaceSize : 限制元空间大小上限,防止异常占用过多物理内存
为什么会堆内存溢出?
在年轻代中经过GC后还存活的对象会被复制到老年代中。当老年代空间不足时,JVM会对老年代进行完全的垃圾回收(Full GC)。如果GC后,还是无法存放从Survivor区复制过来的对象,就会出现OOM(Out of Memory)。
OOM(Out of Memory)异常常见有以下几个原因:
1)老年代内存不足:java.lang.OutOfMemoryError:Javaheapspace
2)永久代内存不足:java.lang.OutOfMemoryError:PermGenspace
3)代码bug,占用内存无法及时回收。
堆内存划分:
堆大小 = 新生代 + 老年代。堆的大小可通过参数–Xms(堆的初始容量)、-Xmx(堆的最大容量) 来指定。
其中,新生代 ( Young ) 被细分为 Eden 和 两个 Survivor 区域,这两个 Survivor 区域分别被命名为 from 和 to,以示区分。默认的,Edem : from : to = 8 : 1 : 1 。(可以通过参数 –XX:SurvivorRatio 来设定 。
即: Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。
JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块 Survivor 区域是空闲着的。
新生代实际可用的内存空间为 9/10 ( 即90% )的新生代空间。
栈内存溢出
是否存在大量的递归调用
是否存在大量的循环或死循环
全局变量是否过多
数组、list、map是否过多
SSM框架的常用注解
MyBatis
@Results:声明映射关系的配置Value属性接受@Result的数组
@Result:配置映射关系
id属性声明是否为主键配置
Property属性对象中的属性名
Column属性查询你的字段名
Spring
创建当前对象交给spring容器管理的注解
@Component(value=“id标识”)
@Controller(value=“id标识”)web层
@Service(value=“id标识”)service层
@Repository(value=“id标识”)dao层
说明需要配置到类上Value属性指定唯一标识
属性依赖注入的注解
@Autowired
@Qualifier(value=“id标识”)value书序可以按照id唯一标识注入
@Value
生命周期相关的注解
@Scope 对象的作用Value属性
@PsotConstruct 配置到方法上 用来配置初始化方法
@PreDestory 配置到方法上用来配置销毁方法
springTest的相关注解
@Runwith(SpringJunit4ClassRunner.class)声明spring提供的类加载配置文件
@ContextConfiguration声明spring的配置信息
AOP相关的注解
@Aspect声明切面类
@PointCut定义公共的切入点 配置到空方法上
value属性切入点表达式
配置通知类型
@Before前置通知
@AfterReturnint 后置通知
@AfterThrowing 异常通知
@After 最终通知
@Around 环绕通知
springMVC
@RequestMapping("/user") 做浏览的访问路径和当前方法的映射
@RequestHeader 获取到请求头的信息
@CookieValue 获取到cookie的jsessionID
@RequestBody 配置到方法参数上,表明将json字符串转化为java对象
@ResponseBody 配置到方法返回值,表明将对象转化为json字符串
@SessionAttributes(value = {"username"}) //代表当前类中的所有方法 只要是model对象操作了指定的参数 都会向session域中存一份
@ModelAttribute("aaa") 向Model中添加元素
springboot
服务端框架
springboot是springMVC的竞品
springMVC的缺点
配置多
依赖管理复杂
部署麻烦
springboot的特点
1.独立运行的spring项目
2.内嵌servlet容器
3.提供starter简化Maven配置
4.提供了大量的自动配置
5.自带的应用监控
6.无代码生成和xml配置
springboot相对于spring来说有什么优点
(1)全部采用注解方式,没有繁琐的xml配置
(2)内置http服务器,比如Tomcat,不需要额外的去集成下载Tomcat
(3)快速整合第三方框架,比如Redis、mybatis(可以理解为自动配置)
springboot常用注解
@springbootApplication:这个注解是Spring Boot最核心的注解,用在 Spring Boot的主类上,标识这是一个 Spring Boot 应用,用来开启 Spring Boot 的各项能力。实际上这个注解是@Configuration,@EnableAutoConfiguration,@ComponentScan三个注解的组合
@EnableAutoConfiguration:允许 Spring Boot 自动配置注解,开启这个注解之后,Spring Boot 就能根据当前类路径下的包或者类来配置 Spring Bean
@Configuration:用于定义配置类,指出该类是 Bean 配置的信息源,相当于传统的xml配置文件,一般加在主类上
@ComponentScan:组件扫描。让spring Boot扫描到Configuration类并把它加入到程序上下文.@ComponentScan注解默认就会装配标识了@Controller,@Service,@Repository,@Component注解的类到spring容器中。
@Repository:用于标注数据访问组件,即DAO组件
@Service:用于修饰service层的组件
@Component:把普通pojo实例化到spring容器中,泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注
@Controller:用于标注控制层组件
@RestController —用于标注控制层组件:相当于@Controller和@ResponseBody
@Bean:相当于XML中的,放在方法的上面,而不是类,意思是产生一个bean,并交给spring管理
@AutoWired:byType方式。把配置好的Bean拿来用,完成属性、方法的组装,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作
@Qualifier:当有多个同一类型的Bean时,可以用@Qualifier(“name”)来指定。与@Autowired配合使用
@Resource():默认byName。与@Autowired干类似的事
@RequsetMapping——-RequestMapping是一个用来处理请求地址映射的注解@GetMapping+@PostMapping+@PutMapping+@DeleteMapping等等:
@Param:用在方法的参数前面,一般只有一个参数的时候可以考虑不用
@RequestParam:用在方法的参数前面
@PathVariable:路径变量。参数与大括号里的名字一样要相同
@ConfigurationProperties:Spring Boot可使用注解的方式将自定义的properties文件映射到实体bean中,比如config.properties文件@ConfigurationProperties(prefix="...")
微服务架构
微服务架构是一种架构概念,它的主要作用是将各个功能分解到离散的服务器当中,从而降低系统的耦合性别,并提高更加灵活的服务支持
本质:用一些比较明确,比较强大的服务去解决更大、更实际的问题
传统开发模式与微服务的区别
传统开发模式
优点:
开发简单,集中式管理
基本不回重复开发
功能都在本地,没有分布式的管理和消耗
缺点:
效率低:开发都在同一个项目中,相互等待,冲突不断
维护难:代码功能耦合在一起,新手不知道从何下手
不灵活:构建时间长,任何修改都要重构项目,耗时
稳定性差:一个微小的问题,可能导致整个系统的崩塌
扩展性不够:无法满足高并发的业务需求
常见的系统架构遵循的三个标准和业务驱动力
提高敏捷性:及时响应业务需求,促进企业发展
提升用户体验:提升用户体验,减少用户流失
降低成本
微服务架构
具体特征
官方的定义:
一些独立的服务共同组成的系统
单独部署,跑在自己的进程中
每个服务为独立的业务开发
分布式管理
非常强调隔离性
大概的标准
分布式服务组成的系统
按照业务,而不思技术划分
做有生命的产品而不是项目
强服务个体和弱通信
自动化运维
高度容错性
快速演化和迭代
MyBatis #{}和${}的区别是什么
#{}是预编译处理,${}是字符串替换;
Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
Mybatis在处理${}时,就是把${}替换成变量的值;
使用#{}可以有效的防止SQL注入,提高系统安全性。
说说什么是Mybatis?
Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,加载驱动、创建连接、创建statement等繁杂的过程,开发者开发时只需要关注如何编写SQL语句,可以严格控制sql执行性能,灵活度高。
作为一个半ORM框架,MyBatis 可以使用 XML 或注解来配置和映射原生信息,将 POJO映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集
MyBatis是一款优秀的持久层框架
MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集的过程
MyBatis可以使用简单的XML或注解来配置和映射原生信息,将接口和Java的实体类映射成数据库中的记录
持久化
持久化是将程序数据在持久状态和瞬时状态间转化的机制
即把数据(如内存中的对象)保存到永久保存的储存设备中(如磁盘),持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、xml数据文件中等等
JDBC就是一种持久化机制,文件IO也是一种持久化机制
为什么需要MyBatis
MyBatis就是帮助程序猿将数据存入数据库中,和从数据库中取数据
传统的jdbc操作,有很多重复代码块,比如L数据取出时的封装,数据库的建立连接等等,通过框架可以减少重复代码,提高开发效率
MyBatis是一个半自动化的ORM框架(Object Relationship Mapping)---> 对象关系映射
技术没有高低之分,只有使用这个技术的人高低之别
MyBatis的优点
简单易学:本身就很小且简单,没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件就可以了,易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现
灵活:MyBatis不会对应用程序或者数据库的现有设计强加任何影响,sql写在xml里、便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求
解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰。更易维护,更易单元测试。sql和代码的分离,提高了可维护性
一级缓存二级缓存
缓存:存在内存中的临时数据,将用于经常查询的数据放在缓存中,用户查询数据就不用去磁盘上读数据而是从缓存中直接取数据,从而提高了查询效率,解决了高并发系统的性能问题
为什么使用缓存
减少和数据库的交互,减少系统开销,提高系统效率
当我们经常需要查询的数据,就可以使用缓存
缓存失效的情况
使用更新、删除、添加数据的时候,缓存会更新,即失效
关闭连接会失效
手动清理缓存
不同Mapper文件
一级缓存:又叫本地缓存,与数据库同一次会话期间的数据会被放在本地缓存中,以后需要这些数据就可以直接在缓存中拿,不需要进入数据库查询,关闭SQLsession的时候,一级缓存就结束
二级缓存:又叫全局缓存,基于namespace级别的缓存
工作机制:
一个会话查询到一条数据,这个数据就会被存放
如果会话结束了,一级缓存也就没了,但我们想要的是,一级缓存结束后,数据会保存到二级缓存中
新的会话读取内容时,就从二级缓存中获取
不同的mapper查处的数据会被放在自己的缓存中
注意点:二级换粗是在一级缓存结束后才将数据存放到二级缓存
缓存的执行原理(顺序):1.先去二级缓存中查找相应的数据,如果有,就直接取出 2.二级缓存没有,就去一级缓存查找,有就直接取出
3.如果一级缓存没有,就去数据库查找,然后将查询到的数据放到一级缓存中,一级缓存一旦关闭就放在二级缓存中
区别:一级缓存作用域SQLsession内,二级缓存作用域是针对mapper进行缓存
Spring框架中用到了哪些设计模式?
1.工厂设计模式:Spring使用工厂模式通过BeanFactory和ApplicationContext创建bean对象。
2.代理设计模式:Spring AOP功能的实现。
3.单例设计模式:Spring中的bean默认都是单例的。
4.模板方法模式:Spring中的jdbcTemplate、hibernateTemplate等以Template结尾的对数据库操作的类,它们就使用到了模板模式。
5.包装器设计模式:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
6.观察者模式:Spring事件驱动模型就是观察者模式很经典的一个应用。
7.适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式、Spring MVC中也是用到了适配器模式适配Controller。
还有很多。。。。。。。
Spring有哪些模块
spring context:继承BeanFactory,提供上下文信息
spring Core:框架的最基础部分,提供IOC容器,对bean进行管理,它的主要组件就是BeanFactory,是工厂模式的实现
spring AOP:继承了所有AOP功能那个,减少代码的功能耦合,清晰的被分离开
spring web:提供了基本的面向web的综合特性
spring MVC:提供了基本的面向web的Model-View-Controller
spring Dao:提供了JDBC的抽象层,还提供了声明事务管理方法
Spring ORM:提供了JPA、JDO、Hibernate、mybatis等ORM映射层
Spring中切面常用的通知
前置通知 在目标方法执行之前执行
后置通知 在目标方法执行之后执行,不管目标方法有没有抛出异常
返回通知 在目标方法成功返回之后执行, 可以获取到目标方法的返回值
异常通知 在目标方法抛出异常后执行
环绕通知 环绕着目标方法执行
谈谈你对Spring 的理解
spring是一个轻量级的IOC和aop框架,目的是为了解决企业应用开发的业务逻辑层和其他各层之间的耦合以及应用开发的复杂性,简化java开发
Spring 容器的主要核心是:
控制反转(IOC),传统的 java 开发模式中,当需要一个对象时,我们会自己使用 new 或者 getInstance 等直接或者间接调用构造方法创建一个对象。而在 spring 开发模式中,spring 容器使用了工厂模式为我们创建了所需要的对象,不需要我们自己创建了,直接调用spring 提供的对象就可以了,这是控制反转的思想。
依赖注入(DI),spring 使用 javaBean 对象的 set 方法或者带参数的构造方法为我们在创建所需对象时将其属性自动设置所需要的值的过程,就是依赖注入的思想。
面向切面编程(AOP),在面向对象编程(oop)思想中,我们将事物纵向抽成一个个的对象。而在面向切面编程中,我们将一个个的对象某些类似的方面横向抽成一个切面,对这个切面进行一些如权限控制、事物管理,记录日志等公用操作处理的过程就是面向切面编程的思想。AOP 底层是动态代理,如果是接口采用 JDK 动态代理,如果是类采用CGLIB 方式实现动态代理。
能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
谈谈你对SpringMVC 的理解
Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过把Model,View,Controller分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。
MySQL三范式
**第一范式**:要求任何一张表必须有主键,每一个字段原子性不可再分。
**第二范式**:建立在第一范式之上,要求所有的非主键字段必须完全依赖主键,不能产生部分依赖。
**第三范式**:建立在第二范式之上,要求所有的非主键字段必须直接依赖主键,不能产生传递依赖。
**多对多,关系表,两个外键**
**一对多,两张表,多的表加外键**
MySQL索引
单列索引:一个索引只包含单个列,但一个表中可以有多个单列索引。 这里不要搞混淆了。
普通索引:MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值,纯粹为了查询数据更快一点。
唯一索引:索引列中的值必须是唯一的,但是允许为空值,
主键索引:是一种特殊的唯一索引,不允许有空值。
组合索引:在表中的多个字段组合上创建的索引,只有在查询条件中使用了这些字段的左边字段时,索引才会被使用,使用组合索引时遵循最左前缀集合
全文索引:全文索引,只有在MyISAM引擎上才能使用
空间索引:空间索引是对空间数据类型的字段建立的索引,MySQL中的空间数据类型有四种,GEOMETRY、POINT、LINESTRING、POLYGON。
普通索引:仅加速查询
唯一索引:加速查询 + 列值唯一(可以有null)
主键索引:加速查询 + 列值唯一 + 表中只有一个(不可以有null)
组合索引:多列值组成一个索引,
专门用于组合搜索,其效率大于索引合并
全文索引:对文本的内容进行分词,进行搜索
B+树
它的关键字的数量是跟路数相等的;
B+Tree的根节点和枝节点中都不会存储数据,只有叶子节点才存储数据。InnoDB 中 B+ 树深度一般为 1-3 层,它就能满足千万级的数据存储。搜索到关键字不会直接返回,会到最后一层的叶子节点。比如我们搜索id=28,虽然在第一层直接命中了,但是全部的数据在叶子节点上面,所以我还要继续往下搜索,一直到叶子节点。
B+Tree的每个叶子节点增加了一个指向相邻叶子节点的指针,它的最后一个数据会指向下一个叶子节点的第一个数据,形成了一个有序链表的结构。
优点:
扫库、扫表能力更强(如果我们要对表进行全表扫描,只需要遍历叶子节点就可以了,不需要遍历整棵B+Tree拿到所有的数据)
B+Tree的磁盘读写能力相对于B Tree来说更强(根节点和枝节点不保存数据区,所以一个节点可以保存更多的关键字,一次磁盘加载的关键字更多)
排序能力更强(因为叶子节点上有下一个数据区的指针,数据形成了链表)
效率更加稳定(B+Tree永远是在叶子节点拿到数据,所以IO次数是稳定的)
MySQL引擎
MyIsam
MyIsam 存储引擎独立于操作系统,简单说就是可用在windows上使用,也可用将数据转移到Lunex操作系统上。系统兼容性很好!!!。这种存储引擎在建表的时候,它会创建3个文件。分别是(.frm, .MYD, .MYI),简单说明一下:.frm 存储表的定义(也就是表结构啦),.MYD 就是表里面的数据,.MYD存储索引。这样的划分操作系统对大文件的操作是比较慢的,这样将表分为三个文件,那么.MYD这个文件单独来存放数据自然可以优化数据库的查询等操作。
特点:
1、不支持事务
2、不支持外键
3、查询速度很快。如果数据库insert和update的操作比较多的话采用表锁效率低(建议使用innodb)。
4、对表进行加锁
InnoDB
InnoDB是一个事务型的存储引擎,有行级锁定和外键约束,提供了具有提交,回滚和崩溃恢复的事务安全,但是对比MyLSAM引擎,写的效率会比差一些,并且会占用更多的磁盘空间以保持数据和索引。
特点:
1. 更新多的表,适合处理多重并发的更新请求。
2. 支持事务。
3. 可以从灾难中恢复(通过bin-log日志等)。
4. 外键约束。只有他支持外键。
5. 支持自动增加列属性auto_increment。
Memory
Memory 采用的方案式使用内存当存储介质,优点式响应速度快。但存储到内存上也导致了一个致命的问题就是当mySql进程崩溃的时数据会丢失。此外Memory对存储的数据有一定的要求,要求存储的是长度不变的数据。
Memory索引支持
散列索引:散列索引的使用场景是数据查找时使用 == 匹配,但范围查询(<=, >=, <, >)较慢
B树索引:B树索引可以使用部分查询和通配查询,范围查询较快
Memory使用场景:
1、数据量小、访问非常频繁、在内存中存放数据,数据量过大会导致内存溢出。可以通过参数max_heap_table_size控制Memory表的大小,限制Memory表的最大的大小。
2、数据是临时数据,而且立即可用到。那么就比较合适存放在内存中。
3、存储在表中的数据如果丢失也没太大关系,不会造成损失。
MySQL中char和varchar的区别
char | varchar |
---|---|
可以不写长度默认为1 | 必须写长度 |
不管存储的是多少,实际的存储空间就是定义的数据长度 | 占用实际的空间 |
最大长度255 | 最大长度65532 |
四个隔离级别
1.读未提交(READ UNCOMMITTED)
问题:可能读到脏数据
一个事务中对数据所做的修改,即使没有提交,这个修改对其他的事务仍是可见的,这种情况下就容易出现脏读,影响了数据的完整性.
2.读已提交(READ COMMITTED)
解决了脏读
问题:不可重复读取数据。
一个事务开始时,只能看见其他已经提交过的事务。这种情况下容易出现不可重复读 (两次读的结果不一样).即不可重复读
3.可重复读(REPEATABLE READ)
可能出现幻读
多次读取记录的结果都是+一致的,可重复读可以解决上面的不可重复读的情况。但是有这样一种情况,当一个事务在读取某个范围的记录时,另外一个事务在这个范围内插入了一条新的数据,当事务再次进行读取数据时,发现比第一次读取记录多了一条,这就是所谓的幻读,两次读取的结果不一致.
4.序列化/串行化(Serializable)
串行就像一个队列一个样,每个事务都是排队等候着执行,只有前一个事务提交之后,下一个事务才能进行操作。这种情况虽然可以解决上面的幻读,但是他会在每一条数据上加一个锁,容易导致大量的锁超时和锁竞争,特别不适用在一些高并发的业务场景下.
最高的隔离级别,效率最低,解决了所有问题
这种隔离级别表示事务排队,不能并发!
事务的四大特性
1. A(atom):原子性:一个事务必须被作为一个不可分割的最小工作单元,每个事务中的所有操作必须要么成功,或者要么失败,永远不可能一些操作失败,一些操作成功,这就是所谓的原子性的概念.
2. C(consistent):一致性:指事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态所有事务要求。就是说当一个事务执行失败了,数据之间是不会受异常的情况而影响,永远保持着他的正确性.
3. I(isolate):隔离性:A事务和B事务之间具有一定的隔离。
4. D(durability):持久性:事务最终结束的一个保障。事务提交,就相当于将没有保存到硬盘上的数据保存到硬盘上。
sql调优的方式
查询sql语句尽量少用select *,应该是具体字段
避免在where子句中使用or连接
使用varchar代替char
优化like语句
索引不建在大量重复数据的字段上
where限定查询的数据
避免在where子句中使用表达式操作
去重distinct过滤字段要少
缩影不能太多,一般少于五个
IN和EXISTS的用法和区别
EXISTS:EXISTS对外表用loop逐条查询,每次查询都会查看EXISTS条件语句(子查询语句),条件语句中能够返回记录行则条件就为真,外表的这条记录就会被加入结果集,如果EXISTS里的子查询不能返回记录行,则当前loop到的这条记录就会被丢弃。需要注意的是,EXISTS里的子查询是可以使用索引进行查询的。
IN:IN相当于多个OR条件的叠加,总的来说,IN就是先将子查询条件的记录都查出来缓存到临时表中,这里可以假设记录集为多个OR条件的叠加。因此可以将这多个OR条件的叠加用来执行原查询,这里原查询也是可以使用索引查询的。MySQL中的IN是把外表和内表做哈希连接来进行查询。哈希连接的意思是,把两个表中较小的那个表(一般情况下是较小的那个表,以减少建立哈希表的时间和空间),对其中每个元组上的连接属性采用哈希函数得到哈希值,从而建立一个哈希表。对另一个表,扫描它的每一行并计算连接属性的哈希值,与前面建立的哈希表对比,哈希值相等的生成结果表。
EXISTS和IN的区别
假设现在查询语句为 select * from A where exists (select * from B where B.id = A.id); select * from A where A.id in (select id from B);
EXISTS可以对B表使用索引查询,而IN则先查出子查询的结果,然后对A表可以使用索引进行查询。因此可以看出在索引条件都相等的情况下,EXISTS适合B表较大的查询,而IN则适合A表较大的查询。
IN子查询中返回结果必须只有一个字段,而EXISTS则没有这个限制。
创建对象的四个方式
1.使用new关键字
2.通过反射机制,使用的是newInstance()方法
3.克隆clone(浅克隆和深克隆)
4.序列化(将一个对象序列化到磁盘上,再通过反序列化将对象信息转化到内存当中)
同步和异步
异步:同一个进程内,多个线程之间有互斥关系,只有等一个线程执行完毕才能执行另一个进程
同步:多个线程同时运行同一个方法,因为方法上加了同步锁(synchronized),一次只能一个线程执行,其他线程处于竞争状态
线程池的创建
如果线程池的线程数量很多,并且每个线程都是执行一次就结束了,这样频繁创建线程就会导致系统效率降低,通过线程池就可以使线程不被销毁,继续执行其他的线程
方法
-
newCachedThreadPool
-
newCachedThreadPool:不限制线程数量的线程池,线程数量大小和操作系统相关 具有以下特征: (1)线程池中数量没有固定,可达到最大值(Interger. MAX_VALUE) (2)线程池中的线程可进行缓存重复利用和回收(回收默认时间为1分钟) (3)当线程池中,没有可用线程,会重新创建一个线程 (4)使用的SynchronousQueue队列 public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
-
newFixedThreadPool
-
newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。 线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。 特征: (1)线程池中的线程处于一定的量,可以很好的控制线程的并发量 (2)线程可以重复被使用,在显式关闭之前,都将一直存在 (3)超出一定量的线程被提交时候需在队列中等待 (4)使用的LinkedBlockingQeque,创建时候使用的是new LinkedBlockingQeque(Integer.MAX_VALUE),所以队列是无限大的 创建方式: (1)Executors.newFixedThreadPool(int nThreads);//nThreads为线程的数量 (2)Executors.newFixedThreadPool(int nThreads,ThreadFactory threadFactory);//nThreads为线程的数量,threadFactory创建线程的工厂方式 创建源码: public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
-
newSingleThreadExecutor
-
newSingleThreadExecutor :创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。 特征: (1)线程池中最多执行1个线程,之后提交的线程活动将会排在队列中以此执行 (2)使用的是 LinkedBlockingQueue 创建方式: (1)Executors.newSingleThreadExecutor() ; (2)Executors.newSingleThreadExecutor(ThreadFactory threadFactory);// threadFactory创建线程的工厂方式 public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
-
newSingleThreadScheduledExecutor
-
newSingleThreadScheduledExecutor创建一个单线程的线程调度器。这个调度器只有一个线程在工作 特征: (1)调度器的线程池中最多执行1个线程,之后提交的线程活动将会排在队列中以此执行 (2)可定时或者延迟执行线程活动 (2)使用的是 DelayedWorkQueue 创建方式: (1)Executors.newSingleThreadScheduledExecutor() ; public static ScheduledExecutorService newSingleThreadScheduledExecutor() { return new DelegatedScheduledExecutorService (new ScheduledThreadPoolExecutor(1)); }
-
newScheduledThreadPool
-
newScheduledThreadPool 创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。 特征: (1)线程池中具有指定数量的线程,即便是空线程也将保留 (2)可定时或者延迟执行线程活动 创建方式: (1)Executors.newScheduledThreadPool(int corePoolSize);// corePoolSize线程的个数 (2)newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory);// corePoolSize线程的个数,threadFactory创建线程的工厂 public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue()); }
-
newWorkStealingPool
-
newWorkStealingPool:这个是 JDK1.8 版本加入的一种线程池,stealing 翻译为抢断、窃取的意思,它实现的一个线程池和上面4种都不一样,用的是 ForkJoinPool 类
队列
ArrayBlockingQueue是一个有界缓存等待队列。可以指定缓存队列的大小,当线程数已经达到最大的maximumPoolSizes时,再有新的元素尝试加入ArrayBlockingQueue时会报错。
LinkedBlockingQueue是一个无界缓存等待队列。当前执行的线程数量达到corePoolSize的数量时,剩余的元素会在阻塞队列里等待。
SynchronousQueue没有容量,是无缓冲等待队列,是一个不存储元素的阻塞队列,会直接将任务交给消费者,必须等队列中的添加元素被消费后才能继续添加新的元素。
二叉树
二叉树是一个联通的无环图,并且每一个定点的度不大于3,有根二叉树还要满足根节点的度不大于2
完全二叉树:设一棵树有n层,除了n层外,其他各层的节点数都是最大个数,且第n层节点都集中在左边
满二叉树:除了最后一层没有节点外,每一层的节点都有两个子节点
平衡二叉树:左右子树两边的高度不超过1
红黑树:特殊的二叉树
根节点必须是黑色的
节点可以是红色或黑色
叶子节点是黑色的
每个红色节点的子节点是黑色的
任何一个节点到每一个子节点上的路径的黑色节点数相同
时间复杂度
应用层的协议
(1)域名系统(Domain Name System,DNS):用于实现网络设备名字到IP地址映射的网络服务。
(2)文件传输协议(File Transfer Protocol,FTP):用于实现交互式文件传输功能。
(3)简单邮件传送协议(Simple Mail Transfer Protocol, SMTP):用于实现电子邮箱传送功能
(4)超文本传输协议(HyperText Transfer Protocol,HTTP):用于实现WWW服务。
(5)简单网络管理协议(simple Network Management Protocol,SNMP):用于管理与监视网络设备。
(6)远程登录协议(Telnet):用于实现远程登录功能。
swagger
集成测试工具
ArrayBlockingQueue是一个有界缓存等待队列。可以指定缓存队列的大小,当线程数已经达到最大的maximumPoolSizes时,再有新的元素尝试加入ArrayBlockingQueue时会报错。
LinkedBlockingQueue是一个无界缓存等待队列。当前执行的线程数量达到corePoolSize的数量时,剩余的元素会在阻塞队列里等待。
SynchronousQueue没有容量,是无缓冲等待队列,是一个不存储元素的阻塞队列,会直接将任务交给消费者,必须等队列中的添加元素被消费后才能继续添加新的元素。
二叉树
二叉树是一个联通的无环图,并且每一个定点的度不大于3,有根二叉树还要满足根节点的度不大于2
完全二叉树:设一棵树有n层,除了n层外,其他各层的节点数都是最大个数,且第n层节点都集中在左边
满二叉树:除了最后一层没有节点外,每一层的节点都有两个子节点
平衡二叉树:左右子树两边的高度不超过1
红黑树:特殊的二叉树
根节点必须是黑色的
节点可以是红色或黑色
叶子节点是黑色的
每个红色节点的子节点是黑色的
任何一个节点到每一个子节点上的路径的黑色节点数相同
时间复杂度
应用层的协议
(1)域名系统(Domain Name System,DNS):用于实现网络设备名字到IP地址映射的网络服务。
(2)文件传输协议(File Transfer Protocol,FTP):用于实现交互式文件传输功能。
(3)简单邮件传送协议(Simple Mail Transfer Protocol, SMTP):用于实现电子邮箱传送功能
(4)超文本传输协议(HyperText Transfer Protocol,HTTP):用于实现WWW服务。
(5)简单网络管理协议(simple Network Management Protocol,SNMP):用于管理与监视网络设备。
(6)远程登录协议(Telnet):用于实现远程登录功能。
swagger
集成测试工具
[外链图片转存中…(img-WWKlAXMY-1645177436294)]