JAVA数据类型分为基本数据类型和引用数据类型
基本数据类型有四类八种:
整型:byte(1字节)、short(2字节)、int(4字节)、long(8字节)
浮点型:float(4字节)、double(8字节)
字符型:char(2字节)
布尔型:boolean(1位)
引用类型:类class、接口interface、数组[]
Float存放的更大,因为虽然float占用4个字节,long占用8个字节,但是float存储结构不同, 是把32位分成了两部分,一部分存放阶码(左移位数的2进制表示,等价于10进制指数),一部分存放尾数(移动后小数点后面的数字,等价于10进制底数)。
可以,因为char是2个字节,汉字也是2个字节
1、Integer是int的包装类,int则是java的一种基本数据类型
2、Integer变量必须实例化后才能使用,而int变量不需要
3、Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
4、Integer的默认值是null,int的默认值是0
不一定,如byte i = 127,加完之后结果为-1
自动转换(小转大/子类转父类)
byte,short,char——>int——>long——>float——>double; byte,short,char相互之间不转换,他们参与运算首先转换为int类型 ;boolean类型不参与转换。
强制转换
目标类型 变量名=(目标类型)(被转换的数据),可能会丢失精度;
结果不是0.1,会丢失精度,用BigDecemal,同样大整形的时候可以使用BigInteger。
浮点数转二进制,是把小数点后面的值一直*2,一直到小数点后面为0结束,如果到了小数的底数表示的位数的时候还没有结束,就会直接丢失。
第一个有问题,第二个没问题,因为+=做了特殊处理,相当于+完之后强转了
s=s+1;先执行等式右边的,s+1会转化为int,int不能自动转换为short ,不能从大到小转类型,只能强转。所以会出现编译出错的问题;
而s+=1;+=是一个操作符,在解析的时候等价于:s=(short)s+1.
int a = 128;
int b = 128;
System.out.println(a == b); //① true
Integer a1 = 128;
Integer b1 = 128;
System.out.println(a1 == b1); //② false
Integer a2 = 127;
Integer b2 = 127;
System.out.println(a2 == b2); //③ true
解析:对于①,由于a和b都是基本数据类型,==对比的时候采用的是值对比,所以相同,打印true
对于②和③,由于是对包装类型进行对于,所以比较的是引用,然后,对于整数,-128到127之间的数据存在了常量缓冲区,所以a2和b2并没有重新创建对象,而是指定了同一个地址,所以③为true,而a1和b1超出了范围,会创建出新的对象,所以不同,②打印false
a << 3
都可以做逻辑运算符,&还可以作为位运算符
&&是短路运算符,如果左边的表达式是false,右边不执行
测试方式:
if(b() && a()){
}
boolean a{
打印一句话
return true;
}
boolean b {
打印一句话
return false;
}
方式一:
int tmp = a ;
a = b;
b =tmp;
方式二:
a = a+b;
b = a-b;
a = a-b;
方式三: 4 4 4
a=a^b;
b=a^b;
a=a^b;
方式四
a = (a+b) - (b=a)
结果和第一个值的符号一样,负数取模是负数,整数取模是整数。
== 的作用
基本类型:比较的就是值是否相同
引用类型:比较的就是地址值是否相同
equals 的作用
引用类型:默认情况下,比较的是地址值。
注:不过,我们可以根据情况自己重写该方法。一般重写都是自动生成,比较对象的成员变量值是否相同
这三个类都是List的实现,都是有序可重复的集合
ArrayList和Vector是基于数组的,存放在连续的内存块,LinkedList底层基于链表的可以分散存储;
所以ArrayList和Vector查询快(通过索引也就是下标直接访问),增删慢(需要移动后面的元素);而LinkedList(链表)查询慢(需要基于指针从头一个一个查找,最坏的情况要遍历整个集合才能找到),增删快(指针移动到指定的位置之后);
ArrayList线程不安全,Vector线程安全(使用synchronized关键字锁定方法),然而,Vector性能较低,所以一般不怎么使用(并没有废弃),建议使用下面的方式代替Vector:
Collections.synchronizedList(List
LinkedList线程不安全,如果需要使用线程安全的链表List,有两种处理方法:
ArrayList构造的时候,大小为0,第一次添加元素的时候,容量变成为默认大小10(构造没有指定容量的情况下);当元素个数超过集合容量的时候进行扩容,扩容方式为当前容量的1.5倍。
Vector构造的时候容量就是10;当元素个数超过集合容量的时候进行扩容,扩容为原来的2倍(如果构造的时候指定了增加元素个数,就按这个进行增长);
这三个类都是Map的实现,也就是说,都是双列的集合,都是存放键值对的,底层实现都是基于数组+链表的结构,也就是hash表。
1) 底层结构
除了LinkedHashMap之外,都是基于数组+链表的结构,而LinkedHashMap还在此基础上维护了一个双向链表,用来保证元素插入的顺序,所以LinkedHashMap可以保证元素的插入顺序,其他的几个则不可以
2) null值要求
HashMap的key和值都可以为null;HashTable的键和值都不能为null;ConcurrentHashMap的键和值都不能为null;
3) 容量和增长方式
HashMap : 默认大小16,负载因子0.75,当hash表的容量超过负载因子的时候开始扩容,扩容为原始容量的2倍。 到达16*0.75的时候进行扩容,为16*2=32
HashTable:初始容量为11,负载因子为0.75。超过负载因子*容量开始扩容,扩容为旧的容量*2+1。 到达8.25的时候进行扩容,为11*2+1=23
4). 安全性及性能
HashMap是线程不安全的,所以效率高;HashTable是基于synchronized实现的线程安全的Map,效率较低;ConCurrentHashMap线程安全,但是锁定的只是一部分代码,所以效率比HashTable高。
会在某个key不再被引用的时候,remove掉这个key
Map
// 添加键值对
wmap.put(w1, "w1");
wmap.put(w2, "w2");
wmap.put(w3, "w3");
// 打印出wmap
System.out.println("原始Map" + wmap);
// ---- 测试 WeakHashMap 的自动回收特性 ----
// 将w1设置null。
// 这意味着“弱键”w1再没有被其它对象引用,调用gc时会回收WeakHashMap中与“w1”对应的键值对
w1 = null;
// 内存回收。这里,会回收WeakHashMap中与“w1”对应的键值对
System.gc();
HashSet底层其实存放了一个HashMap,往set里面添加的元素放在了HashMap的key里面
以HashSet为例,因为底层实现是HashMap,而HashMap保证不了元素插入的顺序(key通过计算hashcode放在了hash表),所以,HashSet也保证不了元素顺序。
当我们对某一个元素进行计算得到一个值,要进行插入操作的时候,发现这个位置已经被其他元素占用了,这种情况就是我们说的哈希碰撞,也叫哈希冲突
①. 开放定址法:发生冲突,继续寻找下一块未被占用的存储地址。如:线性探测、平方探测、随机探测。
②. 再hash:也就是有多个hash函数,当一个函数计算产生冲突之后,再用另外一个函数计算。
③. 公共溢出区:把产生冲突的元素放在一个公共的溢出区里面;
④. 链地址法:采用数组+链表的结构。HashMap采用的就是这种方式
会插在链的头部,因为这样的话,不需要把指针移动到最后一个元素进行插入操作,效率更高。
要求重写hashcode和equals方法。 ★★★★★★★★★★★
不同的key hashcode可能是相同的
实例: https://blog.csdn.net/oqkdws/article/details/79880269
如果要对List集合里面的元素排序,有如下两种方式:
让定义的类实现Comparable接口,重写compareTo方法;然后调用(cellections集合类中的排序方法)java.util.Collections.sort(List
1、实现comparable接口,2、重写方法、调用collections.sort()方法
先如下编写一个排序器:
static class MyComparator implements Comparator
@Override
public int compare(User o1, User o2) {
return o1.getId() - o2.getId();
}
}
然后调用
Collections.sort(List
public static void main(String[] args) {
List
Collections.sort(uList, new MyComparator());
}
总结方法二:
实现或者用匿名内部类的方法实现comparator接口 ,重写compare()方法,然后再调用
Collections.sort(List
相同点:都是要实现一个接口,然后重写接口中的方法,最后调用collections的sort()方法。
可以使用栈Stack
这三个类都是用来处理字符串的。
String是不可变字符串,StringBuffer和StringBuilder是可变字符串。
一旦通过StringBuffer生成了最终想要的字符串,就可以调用它的toString()方法将其转换为一个String对象。
StringBuilder类也代表可变字符串对象。实际上,StringBuilder和StringBuffer基本相似,两个类的构造器和方法也基本相同。不同的是:StringBuffer是线程安全的,而StringBuilder则没有实现线程安全功能,所以性能略高。
StringBuffer是线程安全的,效率较低;StringBuilder是线程不安全的,效率高一些。
String有length()方法,数组没有,有lenth属性。
1个或者2个,因为new,一定会在堆中开辟空间,如果”123”在字符串常量池已经存在,就不会再字符串常量池中创建对象了,这样就只会有1个;如果串池(字符串常量池)中没有,那么就会在串池中创建一个对象,这样,就有两个对象。★★★★★★★★
Java异常是Java提供的一种识别及响应 错误的一致性 的机制。
编译时异常:
1. ClassNotFoundException(类找不到)
2. FileNotFoudException(文件找不到异常)
3. NoSuchMethodException(没有这个方法)
4. SQLException(SQL异常)
5. ParseException(解析异常)
6. IOExeception(IO异常)
运行时异常有:
1. NullPointException(空指针)
2. ArrayIndexOutOfBoundsException(数组下标越界)
3. ClassCastException(类型转换异常)
4. IllegalArgumentException(参数错误异常)
5. ArithmeticException(算术异常,如1/0等)
6. SecurityException(安全异常)
通常,处理异常有两种方式:捕获和抛出
1、throws出现在方法函数头,表示向上抛出异常;而throw出现在函数体,跟的是异常对象实例。
2、throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常。
如果catch里面有return语句,请问finally的代码还会执行吗?如果会,请问是在return前还是return后?
解答:
Finally语句的执行与return的关系:
finally语句在return语句执行之后return返回之前执行的
finally块中的return语句会覆盖try块中的return返回
如果finally语句中没有return语句覆盖返回值,那么原来的返回值可能因为finally里的修改而改变也可能不变
try块里的return语句在异常的情况下不会被执行,这样具体返回哪个看情况
当发生异常后,catch中的return执行情况与未发生异常时try中return的执行情况完全一样
只看总结
最后总结:finally块的语句在try或catch中的return语句执行之后返回之前执行;且finally里的修改语句可能影响也可能不影响try或catch中 return已经确定的返回值,若finally里也有return语句则覆盖try或catch中的return语句直接返回。否则不影响。
Finally 语句一定会执行
Finally
所以会把try catch的return给覆盖掉;
①. 运行时异常有:
NullPointException(空指针)
ArrayIndexOutOfBoundsException(数组下标越界)
ClassCastException(类型转换异常)
IllegalArgumentException(参数错误异常)
ArithmeticException(算术异常,如1/0等)
SecurityException(安全异常)
ConcurrentModificationException(同步修改异常,快速失败异常,发生在集合迭代的时候调用List.add或者list.remove方法)
②. 编译时异常:
ClassNotFoundException(类找不到)
FileNotFoudException(文件找不到异常)
NoSuchMethodException(没有这个方法)
SQLException(SQL异常)
ParseException(解析异常)
IOExeception(IO异常)
抽象、封装、继承、多态(重写体现)。
好处:
隐藏实现细节,提供公共的访问方式;
提高了代码的复用性;
提高安全性;
封装原则:
将不需要对外提供的内容都隐藏起来;
把属性隐藏,提供公共方法对其访问;
子类继承父类的所有状态和行为,同时添加自身的状态和行为。
方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写
(override)实现的是运行时的多态性(也称为后绑定)。
好处
特点
方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写
(override)实现的是运行时的多态性(也称为后绑定)。
(1)加载父类(以下序号相同,表明初始化是按代码从上到下的顺序来的)
1.为父类的静态属性分配空间并赋于初值 (加载父类的静态属性)
1.执行父类静态初始化代码块; (加载父类的静态代码块)
(2)加载子类
2.为子类的静态属性分配空间并赋于初值 (加载子类的静态属性)
2.执行子类的静态代码块的内容; (加载子类的静态代码块)
(3)加载父类构造器
3.初始化父类的非静态属性并赋于初值 (初始化父类的非静态属性)
3.执行父类的非静态代码块; (初始化父类的非静态代码块)
4.执行父类的构造方法; (初始化父类的构造方法)
(4)加载子类构造器
5.初始化子类的非静态属性并赋于初值 (初始化子类的非静态属性)
5.执行子类的非静态代码块; (初始化子类的非静态代码块)
6.执行子类的构造方法. (初始化子类的构造方法)
数字一样的,哪个在前面,哪个先执行。
总之一句话,静态代码块内容先执行(父先后子),接着执行父类非静态代码块和构造方法,然后执行子类非静态代码块和构造方法。
都是用来抽取公共的特性的;都不能初始化;所以都没有构造器 ★★★★★
接口使用interface定义,抽象类使用abstract class定义;
一个类可以实现多个接口,之间用逗号隔开;只能继承一个抽象类;一个接口可以继承多个接口;
接口里面的属性都是静态常量,都是使用public static final修饰的,即便没有写也是;
抽象类里面可以有普通的类变量;
接口里面的方法都是抽象方法,即都没有方法体(注意从jdk8之后是可以有的,使用default修饰方法);
抽象类里面可以没有抽象方法,也可以有(使用abstract修饰);
一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
进程是程序运行的基本单位,也是是资源分配和调度的基本单位,线程是进程运行的基本执行单元,也是是CPU调度和分派的基本单位
创建线程有三种方法:
线程有五个状态:新建、就绪、运行、阻塞、死亡。
状态转换图如下:
美[ˈdiːmən]
美[praɪˈɔːrəti]
使用的是start,因为如果执行run方法,那么就是一个普通的方法调用,而不是启动了一个线程
创建和销毁线程都需要消耗一定的时间和系统资源,为了节约时间,可以使用线程池来存放线程,线程在使用完毕之后,不会直接销毁,而是存放在线程池,这样,下一次使用的时候就不用创建了。
Executors提供了四个方法来创建线程池:
美[ˈskedʒuːld]
① 非静态方法 锁的是当前对象
public synchronized void sellTicket() {}
等价于
synchronized (this) {}
② 静态方法 锁的是当前类
public synchronized static void sellTicket() {}
等价于
synchronized (SellTicket.class) {}
③ 代码块(对象锁) 锁的对象是任意类型的,所以new的是object
有一个多个线程共享的对象来做作为锁
private static Object lock = new Object();
synchronized (o) {
}
两者都是用来同步代码的。
不同:
①.synchronized是java内置的关键字,在jvm层面;而Lock是一个接口,常用的实现为ReentrantLock;美[ˌriˈɛntrənt]
被多个线程访问的变量为共享变量。 https://www.cnblogs.com/dolphin0520/p/3920373.html
在并发编程中,我们通常会遇到以下三个问题:原子性问题,可见性问题,有序性问题。★★★★★★★★★★★
缓存一致性协议。最出名的就是Intel 的MESI协议,MESI协议保证了每个缓存中使用的共享变量的副本是一致的。它核心的思想是:当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。
Java内存模型(Java Memory Model,JMM)来屏蔽各个硬件平台和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果。那么Java内存模型规定了哪些东西呢,它定义了程序中变量的访问规则,往大一点说是定义了程序执行的次序。注意,为了获得较好的执行性能,Java内存模型并没有限制执行引擎使用处理器的寄存器或者高速缓存来提升指令执行速度,也没有限制编译器对指令进行重排序。也就是说,在java内存模型中,也会存在缓存一致性问题和指令重排序的问题。
指令重排序:一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。
处理器在进行重排序时是会考虑指令之间的数据依赖性,如果一个指令Instruction 2必须用到Instruction 1的结果,那么处理器会保证Instruction 1会在Instruction 2之前执行。
指令重排序不会影响单个线程的执行,但是会影响到线程并发执行的正确性。(会影响到多线程)
x++和 x = x+1包括3个操作:读取x的值,进行加1操作,写入新的值。(自增不是原子性的)
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
1). 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。(解决缓存一致性问题)
2). 禁止进行指令重排序(编译原理)。
Volatile :不能保证原子性,但可以保证可见性和一定程度上的有序性。
所以volatile的使用是在保证原子性的前提下
保证有序性例如://x、y为非volatile变量
//flag为volatile变量
x = 2; //语句1
y = 0; //语句2
flag = true; //语句3
x = 4; //语句4
y = -1; //语句5
ThreadLocal是一个本地 线程副本 变量工具类。ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。各个线程之间的变量互不干扰。在高并发场景下,可以实现无状态的调用,特别适用于各个线程依赖不同的变量值完成操作的场景
ThreadLocal的作用:为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
ThreadLocalMap其实就是ThreadLocal的一个静态内部类,里面定义了一个Entry来保存数据,而且还是继承的弱引用。在Entry内部使用ThreadLocal作为key,使用我们设置的value作为value。
ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。
在进行get之前,必须先set,否则会报空指针异常,当然也可以初始化一个,但是必须重写initialValue()方法。
ThreadLocal是一个弱引用,当为null时,会被当成垃圾回收(不是ThreadLocalMap)
如果突然ThreadLocal是null了,也就是要被垃圾回收器回收了,但是此时我们的ThreadLocalMap生命周期和Thread的一样,它不会回收,这时候就出现了一个现象。那就是ThreadLocalMap的key没了,但是value还在,这就造成了内存泄漏。
解决办法:使用完ThreadLocal后,执行remove操作,避免出现内存溢出情况。
从ThreadLocalMap移除即可。
https://baijiahao.baidu.com/s?id=1647621616629561468&wfr=spider&for=pc
AtomicInteger的原理,主要是通过Usafe的方式来完成的。Usafe又是通过CAS机制来实现的。
CAS (compareAndSwapInt)即比较并替换,实现并发算法时常用到的一种技术。CAS操作包含三个操作数——内存位置、预期原值及新值。执行CAS操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值,否则,处理器不做任何操作。
Java中自增或自减,若没有进行额外的同步操作,在多线程环境下就是线程不安全的,因为这个操作不是原子性的。
x++和 x = x+1包括3个操作:读取x的值,进行加1操作,写入新的值。(自增不是原子性的)
AtomicInteger 类为我们提供了一个可以进行原子性读和写操作的 int 变量,它还包含一系列先进的原子性操作,比如 addAndGet。类似的类还有AtomicLong等。
例子:
假如某个时刻变量inc的值为10,
线程1对变量进行自增操作,线程1先读取了变量inc的原始值,然后线程1被阻塞了;
然后线程2对变量进行自增操作,线程2也去读取变量inc的原始值,由于线程1只是对变量inc进行读取操作,而没有对变量进行修改操作,所以不会导致线程2的工作内存中缓存变量inc的缓存行无效,所以线程2会直接去主存读取inc的值,发现inc的值时10,然后进行加1操作,并把11写入工作内存,最后写入主存。
然后线程1接着进行加1操作,由于已经读取了inc的值,注意此时在线程1的工作内存中inc的值仍然为10,所以线程1对inc进行加1操作后inc的值为11,然后将11写入工作内存,最后写入主存。
那么两个线程分别进行了一次自增操作后,inc只增加了1。
https://blog.csdn.net/zlfprogram/article/details/76274211
java并发工具包是jdk5提供的一个工具包,用来帮助我们进行并发编程,提供了一些解决并发问题的API,如AtomicInteger,BlockingQueue(LinkedBlockQueue),ExecutorService,Lock,CurrentHashMap等
强大的Executor框架:可以创建各种不同类型的线程池,调度任务运行等,绝大部分情况下,不再需要自己从头实现线程池和任务调度器。★★★★★★★★★★
并发包里提供的线程安全Map、List和Set。
https://blog.csdn.net/weixin_46217160/article/details/108743389?utm_term=%E7%BA%BF%E7%A8%8B%E4%B9%8B%E9%97%B4%E7%9A%84%E9%80%9A%E8%AE%AF%E6%96%B9%E5%BC%8F&utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~sobaiduweb~default-6-108743389&spm=3001.4430
多线程通讯:多个线程操作同一个资源,但是操作的动作不同。 ★ ★ ★ ★ ★ ★ ★
需求:第一个线程写入(input)用户,另一个线程读取(out)用户。实现读一个,写一个操作。
生产者线程(inputThread):叫做提供(发布)资源,做写的操作
消费者线程(outThread):叫做利用(消费)资源,做读的操作
①.共享变量 (被多个线程访问的变量为共享变量)
线程安全问题 ,因为不具备原子性
②.管道流 (管道输入/输出流的形式)
管道流是一种使用比较少的线程间通信方式,管道输入/输出流和普通文件输入/输出流或者网络输出/输出流不同之处在于,它主要用于线程之间的数据传输,传输的媒介为管道。
管道输入/输出流主要包括4种具体的实现:PipedOutputStrean、PipedInputStrean、PipedReader和PipedWriter,前两种面向字节,后两种面向字符。
java的管道的输入和输出实际上使用的是一个循环缓冲数组来实现的,默认为1024,输入流从这个数组中读取数据,输出流从这个数组中写入数据,当这个缓冲数组已满的时候,输出流所在的线程就会被阻塞,当向这个缓冲数组为空时,输入流所在的线程就会被阻塞。
③.消息传递:线程之间没有公共的状态,线程之间必须通过明确的发送信息来显示的进行通信。
如:wait/notify等待通知方式
join方式
等待通知机制就是将处于等待状态的线程将由其它线程发出通知后重新获取CPU资源,继续执行之前没有执行完的任务。最典型的例子生产者--消费者模式
有一个产品队列,生产者想要在队列中添加产品,消费者需要从队列中取出产品,如果队列为空,消费者应该等待生产者添加产品后才进行消费,队列为满时,生产者需要等待消费者消费一部分产品后才能继续生产。队列可以认为是java模型里的临界资源,生产者和消费者认为是不同的线程,它们需要交替的占用临界资源来进行各自方法的执行,所以就需要线程间通信。
生产者--消费者模型主要为了方便复用和解耦,java语言实现线程之间的通信协作的方式是等待/通知机制
等待/通知机制提供了三个方法用于线程间的通信
wait()当前线程释放锁并进入等待(阻塞)状态notify()唤醒一个正在等待相应对象锁的线程,使其进入就绪队列,以便在当前线程释放锁后继续竞争锁notifyAll()唤醒所有正在等待相应对象锁的线程,使其进入就绪队列,以便在当前线程释放锁后继续竞争锁
等待/通知机制是指一个线程A调用了对象Object的wait()方法进入等待状态,而另一线程B调用了对象Object的notify()或者notifyAll()方法,当线程A收到通知后就可以从对象Object的wait()方法返回,进而执行后序的操作。线程间的通信需要对象Object来完成,对象中的wait()、notify()、notifyAll()方法就如同开关信号,用来完成 等待方和通知方的交互。
https://blog.csdn.net/zhaohong_bo/article/details/89552188
①管道
②命名管道
③信号量
④消息队列 ====== MQ
⑤共享内存
⑥内存映射
⑦套接字
1、管道pipe:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
2、命名管道FIFO:有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
3、消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
4、共享存储SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
5、信号量Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
6、套接字Socket:套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
7、信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
8、内存映射(mapped memory):内存映射允许任何多个进程间通信,每一个使用该机制的进程通过把一个共享的文件映射到自己的进程地址空间来实现它
基本思想: 冒泡排序,类似于水中冒泡,较大的数沉下去,较小的数慢慢冒起来,假设从小到大,即为较大的数慢慢往后排,较小的数慢慢往前排。
直观表达,每一趟遍历,将一个最大的数移到序列末尾。
算法描述:
比较相邻的元素,如果前一个比后一个大,交换之。
第一趟排序第1个和第2个一对,比较与交换,随后第2个和第3个一对比较交换,这样直到倒数第2个和最后1个,将最大的数移动到最后一位。
第二趟将第二大的数移动至倒数第二位
......
因此需要比较n-1趟。
https://www.cnblogs.com/bigdata-stone/p/10464243.html
N个数字要排序完成,总共进行N-1趟排序,每i趟的排序次数为(N-i)次,
private static void bubbleSort(int[] arr) {
if(arr==null || arr.length < 2 ){
return;
}
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - i -1; j++) { // 这里说明为什么需要-1
if (arr[j] > arr[j + 1]) { // 如果前一个数大于后一个数
int temp = arr[j]; // 把前一个数放在一个临时变量
arr[j] = arr[j + 1]; // 把后一个数赋值给前一个数
arr[j + 1] = temp; // 临时变量中是前一个大的数,赋值给后一个数
}
}
}
}
public static void bubbleSort(int[] arr) { for (int j = 1; j < arr.length; j++) {// 外层循环控制比较的趟数,比较的趟数是数组长度-1 // 第j趟比较中 少比较的个数为j-1个,所以i for (int i = 0; i < arr.length - j; i++) { // 内存循环控制要对比的元素个数 if (arr[i] > arr[i + 1]) {// 前面的元素和后面的元素做对比,如果前面的大于后面的,交换位置 int tmp = arr[i]; arr[i] = arr[i + 1]; arr[i + 1] = tmp; } } } } |
https://blog.csdn.net/shujuelin/article/details/82423852
https://www.cnblogs.com/captainad/p/10999697.html(代码实现)
1 /**
2 * 快速排序
3 * @param array
4 */
5 public static void quickSort(int[] array) {
6 int len;
7 if(array == null
8 || (len = array.length) == 0
9 || len == 1) {
10 return ;
11 }
12 sort(array, 0, len - 1);
13 }
14
15 /**
16 * 快排核心算法,递归实现
17 * @param array
18 * @param left
19 * @param right
20 */
21 public static void sort(int[] array, int left, int right) {
22 if(left > right) {
23 return;
24 }
25 // base中存放基准数
26 int base = array[left];
27 int i = left, j = right;
28 while(i != j) {
29 // 顺序很重要,先从右边开始往左找,直到找到比base值小的数
30 while(array[j] >= base && i < j) {// 如果右面的数大于基准数,那么继续寻找比基准数小的数,找到后跳出循环(右找小)
31 j--;
32 }
33
34 // 再从左往右边找,直到找到比base值大的数
35 while(array[i] <= base && i < j) {// 如果左面的数小,那么继续寻找比基准数大的数,找到后跳出循环(左找大)
36 i++;
37 }
38
39 // 上面的循环结束表示找到了位置或者(i>=j)了,交换两个数在数组中的位置
40 if(i < j) {
41 int tmp = array[i];
42 array[i] = array[j];
43 array[j] = tmp;
44 }
45 }
46
47 // 将基准数放到中间的位置(基准数归位)
48 array[left] = array[i]; // 把i位置的数放到左面头部
49 array[i] = base; // 把基准数放在i位置处
50
51 // 递归,继续向基准的左右两边执行和上面同样的操作
52 // i的索引处为上面已确定好的基准值的位置,无需再处理
53 sort(array, left, i - 1); // 左侧数组
54 sort(array, i + 1, right); // 右侧数组
55 }
public void qsort(int array[]) { if (array.length > 1) { _qsort(array, 0, array.length - 1); // 参数二:数组第一个数的下标,参数二:数组中最后一个数的下标 } } /** * 一趟快速排序 * * @param array */ private void _qsort(int[] array, int low, int high) {//参数二:数组第一个数的下标,参数二:数组中最后一个数的下标 if (low < high) { int middle = getMiddle(array, low, high); _qsort(array, low, middle - 1); _qsort(array, middle + 1, high); } } /** * 得到中间值 ===》 也就是基准数应该在的中间的位置 右面找小于等于基准数的,左面找大于等于基准数的 */ private int getMiddle(int[] array, int low, int high) { int tmp = array[low]; while (low < high) { while (low < high && array[high] >= tmp) //如果右面的数比基准数大,继续循环寻找合适的数,找到就跳出循环 high--; array[low] = array[high]; while (low < high && array[low] <= tmp) low++; array[high] = array[low]; } array[low] = tmp; return low; } |
1.必须采用顺序存储结构。 数组结构是有序的 从大到小或者从小到大
2.必须按关键字大小有序排列。
public static void main(String[] args) { int[] a = {1,6,79,80,45,23,10}; Arrays.sort(a); int binarySearch = binarySearch(a, 23, 0, a.length-1); System.out.println(binarySearch); } public static int binarySearch(int[] arr, int key, int low, int high) { if (key < arr[low] || key > arr[high] || low > high) { // 判断这个数是否存在 因为是有序的 所以如果小于最小的,或者大于最大的,或者最小数大于最大数,表示这个数不存在或者该数列是个错误的数列 return -1; } int middle = (low + high) / 2; // 初始中间位置 if (arr[middle] > key) { // 比关键字大则关键字在左区域 return binarySearch(arr, key, low, middle - 1); } else if (arr[middle] < key) { // 比关键字小则关键字在右区域 return binarySearch(arr, key, middle + 1, high); } else { return middle; } } |
常见的写法有七种,要求必须掌握1、2、3、7三种。
下面的写法中都缺少了 私有的构造方法
第一种:懒汉(线程不安全) public class Singleton { private static Singleton instance; // 只声明不赋值 public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } 这种写法lazy loading很明显,但是致命的是在多线程不能正常工作。 第二种:懒汉(线程安全) public class Singleton { private static Singleton instance; public static synchronized Singleton getInstance() { // 只是用synchronized 修饰了方法 if (instance == null) { instance = new Singleton(); } return instance; } } 这种写法能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是,遗憾的是,效率很低,99%情况下不需要同步。 第三种:饿汉 public class Singleton { private static Singleton instance = new Singleton(); // 声明并且赋值 public static Singleton getInstance() { return instance; } } 这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到lazy loading的效果。 第四种:饿汉(变种) public class Singleton { private static Singleton instance = null; static { instance = new Singleton(); } public static Singleton getInstance() { return instance; } } 表面上看起来差别挺大,其实更第三种方式差不多,都是在类初始化即实例化instance。 第五种:(静态内部类)的单例模式 public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); // 常量一般用全大写表示 } public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } } // 用静态内部类的方式 做到了饿汉模式不能延迟加载 这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟第三种和第四种方式不同的是(很细微的差别):第三种和第四种方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比第三和第四种方式就显得很合理。 第六种:(枚举) public enum Singleton { INSTANCE; public void whateverMethod() { } } 这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊,不过,个人认为由于1.5中才加入enum特性,用这种方式写不免让人感觉生疏,在实际工作中,我也很少看见有人这么写过。 第七种:(双重校验锁)的单例模式 https://blog.csdn.net/yuan_qh/article/details/99962482 public class Singleton { private volatile static Singleton singleton; // 只声明,没有赋值,且用volatile 修饰
public static Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { // 用同步锁,锁了当前类对象 if (singleton == null) { singleton = new Singleton(); } } } return singleton; } } |
为什么是双重校验锁实现单例模式呢? ★★★★★★★★★★
第一次校验:也就是第一个if(singleton==null),这个是为了代码提高代码执行效率,由于单例模式只要一次创建实例即可,所以当创建了一个实例之后,再次调用getInstance方法就不必要进入同步代码块,不用竞争锁。直接返回前面创建的实例即可。
第二次校验:也就是第二个if(singleton==null),这个校验是(多线程情况下)防止二次创建实例,假如有一种情况,当singleton还未被创建时,线程t1调用getInstance方法,由于第一次判断singleton==null,此时线程t1准备继续执行,但是由于资源被线程t2抢占了,此时t2也调用getInstance方法,同样的,由于singleton并没有实例化,t2同样可以通过第一个if,然后继续往下执行,同步代码块,第二个if也通过,然后t2线程创建了一个实例singleton。此时t2线程完成任务,资源又回到t1线程,t1此时也进入同步代码块,如果没有这个第二个if,那么,t1就也会创建一个singleton实例,那么,就会出现创建多个实例的情况,但是加上第二个if,就可以完全避免这个多线程导致多次创建实例的问题。
所以说:两次校验都必不可少。
volatile也必不可少,volatile关键字可以防止jvm指令重排优化:
因为 singleton = new Singleton() 这句话可以分为三步:
1. 为 singleton 分配内存空间;
2. 初始化 singleton;
3. 将 singleton 指向分配的内存空间。
但是由于JVM具有指令重排的特性,执行顺序有可能变成 1-3-2。 指令重排在单线程下不会出现问题,但是在多线程下会导致一个线程获得一个未初始化的实例。例如:线程T1执行了1和3,此时T2调用 getInstance() 后发现 singleton 不为空,因此返回 singleton, 但是此时的 singleton 还没有被初始化。
使用 volatile 会禁止JVM指令重排,从而保证在多线程下也能正常执行。
volatile 还能保证变量在多线程运行时的可见性
JAVA反射机制是在程序运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个属性和方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制
先调用Class.getDeclaredField(String fieldName)方法获取到属性;
调用Field.setAccessible(true)暴力访问属性(添加权限)
调用Field.set(Object,Object)方法设置值;(参数一:要被修改值的对象,参数二:要赋的值)
例如:address.set(stu,”北京市”);
反射打破了JAVA的封装特性;
射相当于一系列解释操作,通知jvm要做的事情,性能比直接的java代码要慢很多。
堆:存放对象实例
Java 虚拟机所管理的内存中最大的一块,Java 堆是所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。
栈(虚拟机栈):存储局部变量,操作数栈,动态链接,方法出口信息等
Java虚拟机栈也是线程私有的,它的生命周期和线程相同,每次方法调用的数据都是通过栈传递的。
局部变量表主要存放了编译器可知的各种数据类型(boolean、byte、char、short、int、float、long、double)
栈是运行时的单位,而堆是存储的单位。即: 栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。堆解决的是数据存储的问题,即数据怎么放、放在哪儿。
每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧(stack Frame) ,对应着一次次的Java方法调用。
栈主管Java程序的运行,它保存方法的局部变量(8种基本数据类型、对象的引用地址)、部分结果,并参与方法的调用和返回。
对于栈来说不存在垃圾回收问题GC,但存在内存溢出问题OOM
每个方法执行伴随着进栈(入栈、压栈) 和 执行结束后的出栈工作
方法区:存放类信息,常量(字符串常量池,-128-127),静态变量,编译后的代码(字节码文件)★★★★★★★★★★★★★★
HotSpot VM
J9 VM
Zing VM
sun
IBM
甲骨文 Oracle
-Xms:初始堆大小
-Xmx:最大堆大小
-Xmn:年轻代大小
-XX:NewSize:设置年轻代大小
-XX:MaxSize:设置年轻代最大大小
-XX:PermSize:设置永久代初始大小,内存的1/64
-XX:MaxPermSize:设置永久代最大大小,内存的1/4
-Xss:每个线程的堆栈大小
参看:
https://www.cnblogs.com/jianyungsun/p/6911380.html
垃圾回收是java语言中的一种机制,用来对不再使用的“垃圾”对象占用的空间进行回收;而不用程序员自己手动的释放内存,减少内存泄漏的发生。
内存泄漏简单理解:key不在了或者是null,但是value还在
https://blog.csdn.net/yrwan95/article/details/82829186
主要有四种算法:
标记清除法、标记整理法、copying算法、分代回收算法
①. 标记清除法
标记-清除算法采用从根集合进行扫描,标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。但是有一个比较严重的问题就是容易产生内存碎片,碎片太多可能会导致后续过程中需要为大对象分配空间时无法找到足够的空间而提前触发新的一次垃圾收集动作。
适用于对象存活率高的情况
为了解决Copying算法的缺陷(以空间换时间),充分利用内存空间,提出了Mark-Compact算法。该算法标记阶段和Mark-Sweep一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。
适用于对象存活率低的情况
它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。
这种算法虽然实现简单,运行高效且不容易产生内存碎片,但是却对内存空间的使用做出了高昂的代价,因为能够使用的内存缩减到原来的一半。很显然,Copying算法的效率跟存活对象的数目多少有很大的关系,如果存活对象很多,那么Copying算法的效率将会大大降低。(以空间换时间)
现在商用的jvm中都采用了这种算法来回收新生代,因为新生代的对象基本上都是朝生夕死的,存活下来的对象约占10%左右,所以需要复制的对象比较少,采用这种算法效率比较高。
④.分代回收
分代收集算法是目前大部分JVM的垃圾收集器采用的算法。
老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。
其中分代回收算法把堆内存分为三个区:新生代、老年代(在新生代经过多次垃圾回收仍然存活的对象,会存放到老年代)、永久代(包含类、方法等)
新生代
新生代都采取复制算法 copying
新生代内存按照8:1:1的比例分为一个eden区和两个survivor(survivor0,survivor1)区,大部分对象在Eden区中生成。回收时先将eden区存活对象复制到一个survivor0区,然后清空eden区,当这个survivor0区也存放满了时,则将eden区和survivor0区存活对象复制到另一个survivor1区,然后清空eden和这个survivor0区,此时survivor0区是空的,然后将survivor0区和survivor1区交换,即保持survivor1区为空, 如此往复。
年老代(Old Generation)
老年代的特点是每次回收都只回收少量对象,一般使用的是标记-整理算法
1.在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。
2.内存比新生代也大很多(大概比例是1:2),当老年代内存满时触发Major GC即Full GC,Full GC发生频率比较低,老年代对象存活时间比较长,存活率标记高。
持久代(Permanent Generation)
用于存放静态文件,如Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate 等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。
串行:单线程的方式
并行:多线程的方式
// =====================================================================
1.Serial/Serial Old收集器 是最基本最古老的收集器,它是一个单线程收集器,并且在它进行垃圾收集时,必须暂停所有用户线程。Serial收集器是针对新生代 单线程的收集器,采用的是Copying算法,Serial Old收集器是针对老年代 单线程的收集器,采用的是Mark-Compact算法。它的优点是实现简单高效,但是缺点是会给用户带来停顿。
Copying==》新生代
标整==》老年代
2.ParNew收集器 是Serial收集器的多线程版本,使用多个线程进行垃圾收集。
3.Parallel Scavenge收集器 是一个新生代的多线程收集器(并行收集器),它在回收期间不需要暂停其他用户线程,其采用的是Copying算法,该收集器与前两个收集器有所不同,它主要是为了达到一个可控的吞吐量。
4.Parallel Old收集器 是Parallel Scavenge收集器的老年代版本(并行收集器),使用多线程和Mark-Compact算法。
5.CMS(Concurrent Mark Sweep)收集器 是一种以获取最短回收停顿时间为目标的收集器,它是一种并发收集器,采用的是Mark-Sweep(标记-清除)算法。
6.G1收集器 是当今收集器技术发展最前沿的成果,它是一款面向服务端应用的收集器,它能充分利用多CPU、多核环境。因此它是一款并行与并发收集器,并且它能建立可预测的停顿时间模型。
Garbage Collection (垃圾回收)
①、Scavenge GC
一般情况下,当新对象生成,并且在Eden(新生代的一个区)申请空间失败时,就会触发Scavenge GC,对新生区进行GC。
Minor GC:eden区满时,触发MinorGC(即申请一个对象时,发现eden区不够用,则触发一次MinorGC)
美[ˈmaɪnər]
两者一样
②、Full GC
对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个堆进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC:
1.年老代(Tenured)被写满
2.持久代(Perm)被写满
3.System.gc()被显示调用
4.上一次GC之后Heap(堆)的各域分配策略 动态变化
有两种方式引用计数法、和可达性分析法
①.引用计数法
引用计数算法是垃圾回收器中的早期策略,在这种方法中,堆中的每个对象实例都有一个引用计数器,当一个对象被创建时,且该对象实例分配给一个变量,该变量计数设置为1 ,当任何其他变量赋值为这个对象的引用时,计数加1 ,(a=b ,则b引用的对象实例计数器+1)但当一个对象实例的某个引用超过了生命周期或者被设置为一个新值时,对象实例的引用计数器减1,任何引用计数器为0 的对象实例可以当做垃圾收集。 当一个对象的实例被垃圾收集时,它引用的任何对象实例的引用计数器减1。计数器为0,意味着对象独自漂泊在堆中,没人认识它,不可能再被使用,这时就是一个“废柴”,可以回收了。
这种算法,实现简单,判定效率也高,但是有一个致命的缺陷——很难解决对象之间相互引用的问题。
②.可达性分析法(一般都使用这种)
通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,当GC Roots到一个对象不可达时,则证明这个对象是不可用的,可以将其回收。
这个算法很好的解决了引用计数法在处理相互引用时遇到的难题,如下图,object5和object6虽然相互引用,但是由于他们到GC Roots都不可达,因此会被判定为可回收的对象。
可作为GC Roots的对象包括下面几种:
1、虚拟机栈中引用的对象(如main函数中引用的对象);
2、方法区中类静态属性引用的对象;
3、方法区中常量引用的对象;
4、本地方法栈中Native方法引用的对象。
什么是Native Method:
简单地讲,一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C。
栈(虚拟机栈):存储局部变量,操作数栈,动态链接,方法出口信息等
Java虚拟机栈也是线程私有的,它的生命周期和线程相同,每次方法调用的数据都是通过栈传递的。
局部变量表主要存放了编译器可知的各种数据类型(boolean、byte、char、short、int、float、long、double)
方法区:存放类信息,常量(字符串常量池,-128-127),静态变量,编译后的代码(字节码文件)
ThreadLocal的内存泄漏
以下情况会造成内存泄漏:
HashMap是线程不安全的,所以效率高;HashTable是基于synchronized实现的线程安全的Map,效率较低;ConCurrentHashMap线程安全,但是锁定的只是一部分代码,所以效率比HashTable高。
如:
static Vector v = new Vector(); for (int i = 1; i<100; i++) { Object o = new Object(); v.add(o); o = null; } |
①数据库连接,②网络连接,③IO连接等没有显示调用close关闭,不被GC回收导致内存泄露
加载、验证、准备、解析、初始化
启动类加载器主要加载的是JVM自身需要的类,这个类加载使用C++语言实现的,是虚拟机自身的一部分,它负责将
扩展类加载器是指Sun公司(已被Oracle收购)实现的sun.misc.Launcher$ExtClassLoader类,由Java语言实现的,是Launcher的静态内部类,它负责加载
也称应用程序加载器是指 Sun公司实现的sun.misc.Launcher$AppClassLoader。它负责加载系统类路径java -classpath或-D java.class.path 指定路径下的类库,也就是我们经常用到的classpath路径,开发者可以直接使用系统类加载器,一般情况下该类加载是程序中默认的类加载器,通过ClassLoader#getSystemClassLoader()方法可以获取到该类加载器。
泛型、枚举、自动装箱/拆箱、for-each循环、静态导入、变长参数、并发工具包。
插入式注解API、AWT提供了Desktop(桌面)和SystemTray(系统托盘)
switch可以使用String了、泛型实例化自动推断(如:List
Collections.sort(names, (String a, String b) -> {
return b.compareTo(a);
});
流(Stream)是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算!”,如:
@Test
public void createStream() {
//1.通过Collection集合提供的stream()方法获取流
List
Stream
//使用流进行迭代
Stream
}
BIO的 Stream 是单向的,而 NIO 的 channel 是双向的。
AIO也是异步非阻塞的IO技术,区别在于,NIO需要使用者线程不停的轮询IO对象,来确定是否有数据准备好可以读了,而AIO则是在数据准备好之后,才会通知数据使用者,这样使用者就不需要不停地轮询了
Jre:java runtime environment,java运行时环境,包含了java虚拟机,java基础类库;
Jdk:java development kit,java开发工具,包含了jre,同时还提供了编译器javac,调试分析工具jconsole等。
总的来说,如果我们开发的话,就安装jdk;运行程序的话,就只需要安装jre;
Path环境变量里面记录的是jdk命令的可执行性文件路径
classpath环境变量里记录的是java类的运行文件所在的目录(其实可以不配置)
javac:编译java文件为class文件
java:运行class文件
jar:打包,如:jar -cvf HelloWorld.jar Hello.class World.class
java –jar:运行Jar包
javadoc:生成文档,如:javadoc Hello.java World.java
javap:反编译class文件,会显示类中可以访问的数据和方法
jps:查看java进程
jstack : java虚拟机进程堆栈跟踪工具, 制作线程Dump
jmap : java虚拟机进程堆内存映射,制作堆Dump
jconsole:检测本地或者远程的java进程的资源和性能消耗,可以用来查看垃圾回收、线程、检查死锁、内存等等。
jvisualvm:能够监控线程,内存情况,查看方法的CPU时间和内存中的对 象,已被GC的对象,反向查看分配的堆栈
基本选择器
层次选择器
表单选择器
过滤选择器
$(“[name=’val’]”):值等于
$(“[name!=’aa’]”):值不等于
$(“[name$=’aa’]”):值以aa结尾
:visiable,:hidden
区别
1.存放位置:
session保存在服务器,cookie保存在客户端
2.存放的形式:
session是以对象的形式保存在服务器,cookie以字符串的形式保存在客户端
3.用途:
session适合做客户的身份验证,cookie适合保存用户的个人设置,爱好等
4.路径:
session不能区分路径,同一用户在访问一个网站期间,所有的session在任何一个地方都可以访问到;cookie中如果设置了参数路径,那么同一个网站下的cookie互相访问不到
5.安全性:
cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,session较cookie更安全一些
6.大小及数量限制:
单个cookie在客户端的限制是3K,就是说一个站点在客户端存放的COOKIE不能3K。不同浏览器所含cookie的最大个数不同,一般30到50个;一般认为session没有大小限制
Session随着保存的数据随着会话结束而销毁,cookie存储的数据可以长期保存在浏览器中
联系
session需要借助cookie才能正常工作,如果客户端完全禁止cookie,session将失效,因为session是由应用服务器维持的一个服务端的存储空间,用户在连接服务器时,会由服务器生成唯一的sesssionid,用该sessionid为标识来存取服务端的session空间。而sessionid存储在cookie中,用户提交页面时会将这个sessionid提交到服务端,来存取session数据.这一过程是不用开发人员干预的,所以一旦客户端禁用cookie,那么session也会失效;
另外:
session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,如果主要考虑到减轻服务器性能方面,应当使用COOKIE
sessionid是服务器和客户端链接时候随机分配的
Request,Response,Session,Application,out,pageContext,config,page,exception等九个
1、request对象
request 对象是 javax.servlet.httpServletRequest类型的对象。 该对象代表了客户端的请求信息,主要用于接受通过HTTP协议传送到服务器的数据。(包括头信息、系统信息、请求方式以及请求参数等)。request对象的作用域为一次请求。
2、response对象
response 代表的是对客户端的响应,主要是将JSP容器处理过的对象传回到客户端。response对象也具有作用域,它只在JSP页面内有效。
3、session对象
session 对象是由服务器自动创建的与用户请求相关的对象。服务器为每个用户都生成一个session对象,用于保存该用户的信息,跟踪用户的操作状态。session对象内部使用Map类来保存数据,因此保存数据的格式为 “Key/value”。 session对象的value可以使复杂的对象类型,而不仅仅局限于字符串类型。
4、application对象
application 对象可将信息保存在服务器中,直到服务器关闭,否则application对象中保存的信息会在整个应用中都有效。与session对象相比,application对象生命周期更长,类似于系统的“全局变量”。
5、out 对象
out 对象用于在Web浏览器内输出信息,并且管理应用服务器上的输出缓冲区。在使用 out 对象输出数据时,可以对数据缓冲区进行操作,及时清除缓冲区中的残余数据,为其他的输出让出缓冲空间。待数据输出完毕后,要及时关闭输出流。
6、pageContext 对象
pageContext 对象的作用是取得任何范围的参数,通过它可以获取 JSP页面的out、request、reponse、session、application 等对象。pageContext对象的创建和初始化都是由容器来完成的,在JSP页面中可以直接使用 pageContext对象。
7、config 对象
config 对象的主要作用是取得服务器的配置信息。通过 pageConext对象的 getServletConfig() 方法可以获取一个config对象。当一个Servlet 初始化时,容器把某些信息通过 config对象传递给这个 Servlet。 开发者可以在web.xml 文件中为应用程序环境中的Servlet程序和JSP页面提供初始化参数。
8、page 对象
page 对象代表JSP本身,只有在JSP页面内才是合法的。 page隐含对象本质上包含当前 Servlet接口引用的变量,类似于Java编程中的 this 指针。
9、exception 对象
exception 对象的作用是显示异常信息,只有在包含 isErrorPage="true" 的页面中才可以被使用,在一般的JSP页面中使用该对象将无法编译JSP文件。excepation对象和Java的所有对象一样,都具有系统提供的继承结构。exception 对象几乎定义了所有异常情况。在Java程序中,可以使用try/catch关键字来处理异常情况; 如果在JSP页面中出现没有捕获到的异常,就会生成 exception 对象,并把 exception 对象传送到在page指令中设定的错误页面中,然后在错误页面中处理相应的 exception 对象。
page、request、session、application(四大作用域)
Jsp的内置标签被称为jsp行为
参考文档:https://www.cnblogs.com/whgk/p/6427759.html
Spring框架简化了传统采用EJB开发java项目的复杂度,并且采用了IOC的思想解除了java项目中三个层次的耦合 还引入了(AOP)
SpringMVC是采用了MVC的设计模式开发的web层框架,主要用于替换原生的servlet
Mybatis是实现数据持久化的功能(完成dao层开发)
Spring是为了简化企业应用开发的一个轻量级开源框架;
有以下优点
从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。
4.3.7.release(release代表是发行版本)
在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
其思想是反转资源获取的方向,传统的资源查找方式要求组件向容器发起请求查找资源,作为回应,容器适时的返回资源;而应用了IOC之后,则是容器主动的将资源推送给它所管理的组件,组件需要做的仅是选择一种合适的方式(属性注入[set函数]、构造器注入…)来接受资源,这种行为成为查找的被动形式。
即由容器动态的将某个依赖关系注入到类之中。
Constructor,调用了有参构造
②、setter注入
byName和byType,注入时调用的是依赖对象的set方法
③、基于注解的注入
@autowired 根据依赖对象的类型 == byType
@Resource(name = “对象名”) == byName
@autowired + @Qualifier == byName
IOC的另一种表达方式,即组件以一些预先定义好的方式(如:setter方法)接受来自容器的资源注入,或者说,需要的资源依赖于别人的注入,相对于IOC而言,这种方式更直接。
IOC容器用来管理Bean的创建,装配等,同时管理所有这些Bean的生命周期,主要有两个:BeanFactory和ApplicationContext这两个接口。
1、BeanFactory:IOC容器的基本实现(不常用)
作用:
①.配置,创建以及管理Bean对象;
②.维护Bean之间的依赖关系;
③.负责Bean对象的生命周期
2、ApplicationContext(应用上下文)
提供了更多的高级特性,是BeanFactory的子接口(更加常用),在初始化上下文时就实例化所有单例的 Bean。
主要实现类:
ClassPathXmlApplicationContext——从类路径下的XML配置文件中加载上下文定义,把应用上下文定义文件作为类资源;
FileSystemXmlApplicationContext——读取文件系统下的XML配置文件并加载上下文定义;
XmlWebApplicationContext——读取Web应用下的XML配置文件并装在上下文定义;
通过现有应用上下文引用ApplicationContext,可以调用应用上下文的getBean()方法从Spring容器中获取Bean;
ApplicationContext作用:
①.读取Bean定义文件;
②.维护Bean之间的依赖关系;
③.国际化的支持;
④.资源访问;
⑤.事件传播;
⑥多配置文件加载
AOP是Spring框架面向切面的编程思想,AOP采用一种称为“横切”的技术,将涉及多业务流程 通用功能抽取并单独封装,形成独立的切面,在合适的时机将这些切面横向切入到业务流程指定的位置中。
AOP(Aspect Oriented Programming),也就是面向切面编程,是对面向对象编程OOP的一种补充;通过“横切”技术剖解开封装的对象内部,把那些公共的行为封装到一个可重用模块,称为“切面”。这样的话,就可以通过切面编写一些与业务无关的逻辑,减少代码的重复,降低模块之间的耦合度,便于开发和维护。常用的场景有日志、事务、权限控制。★★★★★★★★
AOP底层原理是代理(不问不要说,不然别人就立马会问代理和反射);
动态代理通过反射实现
动态代理需要实现InvocationHandler接口,重写Object类中的invoke方法
所以动态代理是一个工具类
代理有静态代理(用得少)和动态代理;
动态代理有jdk代理和cglib代理,jdk的局限是被代理的类必须实现了接口,cglib代理底层基于ASM字节码的,所以效率较高。Spring中这两种都有使用。
Before、After、After-Throwing、After-Returning、Arround
前置 , 后置, 异常 , 最终 , 环绕
常用的场景有日志、事务、权限控制。
Spring AOP的粒度只能到方法级别,不能拦截对象字段的修改
Spring管理的属性有五个方面:★★★★★★★★★★★★★★★
①、propagation:事务传播行为。即当前的事务方法被另外一个事务调用时如何使用事务。
getPropagationBehavior(): 返回事务的传播行为,是否有一个活动的事务来决定一个事务调用。
②、isolation:指定事务的隔离级别 getIsolationLevel(): 返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据。
③、回滚规则
④、readonly:是否只读 isReadOnly(): 事务是否只读,事务管理器能够根据这个返回值进行优化,确保事务是只读的。
⑤、timeout:超时时间 getTimeout(): 它返回事务必须在多少秒内完成。
(一个读,一个改,再读发现数据不一致了)
(一个读,一个新增,再读发现多数据)
(两个同时更新,保存先后不一致,后保存的对先保存的进行覆盖)
允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。
其中用比较多的是PROPAGATION_REQUIRED和PROPAGATION_REQUIRED_NEW。
Spring中配置事务有两种方式:编程式事务和声明式事务。
编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。
基于 @Transactional 的声明式事务管理
基于 Aspectj AOP 配置事务
XML方式:利用AOP统一配置,要用到:
示例配置:
<tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes>
<tx:method name="*" propagation="REQUIRED" /> tx:attributes> tx:advice>
<aop:config> <aop:pointcut expression="execution(* com.firewolf.spring.tx.xml.*Service.*(..))" id="pc"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/> aop:config> |
注解方式:在配置文件中添加
工厂模式是spring创建对象的方式
如果记不住,建议把红色字体部分的1,2,7,9,10等四步记下来
实例化、设置属性、给Spring传入BeanID,接受Spring传递过来的IOC容器(2补)
后置处理器(2补,一前一后)、初始化以及销毁(2步)、Bean被使用
====================================================================
Spring创建bean对象的生命周期说明:
Spring创建bean的生命周期:
@Component,@Controller,@Repository,@Service,@Configuration(把类标识成一个配置类),@Bean
@AutoWired和 @Resource,区别在于,前者优先根据类型查找,后者优先根据Bean的ID查找。@Qualifier(用来限制使用哪一个Bean的);
@Value(“${}”)
@Transactional
@Aspect,@PointCut,@Order,@Before, @After……等
注意:其实这里说的Handler就是Controller
Struts2入口为Filter(StrutsPrepareAndExecuteFilter),这就导致他们的机制不同。
总体上Struts2漏洞相对较多,SpringMVC也越来越流行。
远程调用技术:
RPC:自定义数据格式,限定技术,传输速度快,效率高,TCP dubbo
HTTP:统一的数据格式,不限定技术,rest接口,TCP springcloud
Rest是一种基于http的请求风格,要求把资源通过合理的方式暴露出来,而不要涉及到操作,也就是说,对相同的资源使用相同的url。如user的所有操作都是user,而不是像之前那样对同一个资源的处理使用不同的URL,如addUser,deleteUser;Rest更强调的是名词(资源);
RPC(Remote Preducer Call)是远程-过程-调用,是一种不同服务器之间方法调用的方式。
相比来说,RPC效率更高,但是不能够穿透防火墙,代表产品为Dubbo;而Rest可以穿透防火墙,代表产品为SpringCloud。 ★★★★★★★★★★★
RestFul是SpringMVC对rest风格的一种实现。主要支持了Post(增)、Get(查)、Put(改)、Delete(删)等请求。
同时还提供了@DeleteMapping、@PostMapping,@GetMapping,@PutMapping等注解来细化@RequestMapping,对应上面的四种请求。
我们需要在web.xml中配置HiddenHttpMethodFilter来支持put和delete请求
默认情况下,html的form表单只支持get和post请求,如果需要发起put和delete请求,可以在表单里面添加一个name为_method的隐藏域(注意这个是固定的),值设置为”delete”或者”put”,然后把method设置为”post”;
如:
<form action="controller/testRest/1" method="post">
<input type="hidden" name="_method" value="DELETE"/>
<input type="submit" value="TestRest DELETE"/>
form>
在SpringMVC中,我们可以进行全局异常处理,主要有以下三种方式:
编写一个实现了HandlerExceptionResolver接口的异常处理器,然后把这个类交给Spring进行管理。
如:
package com.firewolf.test.Intercepters; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; public class MyExceptionHandler implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { System.out.println("这是异常处理器"); return new ModelAndView("ex"); } } |
创建一个BaseController,在这个类里面编写一些方法,在这个方法上面添加 @ExceptionHandler注解对异常进行处理,然后让所有的控制器继承自BaseController。
如:
package com.firewolf.test.controller; import org.springframework.web.bind.annotation.ExceptionHandler; public class BaseController { @ExceptionHandler(Exception.class) public String handleOtherException(Exception ex) { System.out.println("其他异常----"); return "err"; } @ExceptionHandler(value = { RuntimeException.class }) public String handleRuntimeException(RuntimeException ex) { System.out.println("运行时异常----"); return "err"; } } |
编写自己的异常处理器,使用@ControllerAdvice标注,然后交给Spring管理即可,如:
如:
@ControllerAdvice public class MyExceptionAdvicer { @ExceptionHandler(Exception.class) public String handleOtherException(Exception ex) { System.out.println("其他异常!!!"); return "err"; } @ExceptionHandler(value = { RuntimeException.class }) public String handleRuntimeException(RuntimeException ex) { System.out.println("运行时异常"); return "err"; } } |
推荐使用第三种方式。
Spring Web MVC的处理器拦截器(也就是我们说的拦截器)类似于Servlet开发中的过滤器Filter,用于对处理器(Controller)进行预处理和后处理。本质上也是AOP,但是一般情况下,AOP是针对单一的方法。Interceptor是针对Controller,可以处理request和response对象。
日志记录、权限检查、OpenSessionInView(Hibernate延迟session生命周期)
拦截器主要有一个接口HandlerInterceptor。
提供了三个方法:
preHandler:控制器方法执行前
postHandler:控制方法执行后,但是视图返回之前
afterCompletion:视图渲染完之后调用(释放资源)
Servlet是用来处理前端请求并响应数据的;
Filter和Interceptor是用来对请求进行过滤的,区别如下:★★★★★★★★★
①、拦截器基于java反射机制的,SpringMVC拦截器本质上也是AOP,过滤器基于方法回调;
②、拦截器 不依赖web容器,过滤器依赖web容器;
③、拦截器可以获取Spring容器里面的Bean,而过滤器不可以。
④、拦截器针对的是控制器,过滤器针对的是整个应用程序
⑤、顺序:Filter > Servlet > Interceptor
重定向:return “redirect:/user/list”;
转发:return “forward:/user/list”
答:是单例模式,所以在多线程访问的时候有线程安全问题,不要用同步,会影响性能的,解决方案是在控制器里面不能写字段。
解决方案:是在控制器里面不能写成员变量.也就是共享变量 ★★★★★★★★★★
在mybatis中有两个级别的缓存:SqlSession级别的一级缓存和SqlSessionFactory级别的二级缓存;
默认情况下,一级缓存是开启的,二级缓存需要我们在mybatis映射文件中加入
一级缓存清除时机:
在常见的MVC架构中,一级缓存会在service调用结束的时候清除(因为service结束,会提交事务,就会关闭sqlSession);★★★★★★★★★★★
动态SQL帮助我们在不同的条件下生产不同的SQL语句,非常灵活。
常见的动态SQL标签:
if,choose…when…otherwise,where,set,trim,foreach,bind
使用resultType和resultMap封装返回的结果;
resultType:只能给一个普通java类型,如:User。适用于简单的JAVA对象,并且数据库字段和java类里面的属性名一致的时候。
resultMap:给一个resultMap的ID,在映射文件需要定义一个resultMap,这个将数据库字段和java对象属性进行映射,用于有一对一/一对多关联关系的,或者类属性和数据库字段不一致的时候。如:
<select id="selectSomeThing" resultMap="BaseResultMap">select>
<resultMap id="BaseResultMap" type="com.stylefeng.guns.modular.system.model.Dept"> <id column="id" property="id"/> <result column="num" property="num"/> <result column="pid" property="pid"/> <result column="pids" property="pids"/> <result column="simplename" property="simplename"/> <result column="fullname" property="fullname"/> <result column="tips" property="tips"/> <result column="version" property="version"/> resultMap> |
我们使用
编写SQL有嵌套查询和嵌套结果两种方式。
在Insert语句里面配置useGeneratedKeys=”true”来表名要获取主键,使用keyProperty设置要把主键设置到哪个属性里面,然后就可以从对象里面获取主键了。
<insert id="addUser" parameterType="user" useGeneratedKeys="true" keyProperty="id">
insert into users(name,age) values(#{name},#{age})
insert>
使用@Param注解
MyBatis使用了责任链模式,运行用户在以下四个时机使用插件:
所以,分页插件是在对应ParameterHandler这个时机对参数进行了处理。
方法一:查询的时候给列名取别名
方法二:使用resultMap进行映射,column指定列名,property指定对象属性
都是ORM框架。多半情况下,Hibernate面向对象编程,而Mybatis面向SQL;
开发方面:
SQL优化方面:
对象管理:
缓存方面:
相同点:
Hibernate和Mybatis的二级缓存除了采用系统默认的缓存机制外,都可以通过实现你自己的缓存或为其他第三方缓 存方案,创建适配器来完全覆盖缓存行为。
不同点:
Hibernate的二级缓存配置在SessionFactory生成的配置文件中进行详细配置,然后再在具体的表-对象映射中配置是那种缓存。
MyBatis的二级缓存配置都是在每个具体的表-对象映射中进行详细配置,这样针对不同的表可以自定义不同的缓存机制。并且Mybatis可以在命名空间中共享相同的缓存配置和实例,通过Cache-ref来实现。
总之:
Hibernate 具有良好的管理机制,用户不需要关注SQL,如果二级缓存出现脏数据,系统会保存,;
Mybatis 在使用的时候要谨慎,避免缓存CAche 的使用
Hibernate优势
Hibernate的DAO层开发比MyBatis简单,Mybatis需要维护SQL和结果映射。
Hibernate对对象的维护和缓存要比MyBatis好,对增删改查的对象的维护要方便。
Hibernate数据库移植性很好,MyBatis的数据库移植性不好,不同的数据库需要写不同SQL。
Hibernate有更好的二级缓存机制,可以使用第三方缓存。MyBatis本身提供的缓存机制不佳。
Mybatis优势
MyBatis可以进行更为细致的SQL优化,可以减少查询字段。
MyBatis容易掌握,而Hibernate门槛较高。
一句话总结
Mybatis:小巧、方便、高效、简单、直接、半自动化
Hibernate:强大、方便、高效、复杂、间接、全自动化
三种:临时态、持久态、游离态
转换图:
Get是立即加载,而Load是懒加载;
Get查询如果指定id对应的对象不存在,返回Null;Load不存在抛出异常;
在不用到对象的属性的时候,不会发送SQL语句(也就是说不会去数据库查询)
SpringBoot是为了解决Spring大量的配置文件导致程序员开发速度低下而产生的一个框架。
①去除了大量的XML配置文件,甚至完全消除了配置文件,简化了配置
②简化了复杂的依赖关系
③配合各种starter,可以快速的集成第三方产品,如Redis,RabbitMQ,MyBatis等;★★★
④内置Tomcat等web容器,可以方便项目的打包和发布;
⑤可以进行热部署,在修改代码后可以快速启动。
①不太适合做大型项目,而适合做微服务;
SpringBoot的starter主要用来简化依赖用的,本身并没有java代码,只是对依赖进行了汇总
,可以快速的集成第三方产品
spring-boot-starter-web;
spring-boot-starter-redis
spring-boot-starter-security
spring-boot-starter-test
spring-boot-starter-tomcat
mybatis-spring-boot-starter
SpringBoot的核心就是自动装配,原理如下:
https://blog.csdn.net/huangjinjin520/article/details/100035211
(注册Listener,Servlet,Filter)
方法一:使用new ServletRegistrationBean(new MyServlet(),”/myservlet”)的方式;
方法二:在自己的Listener等上面标注@WebFilter,@WebServlet,@WebListener等注解。
addInterceptor用于添加你自定义的拦截器实例
addPathPatterns用于添加要拦截的url,可以写多个。美[ˈpætərnz]
excludePathPatterns用于添加不需要拦截的url,可以写多个。过滤css、js等静态资源
直接在需要加事务的方法或者类上面添加@Transactional注解
因为SpringBoot默认自动装配了事务管理器,@DataSourceTransactionManager,如果我们需要自己配置一个PlatformTransactionManager,如HibernateTransacionManager
①.引入druid的依赖
②.在配置文件中spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
说白了,就是热部署,所以我们只需要添加热部署依赖spring-boot-devtools即可。
这是在启动类上添加的!!
微服务架构是一种架构模式或者是一种架构风格,它提倡将单一应用程序根据业务功能划分成一组小的服务,每个服务运行在自己独立的进程中,服务之间互相协调、互相配合、为用户提供最终价值。
SpringCloud是基于SpringBoot提供了一套微服务解决方案,很多组件都来自Netflix公司
关键组建(五大神兽):
服务开发:Springboot、Spring、SpringMVC
服务注册与发现:Eureka、Consul、Zookeeper等
服务调用:REST、RPC、gRPC
负载均衡 Ribbon、Nginx等
服务接口调用(客户端调用服务发简单工具) Feign等
服务配置中心管理 SpringCloudConfig、Chef等
服务路由(API网关) Zuul等
服务熔断器 Hystrix、Envoy等
服务部署 Docker、OpenStack、Kubernetes等
消息队列 kafka、RabbitMQ、ActiveMQ等
服务监控 Zabbix、Nagios、Metrics、Spectator等
全链路追踪 Zipkin、Brave、Dapper等
数据流操作开发包 SpringCloud Stream(封装与Redis,Rabbit、Kafka等发送接收消息)
事件消息总线 SpringCloud Bus
服务配置与管理 Netflix公司的Archaius、阿里的Diamond等
对于Feign,默认就已经启动了负载均衡,对于Ribbon,我们需要在注入RestTemplate的时候,添加上@LoadBalanced注解,如:
@Configuration public class MyConfig { @Bean @LoadBalanced //添加了负载均衡,默认会轮询调用服务提供者 RestTemplate restTemplate() { return new RestTemplate(); } } |
在Spring容器中注入一个IRule(负责均衡算法接口)即可,如:
@Bean IRule loadBalanceRule(){ return new RandomRule(); } |
在微服务架构中,根据业务的拆分会导致系统中有很多的微服务,之间的调用会形成很多的依赖。然而由于网络及服务故障的原因,并不能导致所有的服务调用都能100%成功,如果单个服务出现问题,调用这个服务的线程就会阻塞,此时若有大量请求涌入,就会大量消耗资源导致系统瘫痪。各个微服务之间的依赖性会导致故障传递,最终导致整个系统崩溃,这就是服务故障的“雪崩”效应。
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性,用来做服务降级和服务熔断,避免“服务雪崩”(整个服务全部崩溃)。
怎么在过滤器中停止继续访问
SpringCloud和Dubbo都是优秀微服务架构。
这个问题其实就是问问你项目的架构,我们可以从以下两个个方法回答:
mvvm是一个model+view+viewModel框架,视图view,数据模型model,viewModel连接两个,视图上的数据改变可以同时改变model的数据,反过来,model的数据变化也会直接渲染到页面。
mvvm通过可以通过双向绑定快速便捷的改变页面的状态,已经数据的值。而jquery的操作相对就比较复杂,比如需要手动的写代码获取数据,也需要手动的写代码去渲染页面等,而在mvvm结构中,就会自动的帮助我们渲染和提取数据。
总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后。
创建前/后: 在beforeCreate阶段,vue实例的挂载元素$el和数据对象data都为undefined,还未初始化。在created阶段,vue实例的数据对象data有了,$el还没有。此阶段提供了两个钩子函数(beforeCreate/created)
载入前/后:在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。此阶段提供的钩子函数为beforeMount和mounted。
更新前/后:当data变化时,会触发beforeUpdate和updated方法。
销毁前/后:在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在。提供的钩子函数为beforeDestory和destoryed
v-if:判断是否隐藏;
v-for:数据循环出来;使用v-for-index指定索引名字(默认为index),v-for-item接受当前项(默认为item)。
v-bind:class:绑定一个属性;
v-model:实现双向绑定
大
Bootstrap具有移动设备优先、浏览器支持良好、容易上手、响应式设计等优点,所以Bootstrap被广泛应用。
超小设备手机(<768px):.col-xs-
小型设备平板电脑(>=768px):.col-sm-
中型设备台式电脑(>=992px):.col-md-
大型设备台式电脑(>=1200px):.col-lg-
给表格添加类:table-responsive
给图片添加类:img-responsive
img-rounded可以把图片变成圆角,img-circle 将图片变为圆形
用于在不同分辨率显示:col-xs-*等,
在某种情况下显示:visiable-xs等
在某种分辨率下隐藏:hidden-xs等
表格:class=”table”
按钮:class=”btn btn-success”
布局:class=”col-md-*” class=”col-md-offset-*”
响应式图片:class=”img-responsive”
响应式隐藏:class=”hidden-xs”:在手机上不显示
响应式显示:class=”visiable-xs”:在手机上显示
--soft:仅仅回退了本地仓库,相当于撤退到了git commit 之前
--mixed:回退了本地库和暂存区,相当于撤退到了git add 之前
--hard:工作区、暂存区、本地仓库都回退,相当于刚写完代码
常用的版本控制工具有git和svn,我们使用的是git
Gitflow 工作流通过为功能开发、发布准备和维护设立了独立的分支,让发布迭代过程更流畅。各个分支的开发互不干扰;
分支使用图解如下:其中master,develop这种为分支,v0.1,v0.2为版本
常见的分支有:
develop(发开分支)、hotfix(Bug修理分支)、release(预发布分支)、feature(新功能分支),一般我们都在develop分支开发。
一般我们都在develop分支开发
……
<<<<<<<
…很多代码….. [这部分为本地内容]
=========
…很多代码…. [这部分为远程内容]
>>>>>>>>master(分支名,可能是develop等)
实际开发中我们可以借助一些GUI工具(有界面的),eclispse的git对比插件进行对比合并,这样的话,就能够更直观的对比差异了。其他步骤类似。
问题:自己本地为master分支,怎么把别人在develop分支上的修改合并到自己本地的master分支?
解答:
没有,公司已经搭建好,给了仓库地址。
Maven有三套相互独立的生命周期,分别是clean、default和site。每个生命周期包含一些阶段(phase),阶段是有顺序的,后面的阶段依赖于前面的阶段。
1、clean生命周期:清理项目,包含三个phase。
1)pre-clean:执行清理前需要完成的工作
2)clean:清理上一次构建生成的文件
3)post-clean:执行清理后需要完成的工作
2、default生命周期:构建项目,重要的phase如下。
1)validate:验证工程是否正确,所有需要的资源是否可用。
2)compile:编译项目的源代码。
3)test:使用合适的单元测试框架来测试已编译的源代码。这些测试不需要已打包和布署。
4)Package:把已编译的代码打包成可发布的格式,比如jar。
5)integration-test:如有需要,将包处理和发布到一个能够进行集成测试的环境。
6)verify:运行所有检查,验证包是否有效且达到质量标准。
7)install:把包安装到maven本地仓库,可以被其他工程作为依赖来使用。
8)Deploy:在集成或者发布环境下执行,将最终版本的包拷贝到远程的repository,使得其他的开发者或者工程可以共享。
3、site生命周期:建立和发布项目站点,phase如下
1)pre-site:生成项目站点之前需要完成的工作
2)site:生成项目站点文档
3)post-site:生成项目站点之后需要完成的工作
4)site-deploy:将项目站点发布到服务器
使用gav坐标进行配置即可;
创建一个maven项目,打包方式选择pom,用来聚合项目;
在pom项目里面创建子module,如果是web项目,打包方式选择war,如果是普通java项目,选择jar。
使用maven package命令打包即可。也可以使用eclipse的maven install插件
一般冲突的出现有两种:
一、同一个类在多个jar包有;
二、不同的产品(如spring和shiro)的Jar包版本不匹配导致的:
保留主产品的版本,更改其他产品的版本直到没有冲突为止。
在配置依赖的时候,配上
<exclusions>
没有,公司给了仓库地址。
Redis是一个开源的,基于内存的高性能存储系统,可以用作数据库、缓存、消息代理等,提供了strings,hashs,lists,sets,sorted sets等丰富的数据结构。
String,list,hash,set,zset五种。(英文翻译是:远程字典服务)
单线程
高并发读写,可持久化
Redis支持五种数据类型:strings(字符串),hashs(哈希),lists(列表),sets(集合)及zsets(sorted sets:有序集合)。
应用场景:
String
String数据结构是简单的key-value类型,value其实不仅可以是String,也可以是数字。
用于常规key-value缓存应用;
常规计数:微博数,粉丝数等。
hash
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
存储部分变更的数据,多半用来保存hash结构,如用户信息、购物车等。
List
list就是链表,使用Lists结构,我们可以轻松地实现最新消息排序等功能。List的另一个应用就是消息队列,可以利用List的PUSH操作,将任务存在List中,然后工作线程再用POP操作将任务取出进行执行。Redis还提供了操作List中某一段的api,你可以直接查询,删除List中某一段的元素。多半用于需要保证存放顺序的系统。
1).消息队列系统
使用list可以构建队列系统,使用sorted set甚至可以构建有优先级的队列系统。
比如:将Redis用作日志收集器
实际上还是一个队列,多个端点将日志信息写入Redis,然后一个worker统一将所有日志写到磁盘。
2).取最新N个数据的操作,如微博最新信息
set
set就是一个集合,集合的概念就是一堆不重复值的组合。利用Redis提供的set数据结构,可以存储一些集合性的数据。set中的元素是没有顺序的,并且不能重复,所以多半用来去重。
如:在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功能,对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存集到一个新的集合中。
Sorted set
和set相比,sorted set增加了一个权重参数score,使得集合中的元素能够按score进行有序排列,比如一个存储全班同学成绩的sorted set,其集合value可以是同学的学号,而score就可以是其考试得分,这样在数据插入集合的时候,就已经进行了天然的排序。可以用sorted set来做带权重的队列,比如普通消息的sc。多半用来取top N
①.在service直接使用RedisTemplate ====》对jedis进行了封装
opsForSet
opsForList
opsForValue
opsForHash
这些方法操作
②.通过注解实现
缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透
正常使用
也就说使用流程为:
1、先从缓存中取数据,如果能取到,则直接返回数据给用户。这样不用访问数据库,减轻数据库的压力。
2、如果缓存中没有数据,就会访问数据库。
缓存穿透
缓存就像是数据库的一道防火墙,将请求比较频繁的数据放到缓存中,从而减轻数据库的压力。 但是如果有人恶意攻击,那就很轻松的穿透你的缓存,将所有的压力都给数据库。比如上图,你缓存的key都是正整数,但是我偏偏使用负数作为key访问你的缓存,这样就会导致穿透缓存,将压力直接给数据库。
缓存穿透在于一步步规避穿透的原因,如图:
1、在web服务器启动时,提前将有可能被频繁并发访问的数据写入缓存。—这样就规避大量的请求在第3步出现排队阻塞。
2、规范key的命名,并且统一缓存查询和写入的入口。这样,在入口处,对key的规范进行检测。–这样保存恶意的key被拦截。
3、Synchronized双重检测机制,这时我们就需要使用同步(Synchronized)机制,在同步代码块前查询一下缓存是否存在对应的key,然后同步代码块里面再次查询缓存里是否有要查询的key。 这样“双重检测”的目的,还是避免并发场景下导致的没有意义的数据库的访问(也是一种严格避免穿透的方案)。
这一步会导致排队,但是第一步中我们说过,为了避免大量的排队,可以提前将可以预知的大量请求提前写入缓存。
4、不管数据库中是否有数据,都在缓存中保存对应的key,值为空就行。–这样是为了避免数据库中没有这个数据,导致的平凡穿透缓存对数据库进行访问。
5、第4步中的空值如果太多,也会导致内存耗尽。导致不必要的内存消耗。这样就要定期的清理空值的key。避免内存被恶意占满。导致正常的功能不能缓存数据
Subject:就是咱们的用户,
SecurityManager:安全管理器,整个架构的核心,权限认证的入口
Realm:是Shiro安全框架和我们自己的数据(数据库的数据、配置文件的数据)之间的一个桥梁。通过Realm把自己的数据加载进来,传递给Shiro。
Subject(org.apache.shiro.subject.Subject)
当前与软件交互的实体(用户,第三方服务,cron作业等)的特定于安全性的“视图”。
SecurityManager(org.apache.shiro.mgt.SecurityManager)
SecurityManager是Shiro建筑的核心。它主要是一个“伞形”对象,协调其托管组件,以确保它们一起平稳运行。它还管理Shiro对每个应用程序用户的视图,因此它知道如何对每个用户执行安全操作。
Authenticator(认证器)(org.apache.shiro.authc.Authenticator)
Authenticator是负责执行和反应以验证(注册)用户企图的组件。当用户尝试登录时,该逻辑由执行Authenticator。该Authenticator知道如何与一个或多个协调Realms存储有关用户/帐户信息。从这些数据中获取的数据Realms用于验证用户的身份,以保证用户确实是他们所说的人。
AuthenticationStrategy(认证策略)(org.apache.shiro.authc.pam.AuthenticationStrategy)
如果Realm配置了多个,AuthenticationStrategy将协调领域以确定身份验证尝试成功或失败的条件(例如,如果一个领域成功但其他领域失败尝试是否成功?必须所有领域成功吗?只有第一个?)。
Authorizer(授权器)(org.apache.shiro.authz.Authorizer)
Authorizer是部件负责确定用户在该应用程序的访问控制。这种机制最终会说明是否允许用户做某事。与此类似Authenticator,它Authorizer也知道如何协调多个后端数据源以访问角色和权限信息。在Authorizer使用该信息来确定到底是否允许用户执行特定的操作。
SessionManager(org.apache.shiro.session.mgt.SessionManager)
SessionManager知道如何创建和管理用户Session生命周期,提供在所有环境中的用户强大的会话体验。这是安全框架领域的一项独特功能 - 即使没有可用的Web / Servlet或EJB容器,Shiro也能够在任何环境中本地管理用户会话。默认情况下,Shiro将使用现有的会话机制(例如Servlet Container),但如果没有,例如在独立应用程序或非Web环境中,它将使用其内置的企业会话管理提供相同的编程经验。的SessionDAO存在允许任何数据源被用来坚持的会议。
SessionDAO(org.apache.shiro.session.mgt.eis.SessionDAO)
的SessionDAO执行Session代表的持久性(CRUD)操作Session。这允许将任何数据存储插入会话管理基础结构。
CacheManager(org.apache.shiro.cache.CacheManager)
CacheManager创建和管理Cache其他四郎组件使用实例的生命周期。由于Shiro可以访问许多后端数据源以进行身份验证,授权和会话管理,因此缓存一直是框架中的一流架构功能,可在使用这些数据源时提高性能。任何现代开源和/或企业缓存产品都可以插入Shiro,以提供快速有效的用户体验。
cryptography(org.apache.shiro.crypto。*)
密码是企业安全框架的自然补充。Shiro的crypto软件包包含易于使用和理解的密码密码。
Realm(org.apache.shiro.realm.Realm)
Realms充当Shiro和应用程序安全数据之间的“桥接”或“连接器”。当实际与安全相关的数据(如用户帐户)进行交互以执行身份验证(登录)和授权(访问控制)时,Shiro会从为应用程序配置的一个或多个Realm中查找许多这些内容。您可以根据Realms需要配置任意数量(通常每个数据源一个),Shiro将根据需要进行身份验证和授权协调
在shiro中,我们通过给realm配置HashCredenticalsMatcher来实现加密,可以给Matcher指定加密算法和迭代次数等信息。一般我们可以采用md5进行加密。
在添加用户的时候,我们可以通过SimpleHash来把密码加密之后存放到数据库,如果是Md5算法的话,有一个Md5Hash可以直接使用,
在加密的时候,还可以进行盐值加密。
通过给SecurityManager配置一个rememberManager来实现,同时,需要给rememberManager配置一个cookie即可,接下来,登录的时候,设置token.setRememberMe(true)即可。
缓存一般用来存放权限数据,我们实现Cache接口来创建自己的缓存,同时创建自己的CacheManager即可实现缓存,推荐使用redis完成。
Dubbo默认使用的通讯框架是netty,也可以使用别的通讯框架,如mina
Netty 是一个基于 JAVA NIO 类库的异步通信框架,它的架构特点是:异步非阻塞、基于事件驱动、高性能、高可靠性和高可定制性。
默认是阻塞的,可以异步调用,没有返回值的可以这么做
Dubbo使用ZooKeeper作为注册中心,我们也可以使用Redis,不推荐
默认使用Hessian序列化,还有Duddo、FastJson、Java自带序列化 美[ˈheʃn]
服务失效踢出基于zookeeper的临时节点原理
采用多版本开发,可以通过指定版本号来指定引用的版本。
可以结合zipkin实现分布式服务追踪
dubbo:service
dubbo:reference
dubbo:protocol
dubbo:registry
dubbo:application
dubbo:provider
dubbo:consumer
dubbo:method
默认使用dubbo协议。还有 rmi,http,hessian,webservice
dubbo : 单一长连接和 NIO 异步通讯,适合大并发小数据量的服务调用,
以及消费者远大于提供者。Consumer >>provider
http : 基于 Http 表单提交的远程调用协议,使用 Spring 的 HttpInvoke 实
现。多个短连接,传输协议 HTTP,传入参数大小混合,提供者个数多于消
费者,需要给应用程序和浏览器 JS 调用;
dubbox是当当网基于dubbo上做了一些扩展,如加了服务可restful调用,更新了开源组件等
SpringCloud
各个角色说明:
Provider:暴露服务的服务提供方
Consumer:调用远程服务的服务消费方
Register:服务注册与发现的注册中心,一般使用zookeeper
Monitor:统计服务的调用次数和调用时间的监控中心。
Container:服务运行容器
Zookeeper是一个分布式服务框架的协调服务
Leader 主机
Follower 从机
Observer 监控者
临时节点
永久节点
临时有序节点
永久有序节点
分布式锁
协调服务(Dubbo)
分布式事务
Oracle,Mysql,DB2,Postgre
where语句特点:
select gcount,count(1) aa from goods group by gcount where aa>1,这个结果就会报错
having特点:
建议优先使用where进行过滤,减少聚合函数的运行
①.from子句组装来自不同数据源的数据;
②.where子句基于指定的条件对记录行进行筛选;
③.group by子句将数据划分为多个分组;
④.使用聚集函数进行计算;
⑤.使用having子句筛选分组;
⑥.计算所有的表达式;
⑦.select 的字段;
⑧.使用order by对结果集进行排序。
In 查询优化耗费的 CPU 比 union all 多,但可以忽略不计,一般情况下建议使用 in
EXISTS(子查询)==》返回的是一个boolean值,子查询有结果返回true;否则返回false。
EXISTS(select null) ==》 返回的也是 true。
in()后面的子查询 是返回结果集的。
例如:
SELECT * FROM t_user where user_name='张三'
UNION ALL(或者是UNION )
SELECT * FROM t_user WHERE user_no='L0004'
用于根据不同条件查询同一张或拥有相同字段的表中的数据,合并一张表
注意:查询字段个数一致、字段类型要一致。
使用UNION关键字,是将两个结果集合并到一个结果集中,并且它会去掉重复的部分
如果不想把重复的部分去掉的话,我们还可以使用关键字UNION ALL .
应尽量避免在 where 子句中使用 != 或 < > 操作符,否则将引擎放弃使用索引而进行全表扫描
//====================================================================================
①
应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select id from t where num=0
②
应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or num=20
可以这样查询:
select id from t where num=10
union all
select id from t where num=20
③
like 语句里面尽量不要加前%:将导致全表扫描(索引无效)
select id from t where name like '%abc%'
④
in 和 not in 也要慎用,否则会导致全表扫描
用EXISTS替代IN、用NOT EXISTS替代NOT IN
EXISTS(子查询)==》返回的是一个boolean值,子查询有结果返回true;否则返回false。
EXISTS(select null) ==》 返回的也是 true。
in()后面的子查询 是返回结果集的。
索引网址:
https://www.w3school.com.cn/sql/sql_create_index.asp
https://blog.csdn.net/bingguang1993/article/details/81186797?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162261556216780262529680%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=162261556216780262529680&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-81186797.first_rank_v2_pc_rank_v29&utm_term=sql%E7%B4%A2%E5%BC%95&spm=1018.2226.3001.4187
创建、删除索引:
ALTER TABLE student DROP INDEX I_name;
ALTER TABLE student ADD INDEX I_name_age(name, age);
聚集索引存储记录是物理上连续存在,而非聚集索引是逻辑上的连续,物理存储并不连续。就像字段,聚集索引是连续的,a后面肯定是b,非聚集索引就不连续了,就像图书馆的某个作者的书,有可能在第1个货架上和第10个货架上。还有一个小知识点就是:聚集索引一个表只能有一个,而非聚集索引一个表可以存在多个。★★★★★★★★★★★★★
1.普通索引
2.唯一索引 可以是null
3.主键索引 不能是null
4.组合索引
5.全文索引
参看常见配置
https://blog.csdn.net/weixin_39993322/article/details/113609096?ops_request_misc=&request_id=&biz_id=102&utm_term=mysql%E4%B8%AD%20Key_read_requests%20%E5%8F%82%E6%95%B0%E7%9A%84%E6%84%8F%E4%B9%89&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-3-.first_rank_v2_pc_rank_v29&spm=1018.2226.3001.4187
默认151,最大2000
默认80,最多512,如果MySQL的连接数据达到max_connections时,新来的请求将会被存在堆栈中,以等待某一连接释放资源,该堆栈的数量即back_log。如果等待连接的数量超过back_log,将不被授予连接资源。当主要MySQL线程在一个很短时间内得到非常多的连接请求,这就起作用。
它决定索引处理的速度,尤其是索引读的速度。
通过检查状态值Key_read_requests和Key_reads,可以知道key_buffer_size设置是否合理
索引命中率:key_cache_miss_rate = Key_reads / Key_read_requests * 100%
…..
具体参看:http://www.cnblogs.com/luyucheng/p/6340076.html
Char(4):表明可以存放4个字符,不论中文还是英文
varchar
MySQL有很多存储引擎。主要有:MyISAM,InnoDB,NDB,Memeory
其中myisam和innodb的区别在于:
mysql-5.1版本之前默认引擎是MyISAM,之后是innoDB
执行写操作的时候需要锁定这个表,所以会导致效率会降低。不过和 InnoDB 不同的是,MyIASM 引擎是保存了表的行数,于是当进行 select count(*) from table 语句时,可以直接的读取已经保存的值而不需要进行扫描全表。所以,如果表的读操作远远多于写操作时,并且不需要事务的支持的,可以将 MyIASM 作为数据库引擎的首选。
它的设计的目标就是处理大数据容量的数据库系统。MySQL 运行的时候,InnoDB 会在内存中建立缓冲池,用于缓冲数据和索引。但是该引擎是不支持全文搜索,同时启动也比较的慢,它是不会保存表的行数的,所以当进行 select count(*) from table 指令的时候,需要进行扫描全表。由于锁的粒度小,写操作是不会锁定全表的,所以在并发度较高的场景下使用会提升效率的。
SQL默认有个规定:只要10秒钟没有按照规定的时间返回结果,都属于慢查询,存放到日志中。
可以去修改MySQL慢查询默认时间
--查询慢查询时间
show variables like 'long_query_time'; =====》结果是10s
--修改慢查询时间
set long_query_time=1; ---但是重启mysql之后,long_query_time依然是my.ini中的值
开启慢查询日志,可以让MySQL记录下查询超过指定时间的语句,通过定位分析性能的瓶颈,才能更好的优化数据库系统的性能。
slow_query_log 慢查询开启状态:
例如:show variables like 'slow_query_log';
slow_query_log_file 慢查询日志存放的位置(这个目录需要MySQL的运行帐号的可写权限,一般设置为MySQL的数据存放目录)
long_query_time 查询超过多少秒才记录:
例如:show variables like 'long_query_time';
方法一:命令
set global slow_query_log='ON'
set global slow_query_log_file='/usr/local/mysql/data/slow.log'
set global long_query_time=1
方法二:配置文件
修改配置文件my.cnf,在[mysqld]下的下方加入
[mysqld]
slow_query_log = ON
slow_query_log_file = /usr/local/mysql/data/slow.log
long_query_time = 1
索引是一种使记录有序化的技术,它可以指定按某列/某几列预先排序,从而大大提高查询速度(类似于汉语词典中按照拼音或者笔画查找)。
索引的主要作用是加快数据查找速度,提高数据库的性能。
MySQL目前主要有以下几种索引类型:
1.普通索引
2.唯一索引
3.主键索引
4.组合索引 最左前缀
5.全文索引
CREATE TABLE table_name[col_name data type]
[unique|fulltext][index|key][index_name](col_name[length])[asc|desc]
1.unique|fulltext为可选参数,分别表示唯一索引、全文索引
2.index和key为同义词,两者作用相同,用来指定创建索引
3.col_name为需要创建索引的字段列,该列必须从数据表中定义的多个列中选择
4.index_name指定索引的名称,为可选参数,如果不指定,默认col_name为索引值
5.length为可选参数,表示索引的长度,只有字符串类型的字段才能指定索引长度
6.asc或desc指定升序或降序的索引值存储
是最基本的索引,它没有任何限制。
CREATE INDEX index_name ON table(column(length))
ALTER TABLE table_name ADD INDEX index_name ON (column(length))
DROP INDEX index_name ON table
与前面的普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。创建方式在上面index前面添加unique关键字即可
是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。一般是在建表的时候同时会自动会主键创建主键索引 ★★★★★★★★
指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引时遵循最左前缀集合。
ALTER TABLE `table` ADD INDEX name_city_age (name,city,age);
不要求掌握细节,知道即可
主要用来查找文本中的关键字,而不是直接与索引中的值相比较。fulltext索引跟其它索引大不相同,它更像是一个搜索引擎,而不是简单的where语句的参数匹配。fulltext索引配合match against操作使用,而不是一般的where语句加like。它可以在create table,alter table ,create index使用,不过目前只有char、varchar,text 列上可以创建全文索引。值得一提的是,在数据量较大时候,先将数据放入一个没有全局索引的表中,然后再用CREATE index创建fulltext索引,要比先为一张表建立fulltext然后再将数据写入的速度快很多
ALTER TABLE article ADD FULLTEXT index_content(content)
CREATE FULLTEXT INDEX index_content ON article(content)
https://www.cnblogs.com/CarpenterLee/p/5503882.html
行锁:每次锁一行。开销大,加锁慢;会出现死锁,锁定粒度最小,发生锁的冲突率最小,并发度最高。高并发下用行级锁。
表锁:每次对表加锁。开销小,加锁快,不会死锁,粒度较大,锁冲突概率大,并发度较低。
悲观锁:悲观的认为,每次去查询数据的时候都会被别人修改,所以每次都要加锁
select xxx from tb for update;
乐观锁:认为别人不会修改,所以不加锁。
实现:使用数据版本机制,在表里添加一个version字段。★★★★★★★★★★★
update task set value = newValue,version = versionValue + 1 where version = versionValue;
使用预处理语句PrepareStatement,也就是使用?占位符而不是使用字符串拼接
主分区、拓展分区、逻辑分区:早期主引导扇区MBR用64B存放主分区信息,每个分区用16B,因而上限为4个主分区,后来,因分区需求,引入拓展分区(类主分区,所以主分区+扩展分区最多4个),对拓展分区进行分区,即为逻辑分区,不受MBR限制,上限为12个逻辑分区。
/:根目录。
/boot:系统启动目录,保存系统启动相关的文件,如内核文件和启动引导程序 (grub)文件等
/dev:设备文件保存位置。我们已经说过Linux中所有内容以文件形式保存,包括硬 件。那么这个目录就是用来保存所有硬件设备文件的
/etc:配置文件保存位置。系统内所有采用默认安装方式(rpm安装)的服务的配置文件全部都保存在这个目录当中,如用户账户和密码,服务的启动脚本, 常用服务的配置文件等
/root:超级用户的家目录。普通用户家目录在“/home”下,超级用于家目录直 接在“/”下
/home:普通用户的家目录。建立每个用户时,每个用户要有一个默认登录位置,这个 位置就是这个用户的家目录,所有普通用户的家目录就是在/home下建立一个和 用户名相同的目录。如用户user1的家目录就是/home/user1
/tmp:临时目录。系统存放临时文件的目录,该目录下所有用户都可以访问和 写入。我们建议此目录中不能保存重要数据,最好每次开机都把该目录 清空
/usr:系统软件资源目录。注意usr不是user的缩写,而是“Unix Softwre Resource”的缩写,所以不是存放用户数据,而是存放系统软件资源的目 录。系统中安装的软件大多数保存在这里,所以除了/usr/bin/和/usr/sbin/ 这两个目录。
/var:动态数据保存位置。主要保存缓存、日志以及软件运行所产生的文件
/opt:第三方安装的软件保存位置。这个目录就是放置和安装其他软件的位置,我手 工安装的源码包软件都可以安装到这个目录当中。不过我还是更加习惯把软件 放置到/usr/local/目录当中,也就是说/usr/local/目录也可以用来安装软件
/bin:存放系统命令的目录,普通用户和超级用户都可以执行。不过放在/bin下的 命令在单用户模式下也可以执行
/sbin:保存 和系统环境设置相关的命令,只有超级用户可以使用这些命令进行系统 环境设置,但是有些命令可以允许普通用户查看
方式一:修改/etc/profile文件(全局环境变量)
方式二:修改~/.bashrc文件(用户环境变量)
shutdown/power off:关机
uname –r :显示内核版本
exit:退出
echo:输出到控制台
date:系统日期
cal:日历
bc:计算器
logout:注销
help/man:帮助命令
pwd:显示当前工作目录
cd [目录]:切换目录
cd .. :切换到上级目录
cd:切换到用户宿主目录,等价于cd ~
cd . :当前目录
ls [目录]:显示文件或者目录
-l:显示文件详情
-a:显示所有文件,包括隐藏文件
-i:显示i节点信息
mkdir [目录1,目录2….]:创建文件夹
-p:递归创建文件夹
mv [源文件(夹)] [目标文件(夹)]:移动或者重命名文件(夹)
-r:移动文件夹包括子文件夹
cp 源文件 目标文件:复制文件(夹)
rm –rf 文件(夹):删除文件(夹)
rm –rf * :删除当前目录下所有文件
touch filename:创建空白文件
find:查找文件
find目录 -name 文件名 : 根据文件名查找
find 目录 –size 大小:根据文件大小查找
链接命令ln:
软连接:ln -s 源文件/目录 连接名 : 相当于快捷方式
硬链接:ln 源文件 链接名 复制+同步
区别:
软连接比源文件小很多,硬链接和文件大小一样;
删除源文件,软连接就失效,硬链接可以继续使用
不能给目录创建硬链接。
cat : 展示文件所有内,不适合内容过多的文本
more: 分屏查看
h(帮助) space(下翻屏) b(上翻屏) enter(下一行) q键(退出)
less 分屏查看(支持搜索,直接输入/content就可以,按n可以到下一个匹配内容)
e(下一行) y(上一行) f / space(下翻页) b(上翻页) q(退出)
head: 显示前n行,默认显示10行, head -20 文件
tail: 显示最后n行,默认显示10行, tail -20 文件 (查看日志)
vim
1.从命令模式切换到编辑模式: i(在光标前插入),I(当前行首插入), a(光标后插入),A(行末插入),o(下一行插入),O(上一行插入)
2.从命令模式切换到末行模式:按:
3.从末行或者是编辑模式切换到命令模式,esc
4.在末行模式下:
:q(已保存或未更改)
:wq(保存后退出)
:q!(不保存修改并退出)
6.命令模式常用命令:
^ :到行首
$ :到行尾
:set nu 显示行号
:set nonu 隐藏行号
6G 或者 :6 : 光标定位到第六行
dd : 删除当前行
3dd:从当前行开始删除3行
:3,5d : 删除第3到第5行
u:撤销操作
yy:复制当前行
3yy:从当前行开始复制3行
p:粘贴到下一行
P:粘贴到上一行
/内容: 搜索,按n切换到下一个匹配的地方
:%s/old/new/g:全文把old替换成new
:13,16/old/new/g:把13到16行之间的所有old替换成new
mount/unmount:挂载/卸载
df –h:显示已经挂载的分区列表及使用情况
du –sh 目录:查看目录使用
fdisk
fdisk –l:显示分区表
ifconfig:查看网卡信息
ifconfig 网卡名称 ip:设置网卡ip地址,临时生效
ping
-c:指定测试次数
telnet ip 端口:测试能否连通某个ip的端口
ssh:远程登录
scp:远程拷贝
netstat:显示网络相关信息
-t:TCP协议
-u:UDP协议
-l:监听
-r:路由
-n:显示IP地址和端口号
-p:显示对应的应用程序
-a:显示所有socket
常见组合:
netstat –antp:显示所有tcp协议的程序
netstat –antp | grep 8080:查看8080端口是否在监听(如判断tomcat是否启动),
netstat -tlun:显示本机监听的端口
netstat –rn:显示本机路由表
setup:图形界面配置网络信息
write <用户名>:给某个用户发送信息
wall:给所有用户发送信息
last:列出登录系统的用户
top:显示进行信息
-d:指定刷新时间
ps –ef | aux:查看运行的进程
pstree:进程树
kill 进程ID:杀死进程
pkill 进程名:杀死进程
crontab:任务调度命令
在Linux中,软件包分为两类,源码包和二进制包,其中rpm等为二进制包,tar可能是源码包(如redis安装包),也可能是二进制包(如tomcat)
tar:压缩或者解压
-c:打包
-v:显示详细信息
-f:指定文件名
-z:打包同时压缩
-x:解压缩
常见组合
tar –zxvf xxx.tar.gz:解压缩xxx.tar.gz
tar –xvf xxx.tar:解压缩xxx.tar
在解压的时候,可以通过-C选项来指定要压缩到的目录
tar –cvf xxx.tar *:把文件压缩成为xxx.tar
tar –zcvf xxx.tar.gz *:把文件压缩成为xxx.tar.gz
zip压缩 解压
zip nginx.conf.zip nginx.conf:压缩单个文件
zip -r abc123.zip abc 123.txt:压缩多个文件
unzip nginx.conf.zip:解压到当前目录
unzip nginx.conf.zip -d /tt:解压到指定目录
gzip压缩解压
gzip a.txt:把文件a.txt压缩称为a.txt.gz,丢失原文件
gzip –r test:递归压缩test下面的文件或者文件夹
gzip –d a.txt.gz:解压缩文件
gzip –dr test.gz:递归解压
gzip -c nginx.conf > nginx.conf.gz:压缩保留源文件
gzip -d -c nginx.conf.gz > nginx.conf:解压保留源文件
rpm包
rpm –ivh包全名 :安装rpm包
rpm –Uvh 包全名:升级rpm包
rpm –e 包全名:卸载软件包
--nodeps:不检查依赖,直接卸载
rpm –q 包名:查询软件包
-i:显示详细信息
-a:显示所有信息
-l:列表
yum
yum是对rpm包的一种管理方式,而不是一种软件包
yum list:列出yum管理的rpm包
yum install 软件包名:安装
yum update 软件包名:升级
yum remove 软件报名:卸载(慎用)
用户、用户组
命令
useradd <用户名>:添加用户
userdel <用户名>:删除用户
password [用户名]:修改密码,只有root可以,普通用户不能指定用户名,用于修改自己的密码
groupadd <组名>:添加组
相关文件
/etc/password:用户文件
/etc/shadow:密码文件
/etc/group:组文件
/etc/gshadow:组密码文件
权限
chmod [augo] [+-][rwx] 文件或者目录 改变权限
r:可读 4
w:可写 2
x:可执行 1
使用数字改变权限:chmod 777 /aaa
高压解决方案
为了缓解服务器的压力,我们可以从多个层次进行优化;
在网关层面,选择性的丢弃掉一些请求,比如,要求同一个用户id的没5秒才能勾过一个请求到后台服务,这样的话,到后台的请求数量大大下降。
“超卖”解决方案
超卖产生的原因是多线程导致的;
减少库存问题
在订单生产的时候,就减少库存,而不是支付完成之后;在订单完之后,库存就减少了,如果启动一个定时任务,如果到了指定的时间间隔如半小时,就取消订单,同时把库存量加回来
参考资料:https://www.cnblogs.com/ywlaker/p/6113927.html
当数据库的数据较多的时候,比如:租房系统的房源信息首页、商城项目的首页、教育项目的课程页面、OA项目的论坛、博客系统的列表页等待,在显示页面的时候,都会加载速度极慢;
解决:
一般权限框架主要有spring的spring security和apache的shiro,能说哪个说哪个,不过如果都熟悉,最好说spring security,特别是在springcloud中,默认情况下cloud就集成了security。如果都不能说,就时候公司自己使用拦截器或者过滤器来实现的。
经典的五张表:用户、角色、权限、用户-角色、角色-权限
七表:用户、角色、功能、权限以及各自之间表
控制到了,专门有一张表存放按钮信息(按钮所属功能、url)等,然后再来一张角色-按钮表
权限效率低下的时候,可以把角色、权限等放到redis中
登录
这个问题不要单独的只说到输入账号密码登录就可以了;
退出
退出也不要见到的说跳转到页面,还应该注销用户,也就是说把用户从session中清除。
一般都是自己设置过期时间的,在后台通过Cookie.setMaxAge方法,如果没有设置,浏览器关闭就没有了
使用junit + maven(maven针对junit有相应插件maven-surefire-plugin)
**/Test*.java
**/*Test.java
**/*TestCase.java
以下是一些做法,不用说
<plugin> <groupId>org.apache.maven.pluginsgroupId> <artifactId>maven-surefire-pluginartifactId> <version>2.19.1version> <configuration> <includes> <include>**/*Test*.javainclude> includes> <excludes> <exclude>……exclude> excludes> configuration> plugin> |
要得到测试覆盖率,需要额外的插件(cobertura-maven-plugin)支持
具体需要引入下面插件依赖。
|
使用testng+maven进行测试(不说,就说了解过)
参考地址
https://www.jianshu.com/p/2594dcbd3272
写测试用例的时候,尽量使用断言assertTrue