java复习思维导图
2021-05-14 10:03:08 0 举报
梳理近期市场常用java知识点
作者其他创作
大纲/内容
代码管理GIT
服务器软件
常见数据结构
linux
JVM
整体组成
类加载机制
GC垃圾回收
JVM调优
对JVM内存的系统级的调优主要的目的是减少GC的频率和Full GC的次数。
JVM性能调优方法和步骤
1.监控GC的状态
2.生成堆的dump文件
3.分析dump文件
4.分析结果,判断是否需要优化
5.调整GC类型和内存分配
6.不断的分析和调整
JVM调优参数参考
设计模式
设计原则
开闭原则
定义
软件实体应当对扩展开放,对修改关闭,这就是开闭原则的经典定义。
子类可以扩展父类的功能,但不能改变父类原有的功能。
子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。
作用
开闭原则是面向对象程序设计的终极目标,
它使软件实体拥有一定的适应性和灵活性的同时具备稳定性和延续性。
1. 软件测试只需要测试拓展的部分
2. 可以提高代码的可复用性
3. 可以提高软件的可维护性
里氏替换原则
定义
继承必须确保基类所拥有的性质在子类中仍然成立.
作用
1.里氏替换原则是实现开闭原则的重要方式之一。
2.它克服了继承中重写父类造成的可复用性变差的缺点。
3.它是动作正确性的保证。即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性。
依赖倒置原则
定义
要面向接口编程,不要面向实现编程。
依赖倒置的中心思想是面向接口编程。
相对于细节的多变性,抽象的东西要稳定的多,
以抽象为基础搭建的架构比以细节为基础搭建的架构要稳定的多。
作用
依赖倒置原则可以降低类间的耦合性。
依赖倒置原则可以提高系统的稳定性。
依赖倒置原则可以减少并行开发引起的风险。
依赖倒置原则可以提高代码的可读性和可维护性。
单一职责原则
定义
单一职责原则规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分
作用
单一职责原则的核心就是控制类的粒度大小、将对象解耦、提高其内聚性。
降低类的复杂度。一个类只负责一项职责,其逻辑肯定要比负责多项职责简单得多。
提高类的可读性。复杂性降低,自然其可读性会提高。
提高系统的可维护性。可读性提高,那自然更容易维护了。
变更引起的风险降低。变更是必然的,如果单一职责原则遵守得好,当修改一个功能时,可以显著降低对其他功能的影响。
接口隔离原则
定义
要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。
一个类对另一个类的依赖应该建立在最小的接口上。
作用
1.将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
2.接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性。
3.如果接口的粒度大小定义合理,能够保证系统的稳定性;但是,如果定义过小,则会造成接口数量过多,使设计复杂化;
4.能减少项目工程中的代码冗余。过大的大接口里面通常放置许多不用的方法,当实现这个接口的时候,被迫设计冗余的代码。
迪米特法则
定义
如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。
作用
降低了类之间的耦合度,提高了模块的相对独立性。
由于亲合度降低,从而提高了类的可复用率和系统的扩展性。
合成复用原则
定义
在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
作用
模式
单例(Singleton)模式
定义
指一个类只有一个实例,且该类能自行创建这个实例的一种模式,属于创建型模式。
1.单例类只有一个实例对象;
2.该单例对象必须由单例类自行创建;
3.单例类对外提供一个访问该单例的全局访问点
优点
1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例
2、避免对资源的多重占用(比如写文件操作)。
2、避免对资源的多重占用(比如写文件操作)。
缺点
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
实现
懒汉模式
会有线程安全问题,需要synchronized关键字和双重检查
饿汉模式
直接创建static的实例
应用
1.某类只要求生成一个对象的时候
2.当对象需要被共享的场合。
3.当某类需要频繁实例化,而创建的对象又频繁被销毁的时候,
如多线程的线程池、网络连接池等。
2.当对象需要被共享的场合。
3.当某类需要频繁实例化,而创建的对象又频繁被销毁的时候,
如多线程的线程池、网络连接池等。
原型(Prototype)模式
定义
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。
优点
1、性能提高。 2、逃避构造函数的约束。
缺点
1、配备克隆方法需要对类的功能进行通盘考虑. 2、必须实现 Cloneable 接口。
实现
浅克隆
Java 中的 Object 类提供了浅克隆的 clone() 方法
浅克隆直接复制基本类型值,非基本类型属性仍指向原有属性所指向的对象的内存地址
深克隆
深克隆会把引用类型的对应也都复制一份
工厂方法(Factory Method)模式
定义
定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。
这满足创建型模式中所要求的“创建与使用相分离”的特点。
创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
优点
1、一个调用者想创建一个对象,只要知道其名称就可以了。
2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
3、屏蔽产品的具体实现,调用者只关心产品的接口。
缺点
每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,
在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。
实现
主要角色
抽象工厂(Abstract Factory)
具体工厂(ConcreteFactory)
抽象产品(Product)
具体产品(ConcreteProduct)
应用
1.客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。
2.创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
3.客户不关心创建产品的细节,只关心产品的品牌。
2.创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
3.客户不关心创建产品的细节,只关心产品的品牌。
抽象工厂(AbstractFactory)模式
定义
是一种为访问类提供一个创建一组相关或相互依赖对象的接口,
且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
优点
当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
缺点
产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。
建造者(Builder)模式
定义
使用多个简单的对象一步一步构建成一个复杂的对象。
这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。
与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。
这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。
与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。
优点
1、建造者独立,易扩展。 2、便于控制细节风险。
缺点
1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。
实现
建造者(Builder)模式的主要角色如下。
产品角色(Product)
抽象建造者(Builder)
具体建造者(Concrete Builder)
指挥者(Director)
应用
1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖。
代理(Proxy)模式
定义
为其他对象提供一种代理以控制对这个对象的访问。
优点
1、职责清晰。 2、高扩展性。 3、智能化。
缺点
1、由于在客户端和真实主题之间增加了代理对象,
因此有些类型的代理模式可能会造成请求的处理速度变慢。
2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
实现
抽象主题(Subject)类:
通过接口或抽象类声明真实主题和代理对象实现的业务方法。
真实主题(Real Subject)类:
实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
代理(Proxy)类:
提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
应用
1、远程代理。 2、虚拟代理。 3、Copy-on-Write 代理。
4、保护(Protect or Access)代理。 5、Cache代理。
6、防火墙(Firewall)代理。 7、同步化(Synchronization)代理。
8、智能引用(Smart Reference)代理。
适配器(Adapter)模式
定义
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。
这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。
优点
1、可以让任何两个没有关联的类一起运行。
2、提高了类的复用。
3、增加了类的透明度。
4、灵活性好。
缺点
1、过多地使用适配器,会让系统非常零乱,不易整体进行把握。
2、由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。
实现
目标(Target)接口:
当前系统业务所期待的接口,它可以是抽象类或接口。
适配者(Adaptee)类:
它是被访问和适配的现存组件库中的组件接口。
适配器(Adapter)类:
它是一个转换器,通过继承或引用适配者的对象,
把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
应用
1、系统需要使用现有的类,而此类的接口不符合系统的需要。
2、想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类。
3、通过接口转换,将一个类插入另一个类系中。
桥接(Bridge)模式
定义
桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。
它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。
它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。
优点
缺点
实现
应用
装饰(Decorator)模式
外观(Facade)模式
享元(Flyweight)模式
组合(Composite)模式
模板方法(TemplateMethod)模式
策略(Strategy)模式
命令(Command)模式
职责链(Chain of Responsibility)模式
状态(State)模式
观察者(Observer)模式
中介者(Mediator)模式
迭代器(Iterator)模式
访问者(Visitor)模式
备忘录(Memento)模式
解释器(Interpreter)模式
工具类
java基础
数据类型
基本类型
布尔型
boolean(1字节,8bit)
true
false
字符型
char(2字节,16bit)
ASC码表
'0'字符数值为48
'A'字符数值为65
'a'字符数值为97
浮点型
float(4字节,32bit)(3.14F)
double(8字节,64bit)(默认)
整型
byte(1字节,8bit)
short(2字节,16bit)
int(4字节,32bit)(默认)
long(8字节,64bit)
包装类
自动拆装箱(jdk5后支持)
装箱,调用valueOf方法
拆箱,调用xxValue
集合类泛型只能是包装类
包装类可以为null,基本类型作为成员变量有默认值
注意:包装类对-128 ~ 127 的值会有缓存xxxCache,对比这个范围的值的对象会有问题
引用类型
概念
引用是一种数据类型(保存在stack中),保存了对象在内存(heap,堆空间)中的地址
不同的引用可能指向同一个对象
数组
采用一段连续的存储单元来存储数据。对于指定下标的查找,时间复杂度为O(1);
数组的创建
int[] arr = new int[10]
int[] arr = new int []{1,2,3,4,5}
int[] arr = {1,2,3,4,5}
对象
内存分析
寄存器
本地方法栈
方法区:存放.class文件
堆内存:java里面所有使用new关键字创建的对象都是存放在堆内存中
栈:声明的变量都是在栈上去声明的
函数传参
基本类型(包装类)是按值传递(包装类)
引用类型传引用
关键字
transient
Static关键字
static关键字的使用
静态关键字static可以修饰成员变量和成员方法以及可以修饰代码块(静态代码块)
加载时间:静态关键字修饰的额任何内容都是和类一起进行加载的(优先级和类加载评级)
静态关键字修饰的成员变量和方法都可以直接通过:类名.静态变量 类名.静态方法
静态关键字是属于类的,他不属于某一个对象,但是一旦发生变化,全部都会发生变化
注意事项
静态方法不能直接访问普通成员变量或成员方法,只能访问静态所修饰的变量和方法
反之,普通的成员方法(未被static修饰的方法)可以直接访问类变量或静态方法
静态方法中,不能使用this关键字
volatile
保证了变量的可见性(visibility)。被volatile关键字修饰的变量,如果值发生了变更,其他线程立马可见,避免出现脏读的现象。
和synchronized的区别
synchronized
修饰普通方法:锁对象即为当前对象
修饰静态方法:锁对象为当前Class对象
修饰代码块:锁对象为synchronized紧接着的小括号内的对象
注:synchronized不会被继承,父类方法被synchronized修饰,子类没有重写方法,直接调用父类方法,具有同步作用;重写了方法且不带关键字,则不具有同步作用
final
修饰类时
该类不可被继承,不可修改,并且成员方法都默认为final方法(String、包装类)
修饰成员变量
必须赋予初始值且不能被改变,修饰引用变量时,该引用变量不能再指向其他对象
修饰方法
修饰方法方法不可修改,不可被重写,但可以被重载(当觉得父类方法已经满足需求,不需要子类重写时,可用final修饰)
常见类实现原理
List
ArrayList(线程不安全)
实现原理:数组 transient Object[] elementData
默认初始容量10
底层数据结构是数组,查询快,增删慢 线程不安全,效率高
扩容 list.add调用ensureCapacity(int minCapacity),参数minCapacity 为当前 list 容量+1。minCapacity 大于 list.length时触发扩容。方法ensureCapacity 调用 grow(int minCapacity)扩容,grow 创建 一个原数组1.5倍大小的数组,并将原数组所有数据复制到新数组。创建新数组前,校验新数组大小,若新数组大小 大于Integer.MAX_VALUE - 8,则判断minCapacity, 若minCapacity大于int的最大值,则oom,否则创建一个容量为Integer.MAX_VALUE的数组
注:foreach删除元素会抛出并发修改异常,list的size会随删除而变化
LinkedList(线程不安全)
实现原理:一个双向链表,它增删快,效率高,但是查询慢
Map
HashMap(线程不安全)
HashMap的主干是一个Entry数组。Entry是HashMap的基本组成单元,每一个Entry包含一个key-value键值对。(其实所谓Map其实就是保存了两个对象之间的映射关系的一种集合)
ConcurrentHashMap(线程安全)
HashTable(线程安全)
String
String(final不可变)
StringBuffer(线程安全)
StringBuilder(非线程安全)
集合
Collection
java.util.Collection
List,有序,可重复集
ArrayList
javax.management.AttributeList
底层数据结构是数组,查询快,增删慢
线程不安全,效率高
线程不安全,效率高
LinkedList
底层数据结构是链表,查询慢,增删快
线程不安全,效率高
Vector
Stack
底层数据结构是数组,查询快,增删慢
线程安全,效率低
Set,无序,不可重复集(唯一)
HashSet
LinkedHashSet
底层数据结构是链表和哈希表。(FIFO插入有序,唯一)
1.由链表保证元素有序
2.由哈希表保证元素唯一
底层数据结构是哈希表。(无序,唯一)
如何来保证元素唯一性?
1.依赖两个方法:hashCode()和equals()
SortedSet
TreeSet
底层数据结构是红黑树。(唯一,有序)
1. 如何保证元素排序的呢?
自然排序
比较器排序
2.如何保证元素唯一性的呢?
根据比较的返回值是否是0来决定
1. 如何保证元素排序的呢?
自然排序
比较器排序
2.如何保证元素唯一性的呢?
根据比较的返回值是否是0来决定
EnumSet
Queue ,队列“先进先出"
Deque
LinkedList
AbstractQueue
PriorityQueue
ConcurrentLinkedDeque
1. queue的增加元素方法add和offer的区别在于,add方法在队列满的情况下将选择抛异常的方法来表示队列已经满了,而offer方法通过返回false表示队列已经满了;在有限队列的情况,使用offer方法优于add方法;
2. remove方法和poll方法都是删除队列的头元素,remove方法在队列为空的情况下将抛异常,而poll方法将返回null;
3. element和peek方法都是返回队列的头元素,但是不删除头元素,区别在与element方法在队列为空的情况下,将抛异常,而peek方法将返回null.
集合使用规则
元素是否唯一
是:Set
优先使用HashSet
否:List
优先使用ArrayList
为什么要使用迭代器
优点:
1.可以不了解集合内部的数据结构,就可以直接遍历
2.不暴露内部的数据,可以直接外部遍历;
3.适用性强,基本上的集合都能使用迭代器;
java.util.Map
HashMap
无序,非线程安全
数组:存储区间连续,占用内存严重,寻址容易,插入删除困难;
链表:存储区间离散,占用内存比较宽松,寻址困难,插入删除容易;
HashMap综合应用了这两种数据结构,实现了寻址容易,插入删除也容易
原理:基于哈希表的Map接口的非同步实现,
底层由数组和链表结合组成的复合结构,数组被分为一个个桶(bucket),
每个桶存储有一个或多个Entry对象,每个Entry对象包含三部分key(键)、value(值),next(指向下一个Entry),
通过哈希值决定了Entry对象在这个数组的寻址;哈希值相同的Entry对象(键值对),则以链表形式存储。
如果链表大小超过树形转换的阈值(TREEIFY_THRESHOLD= 8,根据泊松分布计算得来),链表就会被改造为红黑树结构。
红黑树的引入保证了在大量hash冲突的情况下,HashMap还具有良好的查询性能
桶的数量默认为16,如果超过了负载因子定义(默认0.75)的容量,就会触发resize函数,重新设置桶的数量,这个操作为ReHash
查询时间复杂度:HashMap的本质可以认为是一个数组,数组的每个索引被称为桶,每个桶里放着一个单链表,一个节点连着一个节点。
很明显通过下标来检索数组元素时间复杂度为O(1),而且遍历链表的时间复杂度是O(n),
所以在链表长度尽可能短的前提下,HashMap的查询复杂度接近O(1)
HashMap中的modCount,记录map的修改次数,和expectedModCount作比较,如果不同,说明其他线程操作了数据,抛出异常ConcurrentModificationException(Fail-Fast机制)
Hashtable
无序,线程安全
构造函数外,Hashtable的所有 public 方法声明中都有 synchronized关键字
原理:hash表,方法都加上了synchronized关键字,所以实现了线程同步,但是性能较差
TreeMap
有序,非线程安全
原理:基于红黑树的一种提供顺序访问的 Map
LinkedHashMap
有序,非线程安全
LinkedHashMap多了一个双向循环链表
原理:HashMap+双向链表
ConcurrentHashMap
线程安全的HashMap
原理:JDK1.8以前Segment分段锁机制,JDK1.8利用CAS+Synchronized
来保证并发更新的安全。
数据结构采用:数组+链表+红黑树。
IO
基本概念
同步异步
关注事件发生时的方式
阻塞非阻塞
关注等待事件时的状态
同步阻塞io(Blocking, synchronous):client在调用read()方法时,stream里没有数据可读,线程停止向下执行,直至stream有数据。
阻塞:体现在这个线程不能干别的了,只能在这里等着
同步:是体现在消息通知机制上的,即stream有没有数据是需要我自己来判断的
同步非阻塞io(Non-blocking, synchronous):调用read方法后,如果stream没有数据,方法就返回,然后这个线程就就干别的去了。
非阻塞:体现在,这个线程可以去干别的,不需要一直在这等着
同步:体现在消息通知机制,这个线程仍然要定时的读取stream,判断数据有没有准备好,client采用循环的方式去读取,可以看出CPU大部分被浪费了
异步非阻塞io(Non-blocking, asynchronous):服务端调用read()方法,若stream中无数据则返回,程序继续向下执行。当stream中有数据时,操作系统会负责把数据拷贝到用户空间,然后通知这个线程,这里的消息通知机制就是异步!而不是像NIO那样,自己起一个线程去监控stream里面有没有数据!
IO(BIO)
特点
面向流
同步,阻塞
无Selector
基于字节流和字符流进行操作
磁盘->内核空间缓冲区->用户空间缓冲区
例子:serverSocket.accept();
NIO
特点
面向缓冲
同步非阻塞,多路复用 IO,同步体现在 selector 仍然要去轮循判断 channel 是否准备好,非阻塞体现在这个过程中处理线程不会一直在等待,可以去做其他的事情
选择器Selector
NIO基于Channel和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。
1.相比于bio的一个一个byte传,nio是以channel形式读取buffer缓冲区,然后
以块数据传输
2.nio减少了复制过程(这里共两种方法)
第一种:jvm进程的虚拟地址空间直接从磁盘中读取。
第二种:直接内存(堆外内存 DirectByteBuffer),不需要加载到JVM空间。
组件
Channel
概念:channel和IO中的Stream(流)是差不多一个等级的。只不过Stream是单向的,Channel是双向的。(Stream区分输入和输出)
类型
FileChannel(文件)
我们只能通过使用一个InputStream、OutputStream或RandomAccessFile来获取一个FileChannel实例
transferFrom()
将数据从源通道传输到FileChannel
transferTo()
将数据从FileChannel传输到其他的channel
FileChannel不能切换到非阻塞模式,所以不可以和Selector一起使用
DatagramChannel(UDP)
Java NIO中的DatagramChannel是一个能收发UDP包的通道。因为UDP是无连接的网络协议,所以不能像其它通道那样读取和写入。它发送和接收的是数据包。
SocketChannel(TCP)
ServerSocketChannel(监听套接字)
ServerSocketChannel.open(); 创建Channel
SelectableChannel.register() 注册到Selector
注意register()方法的第二个参数。
这是一个“interest集合”,意思是在通过Selector监听Channel时对什么事件感兴趣。
SelectionKey.OP_CONNECT 某个channel成功连接到另一个服务器称为“连接就绪”。
SelectionKey.OP_ACCEPT 一个server socket channel准备好接收新进入的连接称为“接收就绪”。
SelectionKey.OP_READ 一个有数据可读的通道可以说是“读就绪”。
SelectionKey.OP_WRITE 等待写数据的通道可以说是“写就绪”。
ServerSocketChannel.configureBlocking(false);设置是否阻塞
ServerSocketChannel.socket().bind(new InetSocketAddress(PORT));设置服务端口
Buffer
属性
Position类似于读写指针,表示当前读(写)到什么位置
Limit 在写模式下表示最多能写入多少数据,在读模式下表示最多能读多少数据
Capacity 容量 调用ByteBuffer.allocate(1024)后,Capacity就是设置的数组长度,读写模式下都是固定的
mark 标记Position位置,通过reset()方法恢复
重要方法
flip()读写模式切换
在Buffer读取数据之后,position从0移动到数据后一位,当在写模式下调用filp()后,limit移动到position的位置,position回到0。
这样底层操作系统就可以从缓冲区中正确读取到数据了,如果不执行,就会从有效数据后面开始读,读到0的数据(确定有效数据的位置)
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
clear()和compact()的区别
调用clear()方法:position将被设回0,limit设置成capacity,相当于Buffer被清空了。
但其实Buffer中的数据并未被清除,只是这些标记告诉我们可以从哪里开始往Buffer里写数据。
如果Buffer中有一些未读的数据,调用clear()方法,数据将“被遗忘”,意味着不再有任何标记会告诉你哪些数据被读过,哪些还没有。
compact()方法将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面。
如果Buffer中仍有未读的数据,且后续还需要这些数据,但是此时想要先写些数据,那么使用compact()方法。
limit属性依然像clear()方法一样,设置成capacity。现在Buffer准备好写数据了,但是不会覆盖未读的数据。
通过调用Buffer.mark()方法,可以标记Buffer中的一个特定的position,之后可以通过调用Buffer.reset()方法恢复到这个position。
Buffer.rewind()方法将position设回0,所以你可以重读Buffer中的所有数据。limit保持不变,仍然表示能从Buffer中读取多少个元素。
类型
基本类型对应的Buffer
ByteBuffer
DoubleBuffer
MappedByteBuffer(文件零拷贝)
MappedByteBuffer是NIO引入的文件内存映射方案,读写性能极高。
MappedByteBuffer有资源释放的问题:被MappedByteBuffer打开的文件只有在垃圾收集时才会被关闭,而这个点是不确定的。
MappedByteBuffer有资源释放的问题:被MappedByteBuffer打开的文件只有在垃圾收集时才会被关闭,而这个点是不确定的。
MappedByteBuffer是ByteBuffer的子类
force():缓冲区是READ_WRITE模式下,此方法对缓冲区内容的修改强行写入文件
load():将缓冲区的内容载入内存,并返回该缓冲区的引用
isLoaded():如果缓冲区的内容在物理内存中,则返回true,否则返回false
HeapByteBuffer
ByteBuffer有两种模式:直接/间接.间接模式最典型(也只有这么一种)的就是HeapByteBuffer,即操作堆内存 (byte[]),
内存有限,如果要发送一个大文件,不能分配足够的内存,这时就必须使用"直接"模式,即 MappedByteBuffer,文件映射
DirectByteBuffer
Selector
要使用Selector, 得向Selector注册Channel,然后调用它的select()方法。与Selector一起使用时,Channel必须处于非阻塞模式下。
这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件
SelectionKey(就绪事件集合)
概述:当向Selector注册Channel时,register()方法会返回一个SelectionKey对象。
属性
interest集合,所选择的感兴趣的事件集合。可以通过SelectionKey读写interest集合
ready集合,ready集合是interest集合的子集,并且表示了interest集合中从上次调用select( )以来已经就绪的那些操作,
if (key.isWritable( ))
等价于:
if ((key.readyOps( ) & SelectionKey.OP_WRITE) != 0)
Channel channel = selectionKey.channel();
Selector selector = selectionKey.selector();
附加的对象(可选)
selectionKey.attach(theObject);或者channel.register(selector, SelectionKey.OP_READ, theObject);
Object attachedObj = selectionKey.attachment();
可以将一个对象或者更多信息附着到SelectionKey上,这样就能方便的识别某个给定的通道。
重要方法
Selector.open();创建Selector
Selector.select();
自上次调用select()方法后有多少通道变成就绪状态。
(不含上次调用的数量)
int select()
阻塞到至少有一个通道在你注册的事件上就绪了。
int select(long timeout)
和select()一样,但是有最长会阻塞timeout毫秒(参数)。
int selectNow()
不会阻塞,不管什么通道就绪都立刻返回
(译者注:此方法执行非阻塞的选择操作。
如果自从前一次选择操作后,
没有通道变成可选择的,则此方法直接返回零。)。
selectedKeys()
调用了select()方法,并且返回值表明有一个或更多个通道就绪了,然后可以通过调用selector的selectedKeys()方法,
访问“已选择键集(selected key set)”中的就绪通道。
Selector不会自己从已选择键集中移除SelectionKey实例,需要调用迭代器的remove()方法移除。
Selector
scatter / gather经常用于需要将传输的数据分开处理的场合,例如传输一个由消息头和消息体组成的消息,你可能会将消息体和消息头分散到不同的buffer中,这样你可以方便的处理消息头和消息体。
Scatter
分散(scatter)从Channel中读取是指在读操作时将读取的数据写入多个buffer中。因此,Channel将从Channel中读取的数据“分散(scatter)”到多个Buffer中。
Gather
聚集(gather)写入Channel是指在写操作时将多个buffer的数据写入同一个Channel,因此,Channel 将多个Buffer中的数据“聚集(gather)”后发送到Channel。
Pipe
Java NIO 管道是2个线程之间的单向数据连接。Pipe有一个source通道和一个sink通道。数据会被写到sink通道,从source通道读取。
Source(读),Sink(写)
AIO
AIO 是彻底的异步通信。
NIO 是同步非阻塞通信
AsynchronousSocketChannel(异步Socket)
AsynchronousChannelGroup(线程组)
监控/控制异步操作
Future
Future.get()
回调CompletionHandler
线程池模拟的(netty4以去除AIO)
反射(reflection)
反射是java提供的一个重要功能,可以在运行时检查类、接口、方法和变量等信息,无需知道类的名字,方法名等。
还可以在运行时实例化新对象,调用方法以及设置和获取变量值。
用途:框架中使用,例如spring、mybatis等等,
JDBC利用反射将数据库的表字段映射到java对象的getter/setter方法。
Jackson, GSON, Boon等类库也是利用反射将JSON文件的属性映射到java对的象getter/setter方法。
Class类
获取方式
类名.class,适用于在编译时已经知道具体的类
Class alunbarClass = Alunbar.class;
Class.forName(),适用于运行时动态获取Class对象,只需将类名作为forName方法的参数
Class alunbarClass = Class.forName("Alunbar");
ClassNotFoundException
主要方法
获取类名
class.getName()包括包名
class.getSimpleName()只有类名
获取构造函数
Class.getConstructors();
Constructor.getParameterTypes()获取到构造函数的参数
初始化对象
Class.newInstance()
必须有无参构造器
Constructor.newInstance();
获取Methods方法信息
获取成员方法参数
获取成员方法返回类型
获取类修饰符
class.getModifiers()可以获取一个类的修饰符,但是返回的结果是int,结合Modifier提供的方法,就可以确认修饰符的类型
例如: Modifier.isPublic(int modifiers)
获取包信息
class.getPackage()
输出结果package com.example
String的输出结果package java.lang, Java Platform API Specification, version 1.8
获取父类Class
class.getSuperclass()
获取接口信息
Class.getInterfaces();
invoke()调用方法
Method.invoke(Class.newInstance())
动态代理
使用反射可以在运行时创建接口的动态实现,java.lang.reflect.Proxy类提供了创建动态实现的功能。
我们把运行时创建接口的动态实现称为动态代理。
动态代理使用场景:
1、数据库连接和事务管理。例如Spring框架有一个事务代理,可以启动和提交/回滚事务
2、用于单元测试的动态模拟对象
3、类似AOP的方法拦截。
调用java.lang.reflect.Proxy类的newProxyInstance()方法就可以常见动态代理,
newProxyInstance()方法有三个参数:
1、用于“加载”动态代理类的类加载器。
2、要实现的接口数组。
3、将代理上的所有方法调用转发到InvocationHandler的对象。
多线程
线程和进程
在串行程序基础上引入线程和进程是为了提高程序的并发度,从而提高程序运行效率和响应时间。
进程 - 进程是具有一定独立功能的程序,进程是系统进行资源分配和调度的一个独立单位。
同一个进程中的多个线程之间可以并发执行。
同一个进程中的多个线程之间可以并发执行。
线程 - 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位;线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
一个线程可以创建和撤销另一个线程;
实现方式
继承 Thread 类
public class MyThread extends Thread {
@Override
public void run() {
doSomething();
}
private void doSomething(){
System.out.println("Just do it!");
}
}
@Override
public void run() {
doSomething();
}
private void doSomething(){
System.out.println("Just do it!");
}
}
public class Main{
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
}
实现 Runnable 接口
Thread 类也是 Runnable 接口的子类,Thread调用Runnable的runfangf
public class RunnableThread implements Runnable {
@Override
public void run() {
doSomething();
}
private void doSomething(){
System.out.println("Just do it!");
}
}
@Override
public void run() {
doSomething();
}
private void doSomething(){
System.out.println("Just do it!");
}
}
public class Main{
public static void main(String[] args) {
RunnableThread runnableThread = new RunnableThread();
Thread thread = new Thread(runnableThread);
thread.start();
}
}
public static void main(String[] args) {
RunnableThread runnableThread = new RunnableThread();
Thread thread = new Thread(runnableThread);
thread.start();
}
}
实现Callable接口和Future创建线程
public class CallableThread implements Callable<String> {
@Override
public String call() throws Exception {
doSomething();
return "over";
}
private void doSomething(){
System.out.println("Just do it!");
}
}
@Override
public String call() throws Exception {
doSomething();
return "over";
}
private void doSomething(){
System.out.println("Just do it!");
}
}
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<String> callable = new CallableThread();//callable不能直接获取返回值
FutureTask<String> futureTask = new FutureTask<String>(callable);//需要futureTask封装一下,获取返回值
Thread thread = new Thread(futureTask);
thread.start();
doSomething();
System.out.println(futureTask.get());
}
private static void doSomething() {
System.out.println("main");
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<String> callable = new CallableThread();//callable不能直接获取返回值
FutureTask<String> futureTask = new FutureTask<String>(callable);//需要futureTask封装一下,获取返回值
Thread thread = new Thread(futureTask);
thread.start();
doSomething();
System.out.println(futureTask.get());
}
private static void doSomething() {
System.out.println("main");
}
}
四种线程池的使用
newFixedThreadPool 定长线程池
newCachedThreadPool 可缓冲线程池
ScheduledThreadPool 周期线程池
newSingleThreadExecutor 单任务线程池
线程状态变化
创建状态
已经有了相应的内存空间和其他资源,但还处于不可运行状态。
就绪状态
新建线程对象后,调用该线程的 start() 方法就可以启动线程。当线程启动时,线程进入就绪状态。
此时,线程将进入线程队列排队,等待 CPU 服务,这表明它已经具备了运行条件。
运行状态
当就绪状态被调用并获得处理器资源时,线程就进入了运行状态。此时,自动调用该线程对象的 run() 方法。
阻塞状态
一个正在执行的线程在某些特殊情况下,如被人为挂起或需要执行耗时的输入/输出操作,会让 CPU 暂时中止自己的执行,进入阻塞状态。
在可执行状态下,如果调用sleep(),suspend(),wait() 等方法,线程都将进入阻塞状态,发生阻塞时线程不能进入排队队列,只有当引起阻塞的原因被消除后,线程才可以转入就绪状态。
死亡状态
线程调用 stop() 方法时或 run() 方法执行结束后,即处于死亡状态。处于死亡状态的线程不具有继续运行的能力。
线程操作方法
Thread.join()线程的强制运行
在线程操作中,可以使用 join() 方法让一个线程强制运行,线程强制运行期间,其他线程无法运行,必须等待此线程完成之后才可以继续执行。。
底层使用wait实现,所以会释放锁。
Thread.sleep()线程的休眠,没有释放锁
在程序中允许一个线程进行暂时的休眠,直接使用 Thread.sleep() 即可实现休眠。
线程进入睡眠状态,不会释放锁
Thread.interrupt()中断线程
当一个线程运行时,另外一个线程可以直接通过interrupt()方法中断其运行状态。
Thread.setPriority(Thread.MAX_PRIORITY)设置线程优先级
在 Java 的线程操作中,所有的线程在运行前都会保持在就绪状态,那么此时,哪个线程的优先级高,哪个线程就有可能会先被执行。
Thread.yield()线程的礼让
在线程操作中,也可以使用 yield() 方法将一个线程的操作暂时让给其他线程执行。
让出时间片,不会释放锁
Object.wait(),会释放锁
当线程执行wait()方法时候,会释放当前的锁,然后让出CPU,进入等待状态。
调动方法之前,必须要持有锁。
Object.notify/notifyAll()
只有当 notify/notifyAll() 被执行时候,才会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,
直到执行完synchronized 代码块的代码或是中途遇到wait() ,再次释放锁。
notify随机唤醒一个,notifyAll唤醒全部
CAS(Compare-and-Swap),即比较并替换
CAS需要有3个操作数:内存地址V,旧的预期值A,即将要更新的目标值B。
优点:很高效的解决了原子操作问题
缺点
1.循环时间长开销很大。
CAS 通常是配合无限循环一起使用的,我们可以看到 getAndAddInt 方法执行时,如果 CAS 失败,会一直进行尝试。
如果 CAS 长时间一直不成功,可能会给 CPU 带来很大的开销。
2.只能保证一个变量的原子操作。
对多个变量操作时,CAS 目前无法直接保证操作的原子性。但是我们可以通过以下两种办法来解决:
1)使用互斥锁来保证原子性;
2)将多个变量封装成对象,通过 AtomicReference 来保证原子性。
3.ABA问题。(初次读取的值是A,修改为B,再改回A)
Java并发包为了解决这个问题,提供了一个带有标记的原子引用类“AtomicStampedReference”,它可以通过控制变量值的版本来保证CAS的正确性。
因此,在使用CAS前要考虑清楚“ABA”问题是否会影响程序并发的正确性,如果需要解决ABA问题,改用传统的互斥同步可能会比原子类更高效。
同步和死锁
synchronized
Synchoronized语法上简洁方便
Synchoronized是JVM方法,由编辑器保证枷锁和释放
ReentrantLock
原理
利用CAS+AQS队列来实现
ReentrantLock是JDK方法,需要手动声明上锁和释放锁,因此语法相对复杂些;如果忘记释放锁容易导致死锁
ReentrantLock具有更好的细粒度,可以在ReentrantLock里面设置内部Condititon类,可以实现分组唤醒需要唤醒的线程
RenentrantLock能实现公平锁
两个线程都在等待对方先完成,造成程序的停滞,一般程序的死锁都是在程序运行时出现的。
自定义Lock
java并发包
Throwable
Error错误
VirtualMachineError
AWTError
Exception异常
IOException
EOFException
FileNotFoundException
RuntimeException
NullPointerException
IndexOutOfBoundsException
ClassCastException
ClassNotFoundException
JDK8特性
1.Lambda表达式
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
@FunctionalInterface 函数式接口
只能有一个未实现的方法,但是可以有多个default方法
Lambda的作用域
可以直接访问外部的局部变量,但是它是隐式final的,不需要声明但是不可以改变
内置函数式接口
2.Stream函数式操作流元素集合
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
Pipelining: 中间操作都会返回流对象本身。
内部迭代, 通过访问者模式(Visitor)实现。
stream() − 为集合创建串行流。
parallelStream() − 为集合创建并行流。
forEach 迭代流中的每个数据
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
map 方法用于映射每个元素到对应的结果
filter 方法用于通过设置的条件过滤出元素。
limit 方法用于获取指定数量的流。
sorted 方法用于对流进行排序。
parallelStream 是流并行处理程序的代替方法。
Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。
统计
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("列表中最大的数 : " + stats.getMax());
System.out.println("列表中最小的数 : " + stats.getMin());
System.out.println("所有数之和 : " + stats.getSum());
System.out.println("平均数 : " + stats.getAverage());
3.接口新增:默认方法与静态方法
接口的方法用default修饰就可以有方法体,如果类实现的多个接口有相同默认方法,需要覆盖
4.方法引用,与Lambda表达式联合使用
5.引入重复注解
6.类型注解
7.最新的Date/Time API (JSR 310)
8.新增base64加解密API
9.数组并行(parallel)操作
parallelSort
10.JVM的PermGen空间被移除:取代它的是Metaspace(JEP 122)元空间
网络编程
socket
UDP和TCP
websocket
注解
元注解
@Target
@Retention
@Documented
@Inherited
自定义注解
1.使用@interface自定义注解时
2.使用注解
3.解析注解
spring全家桶
Spring MVC
优点
封装代码,维护成本低,耦合性低
有利于开发中的分工,提高开发效率
组件重用,易拓展,有利于代码复用,重用性高
有利于开发中的分工,提高开发效率
组件重用,易拓展,有利于代码复用,重用性高
缺点
Spring与MVC 的Servlet API 耦合,难以脱离容器独立运行
增加系统结构和实现的复杂性,太过于细分,开发效率低
视图与控制器间的过于紧密的连接
增加系统结构和实现的复杂性,太过于细分,开发效率低
视图与控制器间的过于紧密的连接
原理
1)用户发送请求至前端控制器 DispatcherServlet。
2)DispatcherServlet 收到请求调用 HandlerMapping 处理器映射器。
3)处理器映射器找到具体的处理器(可以根据 xml 配置、注解进行查找),生成处理器对象
及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet。
4)DispatcherServlet 调用 HandlerAdapter 处理器适配器。
5)HandlerAdapter 经过适配调用具体的处理器(Controller,也叫后端控制器)。
6)Controller 执行完成返回 ModelAndView。
7)HandlerAdapter 将 controller 执行结果 ModelAndView 返回给 DispatcherServlet。
8)DispatcherServlet 将 ModelAndView 传给 ViewReslover 视图解析器。
9)ViewReslover 解析后返回具体 View。
10)DispatcherServlet 根据 View 进行渲染视图(即将模型数据填充至视图中)。
11)DispatcherServlet 响应用户。
2)DispatcherServlet 收到请求调用 HandlerMapping 处理器映射器。
3)处理器映射器找到具体的处理器(可以根据 xml 配置、注解进行查找),生成处理器对象
及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet。
4)DispatcherServlet 调用 HandlerAdapter 处理器适配器。
5)HandlerAdapter 经过适配调用具体的处理器(Controller,也叫后端控制器)。
6)Controller 执行完成返回 ModelAndView。
7)HandlerAdapter 将 controller 执行结果 ModelAndView 返回给 DispatcherServlet。
8)DispatcherServlet 将 ModelAndView 传给 ViewReslover 视图解析器。
9)ViewReslover 解析后返回具体 View。
10)DispatcherServlet 根据 View 进行渲染视图(即将模型数据填充至视图中)。
11)DispatcherServlet 响应用户。
常用注解
@Controller
@RequestMapping
SpringMVC 怎么样设定重定向和转发
在返回值前面加"forward:"就可以让结果转发
在返回值前面加"redirect:"就可以让返回值重定向
事务传播机制
Propagation.REQUIRED
如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。
Propagation.SUPPORTS
支持当前事务,如果当前没有事务,就以非事务方式执行。
Propagation.MANDATORY
使用当前的事务,如果当前没有事务,就抛出异常。
Propagation.REQUIRES_NEW
不管调用者(addEmpByRequiresNew)是否存在事务,被调用者(addDeptByRequiresNew)都会新开一个事务,
相当于被调用者都存在于自己的事务中和调用者没有关系。
Propagation.NOT_SUPPORTED
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
Propagation.NEVER
以非事务方式执行,如果当前存在事务,则抛出异常。
Propagation.NESTED
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
(这个和REQUIRED区别在于一个是加入到一个事务,一个是在嵌套事务运行)
Spring IOC
原理
IOC(控制反转)就是由spring来负责控制对象的生命周期和对象间的关系。
IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。
利用Java 语言的反射功能实例化Bean 并建立 Bean 之间的依赖关系。
Spring 的 IoC 容器在完成这些底层工作的基础上,还提供了 Bean 实例缓存、生命周期管理、 Bean 实例代理、事件发布、资源装载。
Spring 的 IoC 容器可以建立父子层级关联的容器体系,子容器可以访问父容器中的 Bean,但父容器不能访问子容器的 Bean。
Bean的生命周期
实例化 Instantiation
属性赋值 Populate
初始化 Initialization
销毁 Destruction
注解
@Component:组件(spring扫描包下有该注解的类)
@ComponentScan(包名):组件扫描(spring扫描该包名下的类)
@Configuration:配置类
@Autowired:自动装配(它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。 通过 @Autowired的使用来消除 set ,get方法)
@Autowired默认按类型装配,默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false。
@Resource默认按名称装配,当找不到与名称匹配的bean时才按照类型进行装配。名称可以通过name属性指定,
如果没有指定name属性,当注解写在字段上时,默认取字段名,当注解写在setter方法上时,默认取属性名进行装配。
@Scope(“singleton”):单例模式
@Scope(“prototype”):多例模式
Bean的生命周期注解
@PostConstruct 相当于init-method,Bean初始化业务逻辑的处理
@PreDestroy 相当于destroy-method,Bean销毁业务逻辑的处理
优点
ioc的核心思想,资源不由使用资源的双方管理,而由不使用资源的第三方管理。
1)资源集中管理,实现资源的可配置和易管理。
2)降低了使用资源双方的依赖程度(耦合度)。
1)资源集中管理,实现资源的可配置和易管理。
2)降低了使用资源双方的依赖程度(耦合度)。
缺点
1)生成一个对象的步骤变复杂了(其实上操作上还是挺简单的),对于不习惯这种方式的人,会觉得有些别扭和不直观。
2)对象是使用反射生成,在效率上有些损耗。
但相对于IoC提高的维护性和灵活性来说,这点损耗是微不足道的,除非某对象的生成对效率要求特别高
Spring AOP
AOP代理
静态代理
Spring AOP动态代理
优点
1)解耦
2)重复利用代码
3)便于装卸
缺点
性能略低,仅适用于方法调用,必须在Spring容器
注解
@Aspect:作用:把当前类声明为切面类。
@Pointcut作用:指定切入点表达式。属性:value:指定表达式的内容
@Before:作用:把当前方法看成是前置通知。属性:value:用于指定切入点表达式,还可以指定切入点表达式的引用。
@AfterReturning作用:把当前方法看成是后置通知。属性:value:用于指定切入点表达式,还可以指定切入点表达式的引用。
@AfterThrowing作用:把当前方法看成是异常通知。属性:value:用于指定切入点表达式,还可以指定切入点表达式的引用。
@After作用:把当前方法看成是始终通知。属性:value:用于指定切入点表达式,还可以指定切入点表达式的引用。
@Around作用:把当前方法看成是环绕通知。属性:value:用于指定切入点表达式,还可以指定切入点表达式的引用。
应用
操作数据库的事务,日志,拦截器,权限控制(spring-security)
Spring Boot
简述
Spring Boot就是对Spring做了一些默认配置。
核心理念:开箱即用,快速启动
核心理念:开箱即用,快速启动
特点
(1)快速开发spring应用的框架
(2)内嵌tomcat和jetty容器,不需要单独安装容器,jar包直接发布一个web应用
(3)简化maven配置,parent这种方式,一站式引入需要的各种依赖
(4)基于自动注解的零配置思想,简化了xml配置
(5)和各种流行框架,spring web mvc,mybatis,spring cloud无缝整合
(2)内嵌tomcat和jetty容器,不需要单独安装容器,jar包直接发布一个web应用
(3)简化maven配置,parent这种方式,一站式引入需要的各种依赖
(4)基于自动注解的零配置思想,简化了xml配置
(5)和各种流行框架,spring web mvc,mybatis,spring cloud无缝整合
常用注解
@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
@RestController
@RequestMapping
@GetMapping/@PostMapping
@PathVariable 获取url中的数据
@RequestParam :获取请求参数的值
@RequestHeader 把Request请求header部分的值绑定到方法的参数上
@CookieValue 把Request header中关于cookie的值绑定到方法的参数上
@Transactional事务注解
@ControllerAdvice 统一处理异常
@ExceptionHandler 注解声明异常处理方法
@EnableAsync/@Async 异步执行
自动配置的实现
自动配置注解是@EnableAutoConfiguration
常用依赖说明
Spring Security
springsecurity底层实现为一条过滤器链,用户请求进来,判断有没有请求的权限,抛出异常,重定向跳转。
核心功能
Authentication:认证,用户登陆的验证(解决你是谁的问题)
Authorization:授权,授权系统资源的访问权限(解决你能干什么的问题)
安全防护,防止跨站请求,session 攻击等
基本原理
表达式控制方法权限注解
@PreAuthorize
@PreAuthorize("hasRole('ROLE_ADMIN')")
@PostAuthorize
@PreFilter
@PostFilte
shiro与Spring Security
shiro入门更容易
Spring Security因为它的复杂,所以从功能的丰富性的角度更胜一筹。
Spring Data JPA
和Mybatis的区别
1)hibernate是面向对象的,而MyBatis是面向关系的
2)Mybatis可以进行更细致的SQL优化,查询必要的字段,但是需要维护SQL和查询结果集的映射。数据库的移植性较差,针对不同的数据库编写不同的SQL。
Hibernate对数据库提供了较为完整的封装,封装了基本的DAO层操作,有较好的数据库移植性。但是学习周期长,开发难度大于Mybatis。
数据库
Mysql
常见数据结构
数值型
INTEGER、SMALLINT、DECIMAL和NUMERIC
浮点型
FLOAT、DOUBLE
日期/时间
DATETIME、DATE、TIMESTAMP、TIME和YEAR
字符串(字符)类型
CHAR、VARCHAR、BINARY、VARBINARY、BLOB、TEXT
其他
枚举类型ENUM
SET是集合类型,它是一个排列组合
引擎
InnoDB
默认引擎(5.1后),是集聚引擎,支持事务安全表(ACID),支持行锁定和外键,5.6以后才有全文索引
原理
InnoDB 底层存储结构为B+树, B树的每个节点对应innodb的一个page,page大小是固定的,一般设为 16k。
其中非叶子节点只有键值,叶子节点包含完成数据。
特点
1、InnoDB给MySQL提供了具有提交、回滚和崩溃恢复能力的事物安全(ACID兼容)存储引擎。
2、InnoDB是为处理巨大数据量的最大性能设计。
3、InnoDB存储引擎完全与MySQL服务器整合,InnoDB存储引擎为在主内存中缓存数据和索引而维持它自己的缓冲池。
4、InnoDB支持外键完整性约束,存储表中的数据时,每张表的存储都按主键顺序存放,如果没有显示在表定义时指定主键,InnoDB会为每一行生成一个6字节的ROWID,并以此作为主键
5、InnoDB被用在众多需要高性能的大型数据库站点上
MyISAM
MyIASM 5.1前 MySQL默认的引擎,是非集聚引擎,但是它没有提供对数据库事务的支持,也不支持行级锁和外键,
因此当 INSERT(插入)或 UPDATE(更新)数据时即写操作需要锁定整个表,效率便会低一些。
对于一些只读数据,或者表空间较小,可以忍受恢复操作,可以使用MyISAM。MyISAM会将表存储在两个文件中:数据文件、索引文件。
MyISAM表的行记录数,取决于磁盘空间和操作系统中的单个文件最大尺寸。
TokuDB
底层存储结构为 Fractal Tree
TokuDB 在线添加索引,不影响读写操作, 非常快的写入性能, Fractal-tree 在事务实现上有优
势。 他主要适用于访问频率不高的数据或历史数据归档。
索引
优点
1)索引大大减小了服务器需要扫描的数据量
2)索引可以帮助服务器避免排序和临时表
3)索引可以将随机IO变成顺序IO
4)索引对于InnoDB(对索引支持行级锁)非常重要,因为它可以让查询锁更少的元组。
5)关于InnoDB、索引和锁:InnoDB在二级索引上使用共享锁(读锁),但访问主键索引需要排他锁(写锁)
缺点
1)虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。
因为更新表时,MySQL不仅要保存数据,还要保存索引文件。
2)建立索引会占用磁盘空间的索引文件。一般情况这个问题不太严重,但如果你在一个大表上创建了多种组合索引,索引文件的会膨胀很快。
3)如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果。
4)对于非常小的表,大部分情况下简单的全表扫描更高效;
聚簇索引/非聚簇索引
聚簇索引:将数据存储与索引放到了一块,找到索引也就找到了数据
非聚簇索引:将数据存储于索引分开结构,索引结构的叶子节点指向了数据的对应行,
索引存储类型
B-Tree索引,MyISAM和InnoDB都支持
MyISAM:非聚簇索引,data里存的是指向数据地址的指针,所以有三个文件(.frm .MYI .MYD)
InnoDB:聚簇索引,data里就放的是数据,所以有两个文件(.frm .idb)
B+Tree:每一个叶子节点都包含指向下一个叶子节点的指针,从而方便叶子节点的范围遍历。
B-Tree通常意味着所有的值都是按顺序存储的,并且每一个叶子页到根的距离相同,很适合查找范围数据。
B-Tree可以对<,<=,=,>,>=,BETWEEN,IN,以及不以通配符开始的LIKE使用索引。
Hash索引
哈希索引基于哈希表实现,只有精确索引所有列的查询才有效。
RTree空间数据索引
MyISAM支持空间索引,主要用于地理空间数据类型,例如GEOMETRY。
全文索引
全文索引是MyISAM的一个特殊索引类型,它查找的是文本中的关键词,主要用于全文检索。
MySQL InnoDB从5.6开始已经支持全文索引,但InnoDB内部并不支持中文、日文等,因为这些语言没有分隔符。
索引分类
primary主键索引 唯一且不能为空
唯一索引
主键索引
组合索引
最左前缀匹配原则
组合索引(a,b,c)
当按照索引中所有列进行精确匹配(“=” 或 “IN”)时,索引可以被用到,并且 type 为 const。
一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配
理论上索引对顺序是敏感的,但是由于 MySQL 的查询优化器会自动调整 where 子句的条件顺序以使用适合的索引,
所以 MySQL 不存在 where 子句的顺序问题而造成索引失效
原理
b+ 树的数据项是复合的数据结构,比如 (a,b,c) 的时候,
b+ 树是按照从左到右的顺序来建立搜索树的
建立索引的时机
一般来说,在WHERE和JOIN中出现的列需要建立索引,但也不完全如此,
因为MySQL的B-Tree只对<,<=,=,>,>=,BETWEEN,IN,以及不以通配符开始的LIKE才会使用索引。
注意事项
索引字段尽量使用数字型(简单的数据类型)
尽量不要让字段的默认值为NULL ,在MySQL中,含有空值的列很难进行查询优化,
因为它们使得索引、索引的统计信息以及比较运算更加复杂。
前缀索引和索引选择性
使用唯一索引 ,考虑某列中值的分布。索引的列的基数越大,索引的效果越好。
使用组合索引代替多个列索引
一个多列索引(组合索引)与多个列索引MySQL在解析执行上是不一样的,如果在explain中看到有索引合并(即MySQL为多个列索引合并优化),应该好好检查一下查询的表和结构是不是已经最优。
注意重复/冗余的索引、不使用的索引
对于重复/冗余、不使用的索引:可以直接删除这些索引。因为这些索引需要占用物理空间,并且也会影响更新表的性能。
索引使用
如果对大的文本进行搜索,使用全文索引而不要用使用 like ‘%…%
like语句不要以通配符开头
对于LIKE:在以通配符%和_开头作查询时,MySQL不会使用索引。
不要在列上进行运算
用 or 分割开的条件, 如果 or 前的条件中的列有索引, 而后面的列中没有索引, 那么涉及到的索引都不会被用到。
组合索引的使用要遵守“最左前缀”原则'
使用索引排序时,ORDER BY也要遵守“最左前缀”原则
如果列类型是字符串,那么一定记得在 where 条件中把字符常量值用引号引起来,否则的话即便这个列上有索引,MySQL 也不会用到的,因为MySQL 默认把输入的常量值进行转换以后才进行检索。
任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段
如果 MySQL 估计使用索引比全表扫描更慢,则不使用索引。
explain执行计划
select_type
SIMPLLE:简单查询,该查询不包含 UNION 或子查询
PRIMARY:如果查询包含UNION 或子查询,则最外层的查询被标识为PRIMARY
UNION:表示此查询是 UNION 中的第二个或者随后的查询
DEPENDENT:UNION 满足 UNION 中的第二个或者随后的查询,其次取决于外面的查询
UNION RESULT:UNION 的结果
SUBQUERY:子查询中的第一个select语句(该子查询不在from子句中)
DEPENDENT SUBQUERY:子查询中的 第一个 select,同时取决于外面的查询
DERIVED:包含在from子句中子查询(也称为派生表)
UNCACHEABLE SUBQUERY:满足是子查询中的第一个 select 语句,同时意味着 select 中的某些特性阻止结果被缓存于一个 Item_cache 中
UNCACHEABLE UNION:满足此查询是 UNION 中的第二个或者随后的查询,同时意味着 select 中的某些特性阻止结果被缓存于一个 Item_cache
type
ALL:全表扫描,这个类型是性能最差的查询之一。通常来说,我们的查询不应该出现 ALL 类型,因为这样的查询,在数据量最大的情况下,对数据库的性能是巨大的灾难。
index:全索引扫描,和 ALL 类型类似,只不过 ALL 类型是全表扫描,而 index 类型是扫描全部的索引,主要优点是避免了排序,但是开销仍然非常大。如果在 Extra 列看到 Using index,说明正在使用覆盖索引,只扫描索引的数据,它比按索引次序全表扫描的开销要少很多。
range:范围扫描,就是一个有限制的索引扫描,它开始于索引里的某一点,返回匹配这个值域的行。这个类型通常出现在 =、<>、>、>=、<、<=、IS NULL、<=>、BETWEEN、IN() 的操作中,key 列显示使用了哪个索引,当 type 为该值时,则输出的 ref 列为 NULL,并且 key_len 列是此次查询中使用到的索引最长的那个。
ref:一种索引访问,也称索引查找,它返回所有匹配某个单个值的行。此类型通常出现在多表的 join 查询, 针对于非唯一或非主键索引, 或者是使用了最左前缀规则索引的查询。
eq_ref:使用这种索引查找,最多只返回一条符合条件的记录。在使用唯一性索引或主键查找时会出现该值,非常高效。
const、system:该表至多有一个匹配行,在查询开始时读取,或者该表是系统表,只有一行匹配。其中 const 用于在和 primary key 或 unique 索引中有固定值比较的情形。
NULL:在执行阶段不需要访问表。
key
这一列显示MySQL实际决定使用的索引。如果没有选择索引,键是NULL。
key_len
这一列显示了在索引里使用的字节数,当key列的值为 NULL 时,则该列也是 NULL
rows
这一列显示了估计要找到所需的行而要读取的行数,这个值是个估计值,原则上值越小越好。
索引回表和覆盖
innodb有聚簇索引和普通索引,普通索引的叶子节点存储主键值,聚簇索引会存行记录,且只有一个聚集索引。
回表:主键查询会使用聚簇索引,直接返回数据;普通索引需要先找到主键id,再使用聚簇索引找行记录数据
覆盖:只需要在一棵索引树上就能获取SQL所需的所有列数据,无需回表,速度更快
方法:将被查询的字段,建立到联合索引里去(要查询的数据在索引中,可以避免回表)
MySQL里同一个数据表里的索引总数限制为16个。
事务ACID
原子性(Atomicity)
事务在执行性,就是说不允许事务部分得执行,一个事务是一个不可分割的工作单位。即使因为故障而使事务不能完成,rollback后也要回退到对数据库进行操作前的状态。
一致性(Consistency)
事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。比如A,B账户相互转账之后,总金额不变。
隔离性(Isolation)
一个事务的执行不能被其他事务干扰,当多个事务并发执行时,各个事务不会互相影响。
持久性(Durability)
持续性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。
幻读,不可重复读,脏读
脏读(DirtyReads):所谓脏读就是对脏数据(Drity Data)的读取,而脏数据所指的就是未提交的数据。
一个事务正在对一条记录做修改,在这个事务完成并提交之前,这条数据是处于待定状态的(可能提交也可能回滚),
这时,第二个事务来读取这条没有提交的数据,并据此做进一步的处理,就会产生未提交的数据依赖关系。
不可重复读(Non-RepeatableReads):一个事务先后读取同一条记录,期间另一个事务修改了数据,
并且已经commit,所以两次读取的数据不同,称之为不可重复读。
幻读(PhantomReads):一个事务先后读取同一个表,期间其他事务插入了新的数据,并且已经commit,这种现象就称为幻读。
它和不可重复读的区别:不可重复读的重点是update和delete,幻读重点是insert
隔离级别
read uncommited :幻读√,不可重复读√,脏读√
read committed:幻读√,不可重复读√,脏读×
读取锁,读完之后就解锁
repeatable read:幻读√,不可重复读×,脏读×
对正在操作的数据加锁,并且只有等到事务结束才放开锁
serializable :幻读×,不可重复读×,脏读×
因为获得范围锁,且事务是一个接着一个串行执行,则保证了不会发生幻读。
锁
锁种类
公平锁/非公平锁
1)公平锁是指多个线程按照申请锁的顺序来获取锁。
2)非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。
非公平锁的优点在于吞吐量比公平锁大。
Java ReentrantLock通过构造函数指定该锁是否是公平锁,默认是非公平锁。
Synchronized而言,也是一种非公平锁。
可重入锁
可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。
独享锁/共享锁
独享锁是指该锁一次只能被一个线程所持有。
共享锁是指该锁可被多个线程所持有。
Java ReentrantLock、Synchronized是独享锁。但是对于Lock的另一个实现类ReadWriteLock,其读锁是共享锁,其写锁是独享锁。
互斥锁/读写锁
上面讲的独享锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。
互斥锁在Java中的具体实现就是ReentrantLock 。
读写锁在Java中的具体实现就是ReadWriteLock
乐观锁/悲观锁
悲观锁认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁的并发操作一定会出问题。
乐观锁则认为对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,会采用尝试更新,不断重新的方式更新数据。乐观的认为,不加锁的并发操作是没有事情的。
悲观锁在Java中的使用,就是利用各种锁。
乐观锁在Java中的使用,是无锁编程,常常采用的是CAS算法,典型的例子就是原子类,通过CAS自旋实现原子操作的更新。
分段锁
分段锁其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。
偏向锁/轻量级锁/重量级锁
这三种锁是指锁的状态,并且是针对Synchronized。
偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。
自旋锁
在Java中,自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,
这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。
查询处理锁表
1.show OPEN TABLES where In_use > 0;
2.show processlist;
3.kill thread_id;
视图
CREATE VIEW view_name AS SELECT
SQL CREATE OR REPLACE VIEW Syntax CREATE OR REPLACE VIEW
CREATE VIEW [Current Product List] AS SELECT
分库分表
主从复制
读写分离
MyCat
sharding-sphere
redis
数据结构
string(字符串)
hash(哈希)
list(列表)
set(集合)
zset(sorted set:有序集合)
redis缓存问题
缓存雪崩
缓存击穿
缓存穿透
常见解决方案
1.直接缓存NULL值
2.限流
3.缓存预热
4.分级缓存
5.缓存永远不过期
redis订阅发布
持久化方式
集群模式
主从模式
sentinel哨兵模式
cluster集群模式
分布式锁
setnx
redLock
redisson
mongodb
数据结构
优点
常用框架组件
maven
Gradle
orm框架
Mybatis
mybatis-plus
多数据源
JPA
hibernate
Morphia
Quartz任务调度
搜索引擎
Lucene
Solr
ElasticStack
ElasticSearch
基本概念
索引(index)
文档类型
文档(document)
映射
RESTful API
创建非结构化索引
删除索引
插入数据
更新数据
删除数据
搜索数据
根据id搜索数据
搜索全部数据
关键字搜素数据
DSL搜索
等于
大于小于
关键字高亮
聚合查询
核心详解
文档
元数据
_index
_type
_id
查询响应
pretty
指定显示的字段_search?_source=id,name
判断文档是否存在
批量操作
_mget
问题解决
跨域
特点和优势
1)分布式实时文件存储,可将每一个字段存入索引,使其可以被检索到。
2)实时分析的分布式搜索引擎。
分布式:索引分拆成多个分片,每个分片可有零个或多个副本。
集群中的每个数据节点都可承载一个或多个分片,并且协调和处理各种操作;
负载再平衡和路由在大多数情况下自动完成。
3)可以扩展到上百台服务器,处理PB级别的结构化或非结构化数据。也可以运行在单台PC上(已测试)
4)支持插件机制,分词插件、同步插件、Hadoop插件、可视化插件等。
集群
组成角色
client node
master node
data node
配置项说明
脑裂问题
原因
网络原因
节点负载
回收内存
解决
角色分离
参数配置
Logstash-input-jdbc 同步mysql 数据至ElasticSearch
Logstash
处理流程
Input---->Filter---->Output
架构
我们可以有多个输入的文本,另外由Queue分发到不同的Pipline中,这里的Pipline中文的意思就是管道,
在程序中我们可以理解为线程,可以有多个Pipline,并且每个Pipline之间是互不打扰的,另外每个Batcher,Filter和Output组成,
Batcher是批量从Queue获取数据的,这个值可以通过配置进行设置;
Kibana
Beats
Packetbeat
Filebeat
Metricbeat
Winlogbeat
消息队列
docker
k8s(Kubernetes)
docker-compose
分布式/微服务
概念
SOA架构
分布式和微服务
传统单应用负载的缺点
无法直观地看到服务提供方和服务消费方当前的运行状况与通信频率;
消费方的失败重发、负载均衡等都没有统一策略,这加大了开发每个服务的难度,不利于快速演化。
架构模式
Sidecar 模式
链式微服务(Chained Microservice)
分支微服务(Branch Microservice)
事件溯源模式(Event Sourcing Pattern)
持续交付方式
CAP原则
C(Consistency)一致性
A(Availability)可用性
P(Partition tolerance)分区容错性
框架
springcloud :全家桶+嵌入式第三方组件
通讯方式:http restful
注册中心:eruka/consul
配置中心:config
网关:zuul
分布式追踪系统:sleuth+zipkin
通讯方式:http restful
注册中心:eruka/consul
配置中心:config
网关:zuul
分布式追踪系统:sleuth+zipkin
Spring Cloud Netflix Eureka:服务注册中心,用于服务治理。
注册中心配置
服务提供者配置
服务调用者配置
@LoadBalanced
这个注解会自动构造 LoadBalancerClient 接口的实现类并注册到 Spring 容器中
Spring Cloud Netflix Zuul :服务网关
Spring Cloud Netflix Ribbon:负载均衡,基于客户端的负载均衡组件。
Spring Cloud Netflix Hystrix :断路器,容错框架,能够防止服务的雪崩效应。
Spring Cloud Feign:WEB 客户端,能够简化 HTTP 接口的调用
注解
Spring Cloud Config :分布式配置管理。
Spring Cloud Sleuth 服务调用链跟踪,可配合Zipkin进行可视化
Spring Cloud Stream:构建消息驱动的微服务应用程序的框架。
Spring Cloud Gateway 服务网关
Spring Cloud Consul 服务注册、服务配置
Spring Cloud OpenFeign 服务之间Restful调用
Spring Cloud Bus:消息代理的集群消息总线。
dubbo:zookeeper + dubbo + springboot
通讯方式:rpc(相对于http效率更高)HTTP和RPC的优缺点
注册中心:zookeeper/redis
配置中心:diamond
分布式锁
条件
实现方式
基于数据库
基于Memcached
基于Redis
基于Zookeeper
Chubby
分布式事务
解决方案
两阶段提交协议(2PC)
事务补偿(TCC)
消息队列实现最终一致
网络基础
HTTP协议
概念
HTTP是Hyper Text Transfer Protocol(超文本传输协议)的缩写。
HTTP是一个应用层协议,由请求和响应构成,是一个标准的客户端服务器模型。HTTP是一个无状态的协议。
常见状态码
RESTful风格接口
POST 新增
DELETE 删除
PUT 更新
GET 查询
HTTPS原理
TCP/IP
OSI模型
应用层
应用层
会话层
传输层
网络层
数据链路层
MQTT
第三方平台对接
支付流程
微信支付
第三方登录流程
短信对接
邮箱对接
面试题大全
收藏
0 条评论
下一页
为你推荐
查看更多