首页 > 编程语言 > 详细

Java核心整理

时间:2020-06-26 11:11:37      阅读:74      评论:0      收藏:0      [点我收藏+]

java学习方向

技术分享图片

1、JVM

(1) 基本概念:

	JVM是可运行Java代码的假想计算机,
        包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收,堆 和 一个存储方法域。
        JVM 是运行在操作系统之上的,它与硬件没有直接的交互。

技术分享图片

(2) 运行过程:

	我们都知道Java 源文件,通过编译器,能够生产相应的.Class 文件,也就是字节码文件,
              而字节码文件又通过Java 虚拟机中的解释器,编译成特定机器上的机器码 。
	也就是如下:
		① Java 源文件—->编译器—->字节码文件
		② 字节码文件—->JVM—->机器码
	每一种平台的解释器是不同的,但是实现的虚拟机是相同的,这也就是Java 为什么能够跨平台的原因了 ,
            当一个程序从开始运行,这时虚拟机就开始实例化了,多个程序启动就会存在多个虚拟机实例。
            程序退出或者关闭,则虚拟机实例消亡,多个虚拟机实例之间数据不能共享。

线程

	这里所说的线程指程序执行过程中的一个线程实体。JVM 允许一个应用并发执行多个线程。
    Hotspot JVM 中的Java 线程与原生操作系统线程有直接的映射关系。
    当线程本地存储、缓冲区分配、同步对象、栈、程序计数器等准备好以后,就会创建一个操作系统原生线程。
    Java 线程结束,原生线程随之被回收。操作系统负责调度所有线程,并把它们分配到任何可用的 CPU 上。
    当原生线程初始化完毕,就会调用 Java 线程的 run() 方法。
    当线程结束时,会释放原生线程和 Java 线程的所有资源。

Hotspot JVM 后台运行的系统线程主要有下面几个:

虚拟机线程(VM thread) 这个线程等待 JVM 到达安全点操作出现。这些操作必须要在独立的线程里执行,因为当堆修改无法进行时,线程都需要 JVM 位于安全点。这些操作的类型有:stop-the-world垃圾回收、线程栈 dump、线程暂停、线程偏向锁(biased locking)解除。
周期性任务线程 这线程负责定时器事件(也就是中断),用来调度周期性操作的执行。
GC 线程 这些线程支持 JVM 中不同的垃圾回收活动。
编译器线程 这些线程在运行时将字节码动态编译成本地平台相关的机器码。
信号分发线程 这个线程接收发送到 JVM 的信号并调用适当的 JVM 方法处理。

JVM 内存区域

技术分享图片

垃圾回收与算法

技术分享图片

2、JAVA IO/NIO

netty框架

byte[] 或者char[] stream

buffer Channel

技术分享图片

IO流

*  流的体系结构:
* 抽象基类          字节流(或文件流)
* InputStream      FileInputStream { read(byte[]  buffer) }            
* OutputStream     FileOutputStream { write(byte[] buffer,0,len) }     
* Reader           FileReader { read(char[]  buffer) }                 
* Writer           FileWriter { write(char[] buffer,0,len) }       
缓冲流(处理流的一种)
* BufferedInputStream { read(byte[] buffer}
* BufferedOutputStream {write(byte[] buffer,0,len)  /flush()}
* BufferedReader { read(char[]  buffer) /readLine() }
* BufferedWriter { write(char[] buffer,0,len) /flush() }

技术分享图片

char byte

缓冲流作用:提供流的读取、写入的速度

1.转换流:属于字符流,作用:提供了在字节流和字符流之间的转换。

? InputStreamReader:将一个字节的输入流转换为字符的输入流。

? OutputStreamWriter:将一个字符的输出流转换为字节的输出流。

2.解码:字节、字节数组 --->字符数组、字符串

编码:字符数组、字符串 --->字节、字节数组

3.字符集

序列化:
*  Person需要满足如下的要求,方可序列化
*      1.需要实现接口:Serializable
*      2.当前类提供一个全局常量:serialVersionUID
*      3.除了当前Person类需要实现Serializable接口之外,
*          还必须保证其内部所有属性也必须是可序列化的。(默认情况下,基本数据类型可序列化)
*  ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量。

作用:
	ObjectOutputStream:内存中的对象 --->存储中的文件、通过网络传输出去:序列化过程
	ObjectInputStream:存储中的文件、通过网络接收过来--->内存中的对象:反序列化过程

  ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

阻塞IO模型

技术分享图片

	最传统的一种IO 模型,即在读写数据过程中会发生阻塞现象。
    当用户线程发出IO 请求之后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户线程就会处于阻塞状态,用户线程交出CPU。
    当数据就绪之后,内核会将数据拷贝到用户线程,并返回结果给用户线程,用户线程才解除block 状态。
    典型的阻塞IO 模型的例子为:data = socket.read();如果数据没有就绪,就会一直阻塞在read 方法。

非阻塞IO模型

	当用户线程发起一个read 操作后,并不需要等待,而是马上就得到了一个结果。
    如果结果是一个error 时,它就知道数据还没有准备好,于是它可以再次发送read 操作。
    一旦内核中的数据准备好了,并且又再次收到了用户线程的请求,那么它马上就将数据拷贝到了用户线程,
    然后返回。所以事实上,在非阻塞IO 模型中,用户线程需要不断地询问内核数据是否就绪,
    也就说非阻塞IO不会交出CPU,而会一直占用CPU。
典型的非阻塞IO 模型一般如下:
 while(true){
	data = socket.read();
	if(data!= error){
		处理数据
		break;
	}
 }       
 	但是对于非阻塞IO 就有一个非常严重的问题,在while 循环中需要不断地去询问内核数据是否就绪,
      这样会导致CPU占用率非常高,因此一般情况下很少使用while 循环这种方式来读取数据。       

多路复用IO模型

技术分享图片

	多路复用IO 模型是目前使用得比较多的模型。Java NIO 实际上就是多路复用IO。
    在多路复用IO模型中,会有一个线程不断去轮询多个socket 的状态,只有当socket 真正有读写事件时,
才真正调用实际的IO 读写操作。因为在多路复用IO 模型中,只需要使用一个线程就可以管理多个socket,
系统不需要建立新的进程或者线程,也不必维护这些线程和进程,并且只有在真正有socket 读写事件进行时,
才会使用IO 资源,所以它大大减少了资源占用。在Java NIO 中,是通过selector.select()去查询每个通道是否有到达事件,
如果没有事件,则一直阻塞在那里,因此这种方式会导致用户线程的阻塞。多路复用IO 模式,通过一个线程就可以管理多个socket,
只有当socket 真正有读写事件发生才会占用资源来进行实际的读写操作。因此,多路复用IO 比较适合连接数比较多的情况。
        
	另外多路复用IO 为何比非阻塞IO 模型的效率高是因为在非阻塞IO 中,不断地询问socket 状态时通过用户线程去进行的,
而在多路复用IO 中,轮询每个socket 状态是内核在进行的,这个效率要比用户线程要高的多。
        
	不过要注意的是,多路复用IO 模型是通过轮询的方式来检测是否有事件到达,并且对到达的事件逐一进行响应。
因此对于多路复用IO 模型来说,一旦事件响应体很大,那么就会导致后续的事件迟迟得不到处理,并且会影响新的事件轮询。

信号驱动IO模型

	在信号驱动IO 模型中,当用户线程发起一个IO 请求操作,会给对应的socket 注册一个信号函数,
然后用户线程会继续执行,当内核数据就绪时会发送一个信号给用户线程,用户线程接收到信号之后,
便在信号函数中调用IO 读写操作来进行实际的IO 请求操作。

异步IO模型

	异步IO 模型才是最理想的IO 模型,在异步IO 模型中,当用户线程发起read 操作之后,
立刻就可以开始去做其它的事。而另一方面,从内核的角度,当它受到一个asynchronous read 之后,
它会立刻返回,说明read 请求已经成功发起了,因此不会对用户线程产生任何block。然后,
内核会等待数据准备完成,然后将数据拷贝到用户线程,当这一切都完成之后,内核会给用户线程发送一个信号,
告诉它read 操作完成了。也就说用户线程完全不需要实际的整个IO 操作是如何进行的,只需要先发起一个请求,
当接收内核返回的成功信号时表示IO 操作已经完成,可以直接去使用数据了。
        
   也就说在异步IO 模型中,IO 操作的两个阶段都不会阻塞用户线程,这两个阶段都是由内核自动完成,
然后发送一个信号告知用户线程操作已完成。用户线程中不需要再次调用IO 函数进行具体的读写。
这点是和信号驱动模型有所不同的,在信号驱动模型中,当用户线程接收到信号表示数据已经就绪,
然后需要用户线程调用IO 函数进行实际的读写操作;而在异步IO 模型中,收到信号表示IO 操作已经完成,
不需要再在用户线程中调用IO 函数进行实际的读写操作。     
        

JAVA IO包

技术分享图片

3、Java NIO

技术分享图片
技术分享图片
技术分享图片
技术分享图片

	NIO 主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector。
    传统IO 基于字节流和字符流进行操作,
    而NIO 基于Channel 和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,
      或者从缓冲区写入到通道中。Selector(选择区)用于监听多个通道的事件(比如:连接打开,数据到达)。
      因此,单个线程可以监听多个数据通道。
        
NIO 和传统IO 之间第一个最大的区别是,IO 是面向流的,NIO 是面向缓冲区的。

技术分享图片

NIO的缓冲区

      Java IO 面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何
地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。
NIO 的缓冲导向方法不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。
这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。
而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。

NIO的非阻塞

      IO 的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,
直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 NIO 的非阻塞模式,
使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,
就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 
非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,
这个线程同时可以去做别的事情。 线程通常将非阻塞IO 的空闲时间用于在其它通道上执行IO 操作,
所以一个单独的线程现在可以管理多个输入和输出通道(channel)。

技术分享图片

Channel

      首先说一下Channel,国内大多翻译成“通道”。Channel 和IO 中的Stream(流)是差不多一个等级的。
只不过Stream 是单向的,譬如:InputStream, OutputStream,而Channel 是双向的,
既可以用来进行读操作,又可以用来进行写操作。

NIO 中的Channel 的主要实现有:
1. FileChannel
2. DatagramChannel
3. SocketChannel
4. ServerSocketChannel

这里看名字就可以猜出个所以然来:分别可以对应文件IO、UDP 和TCP(Server 和Client)。
下面演示的案例基本上就是围绕这4 个类型的Channel 进行陈述的。

Buffer

	Buffer,故名思意,缓冲区,实际上是一个容器,是一个连续数组。Channel 提供从文件、
网络读取数据的渠道,但是读取或写入的数据都必须经由Buffer。

技术分享图片

      上面的图描述了从一个客户端向服务端发送数据,然后服务端接收数据的过程。客户端发送数据时,
必须先将数据存入Buffer 中,然后将Buffer 中的内容写入通道。
服务端这边接收数据必须通过Channel 将数据读入到Buffer 中,然后再从Buffer 中取出数据来处理。

在NIO 中,Buffer 是一个顶层父类,它是一个抽象类,常用的Buffer 的子类有:
ByteBuffer、IntBuffer、 CharBuffer、 LongBuffer、 DoubleBuffer、FloatBuffer、
ShortBuffer

Selector

      Selector 类是NIO 的核心类,Selector 能够检测多个注册的通道上是否有事件发生,如果有事件发生,
便获取事件然后针对每个事件进行相应的响应处理。这样一来,只是用一个单线程就可以管理多个通道,
也就是管理多个连接。这样使得只有在连接真正有读写事件发生时,才会调用函数来进行读写,
就大大地减少了系统开销,并且不必为每个连接都创建一个线程,不用去维护多个线程,并且避免了多线程之间的上下文切换导致的开销。

技术分享图片

4、JAVA集合

List和Set:

* Collection接口:单列集合,用来存储一个一个的对象
* list接口:存储有序的、可重复的数据。 -->"动态"数组,替换原有的数组
*
* 面试题:ArrayList、LinkedList、Vector三种的异同?
*   同:三个类都是实现了List接口,存储数据的特点相同:存储有序的、可重复的数据
*  不同:ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementDate存储
*      LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
*      Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementDate存储。
*
* Set接口:存储无序的、不可重复的数据
*      HashSet:作为Set接口的主要实现类;线程不安全的;可以存储null值
*          LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历。
*                        对于频繁的遍历操作,LinkedHashSet效率高于HashSet。
*      TreeSet:可以按照添加对象的指定属性,进行排序。
*      以HashSet为例说明:
*      1.无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。
*      2.不可重复性:保证添加的元素按照equals()判断时,不能返回true。即:相同的元素只能添加一个。
*
*      添加元素的过程:以HashSet为例:
*          我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算a的哈希值,
*          此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断
*          数组此位置上是否已经有元素:
*              如果此位置上没有其他元素,则元素a添加成功。 -->情况1
*              如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
*                  如果hash值不相同,则元素a添加成功。->情况2
*                  如果hash值相同,进而需要调用元素a所在类的equals()方法:
*                      equals()返回true,元素a添加失败
*                      equals()返回false,则元素a添加成功。->情况3
*
*    对于添加成功的情况2和情况3而言:元素a与已经存在指定索引位置上数据以链表的方式存储。
*    jdk 7:元素a放到数组中,指向原来的元素。
*    jdk 8:原来的元素在数组中,指向元素a.    七上八下。

*   HashSet底层:数组+链表的结构。
*     要求:向set中添加的数据,其所在的类一定要重写hashCode()和equals()
*          重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相等的散列码
*
*      LinkedHashSet的使用
*      LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据。
*       优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet。
*
*     TreeSet:
*      1.向TreeSet中添加的数据,要求是相同类的对象。
*      2.两种排序方式:自然排序(实现Comparable接口)和定制排序(Comparator)
*      3.自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals()。
*      3.定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals()。

自然排序(实现Comparable接口):

定制排序(Comparator):

Map接口

 一.Map的实现类结构:
*  | ----Map:双列数据,存储key-value对的数据 --类似于高中的函数:y = f(x)
*          |----HashMap:作为Map的主要实现类;线程不安全的,效率高;存储null的key和value
*              |----LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历。
*                      原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素,
*                      对于频繁的遍历操作,此类执行效率高于HashMap。
*          |----TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序。
*                      底层使用红黑树
*          |----Hashtable:作为古老的实现类;线程安全的,效率低;不能存储null的key和value。
*              |----Properties:常用来处理配置文件。key和value都是String类型。
*
* HashMap的底层:数组+链表(jdk7及之前)
*              数组+链表+红黑树(jdk8)
*
* 面试题:
*  1.HashMap的底层实现原理?
*  2.HashMap和Hashtable的异同?
*  3.CurrentHashMap与Hashtable的异同?
*
* 二、Map结构的理解:
*   Map中的key:无序的、不可重复的,使用Set存储所有的key --->key所在的类要重写equals()和hashCode()(以HashMap为例)
*   Map中的value:无序的、可重复的,使用Collection存储所有的value --->value所在的类要重写equals()
*   一个键值对:key-value构成了一个Entry对象。
*   Map中的entry:无序的、不可重复的,使用Set存储所有的entry。
* 三、HashMap的底层实现原理:
*      以jdk7为例说明:
*          HashMap map = new HashMap();在实例化以后,底层创建了长度是16的一维数组Entry[] table.
*          ...可能已经执行过多次put...
*          map.put(key1,value1):
*          首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算数计算以后,得到在Entry数组中存放位置。
*          如果此位置上的数据为空,此时的key1-value1添加成功。----情况1
*          如果此位置上数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),
*          比较key1和已经存在的一个或多个数据的哈希值:
*              如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。----情况2
*              如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)方法,比较:
*                  如果equals()返回false:此时key1-value1添加成功。----情况3
*                   如果equals()返回true:使用value1替换value2值。
*      补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。
*      在不断的添加过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空),扩容。默认的扩容方式:扩容为原来容量的2倍,并将原来的数据复制过来。
*
*      jdk8 相比较jdk7在底层实现方面的不同:
*          1.new HashMap():底层没有创建一个长度为16的数组。
*          2.jdk 8底层的数组是:Node[],而非Entry[]
*          3.首次调用put()方法时,底层创建长度为16的数组
*          4.jdk7底层结构只有:数组+链表。jdk8中底层结构:数组+链表+红黑树。
*              当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64时,
*              此时此索引位置上的所有数据改为使用红黑树存储。
*
*              DEFAULT_INITIAL_CAPACITY:HashMap的默认容量,16
*              DEFAULT_LOAD_FACTOR::HashMap的默认加载因子:0.75
*              threshold:扩容的临界值,=容量*填充因子:16*0.75 => 12
*              TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树:8
*              MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64
*
*  四、LinkedHashMap的底层实现原理(了解)
*      源码中:
*       static class Entry<K,V> extends HashMap.Node</K,V>{
*           Entry<K,V>before,after;//能够记录添加的元素的先后顺序
*       }
HashMap hashMap = new HashMap();
hashMap.put("AA",33);
hashMap.put("AB",14);
hashMap.put(44,535);

//遍历所有的key集:keySet()
Set set = hashMap.keySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()){
    System.out.println(iterator.next());
}
//遍历所有的value集:values()
Collection values = hashMap.values();
for(Object obj:values){
    System.out.println(obj);
}
//遍历所有的key-value
//entrySet()
Set entrySet = hashMap.entrySet();
Iterator iterator1 = entrySet.iterator();
while(iterator1.hasNext()){
  Object obj = iterator1.next();
  Map.Entry entry = (Map.Entry) obj;
    System.out.println(entry.getKey() + "---" + entry.getValue());
}

//方式二:
Set keySet = hashMap.keySet();
Iterator iterator2 = keySet.iterator();
while(iterator2.hasNext()){
    Object key = iterator2.next();
    Object value = hashMap.get(key);

}

key.hashCode(),Math.abs(key.hashcode()%15)

接口继承关系和实

集合类存放于Java.util 包中,主要有3 种:set(集)、list(列表包含Queue)和map(映射)。
	1. Collection:Collection 是集合List、Set、Queue 的最基本的接口。
	2. Iterator:迭代器,可以通过迭代器遍历集合中的数据
	3. Map:是映射表的基础接口

Collections工具类:操作collection、Map的工具类

Collections类中提供了多个synchronizedXxx()方法,该方法可使将指定集合包装成线程同步的集合,
从而可以解决多线程并发访问集合时的线程安全问题。
List list = new ArrayList();
list.add(56);
list.add(0);
list.add(66);
list.add(-76);

List dest = Arrays.asList(new Object[list.size()]);
System.out.println(dest.size()+"dest:"+dest);
Collections.copy(dest,list);
System.out.println(dest);

List synchronizedList = Collections.synchronizedList(list);

技术分享图片
技术分享图片

List

Java 的List 是非常常用的数据类型。List 是有序的Collection。Java List 一共三个实现类:

分别是ArrayList、Vector 和LinkedList。

技术分享图片

ArrayList(数组)

	ArrayList 是最常用的List 实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。
数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要将已经有数组的数据复制到新的存储空间中。
当从ArrayList 的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。
因此,它适合随机查找和遍历,不适合插入和删除。

Vector(数组实现、线程同步)

	Vector 与ArrayList 一样,也是通过数组实现的,不同的是它支持线程的同步,
即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,
因此,访问它比访问ArrayList 慢。

LinkedList(链表)

	LinkedList 是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。
另外,他还提供了List 接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。

set

	Set 注重独一无二的性质,该体系集合用于存储无序(存入和取出的顺序不一定相同)元素,值不能重复。
对象的相等性本质是对象hashCode 值(java 是依据对象的内存地址计算出的此序号)判断的,
如果想要让两个不同的对象视为相等的,就必须覆盖Object 的hashCode 方法和equals 方法。

技术分享图片

HashSet(Hash表)

	哈希表边存放的是哈希值。HashSet 存储元素的顺序并不是按照存入时的顺序(和List 显然不同) 
而是按照哈希值来存的所以取数据也是按照哈希值取得。元素的哈希值是通过元素的hashcode 方法来获取的, 
HashSet 首先判断两个元素的哈希值,如果哈希值一样,接着会比较equals 方法 如果 equls 结果为true ,
HashSet 就视为同一个元素。如果equals 为false 就不是同一个元素。	
        
	哈希值相同equals 为false 的元素是怎么存储呢,就是在同样的哈希值下顺延(可以认为哈希值相同的元素放在一个哈希桶中)。
也就是哈希一样的存一列。
        如图1 表示hashCode 值不相同的情况;
        图2 表示hashCode 值相同,但equals 不相同的情况。
HashSet 通过hashCode 值来确定元素在内存中的位置。一个hashCode 位置上可以存放多个元素。

技术分享图片

TreeSet(二叉树)

	1. TreeSet()是使用二叉树的原理对新add()的对象按照指定的顺序排序(升序、降序),
每增加一个对象都会进行排序,将对象插入的二叉树指定的位置。
	2. Integer 和String 对象都可以进行默认的TreeSet 排序,而自定义类的对象是不可以的,
自己定义的类必须实现Comparable 接口,并且覆写相应的compareTo()函数,才可以正常使用。
    3. 在覆写compare()函数时,要返回相应的值才能使TreeSet 按照一定的规则来排序
	4. 比较此对象与指定对象的顺序。如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。

LinkedHashSet(HashSet+LinkedHashMap)

	对于LinkedHashSet 而言, 它继承与HashSet 、又基于LinkedHashMap 来实现的。
	LinkedHashSet 底层使用LinkedHashMap 来保存所有元素,它继承与HashSet,
其所有的方法操作上又与HashSet 相同,因此LinkedHashSet 的实现上非常简单,只提供了四个构造方法,
并通过传递一个标识参数,调用父类的构造器,底层构造一个LinkedHashMap 来实现,
在相关操作上与父类HashSet 的操作相同,直接调用父HashSet 的方法即可。

Map

技术分享图片

HashMap(数组+链表+红黑树)

	key.hashCode(),Math.abs(key.hashcode()%15)

	HashMap 根据键的hashCode 值存储数据,大多数情况下可以直接定位到它的值,因而具有很快的访问速度,
但遍历顺序却是不确定的。 HashMap 最多只允许一条记录的键为null,允许多条记录的值为null。
HashMap 非线程安全,即任一时刻可以有多个线程同时写HashMap,可能会导致数据的不一致。如果需要满足线程安全,
可以用 Collections 的synchronizedMap 方法使HashMap 具有线程安全的能力,或者使用ConcurrentHashMap。
我们用下面这张图来介绍HashMap 的结构。

Java7实现

技术分享图片

	大方向上,HashMap 里面是一个数组,然后数组中每个元素是一个单向链表。
上图中,每个绿色的实体是嵌套类 Entry 的实例,Entry 包含四个属性:key, value, hash 值和用于单向链表的 next。
	1. capacity:当前数组容量,始终保持 2^n,可以扩容,扩容后数组大小为当前的 2 倍。
	2. loadFactor:负载因子,默认为 0.75。
    3. threshold:扩容的阈值,等于 capacity * loadFactor

Java8实现

	Java8 对 HashMap 进行了一些修改,最大的不同就是利用了红黑树,所以其由 数组+链表+红黑树 组成。
	根据 Java7 HashMap 的介绍,我们知道,查找的时候,根据 hash 值我们能够快速定位到数组的具体下标,
但是之后的话,需要顺着链表一个个比较下去才能找到我们需要的,时间复杂度取决于链表的长度,为 O(n)。
为了降低这部分的开销,在 Java8 中,当链表中的元素超过了 8 个以后,会将链表转换为红黑树,
在这些位置进行查找的时候可以降低时间复杂度为 O(logN)。

技术分享图片

ConcurrentHashMap

Segment段
	ConcurrentHashMap 和 HashMap 思路是差不多的,但是因为它支持并发操作,所以要复杂一些。
    整个 ConcurrentHashMap 由一个个 Segment 组成,Segment 代表”部分“或”一段“的意思,
所以很多地方都会将其描述为分段锁。注意,行文中,我很多地方用了“槽”来代表一个segment。
    
线程安全(Segment继承ReentrantLock加锁)
	简单理解就是,ConcurrentHashMap 是一个 Segment 数组,Segment 通过继承ReentrantLock 来进行加锁,
所以每次需要加锁的操作锁住的是一个 segment,这样只要保证每个 Segment 是线程安全的,也就实现了全局的线程安全。
    
并行度(默认16)
	concurrencyLevel:并行级别、并发数、Segment 数,怎么翻译不重要,理解它。默认是 16,
也就是说 ConcurrentHashMap 有 16 个 Segments,所以理论上,这个时候,最多可以同时支持 16 个线程并发写,
只要它们的操作分别分布在不同的 Segment 上。这个值可以在初始化的时候设置为其他值,但是一旦初始化以后,
它是不可以扩容的。再具体到每个 Segment 内部,其实每个 Segment 很像之前介绍的 HashMap,
不过它要保证线程安全,所以处理起来要麻烦些。
    
Java8 实现(引入了红黑树)
Java8 对 ConcurrentHashMap 进行了比较大的改动,Java8 也引入了红黑树。

技术分享图片
技术分享图片

HashTable(线程安全)

	Hashtable 是遗留类,很多映射的常用功能与HashMap 类似,不同的是它承自Dictionary 类,
并且是线程安全的,任一时间只有一个线程能写Hashtable,并发性不如ConcurrentHashMap,因为ConcurrentHashMap 引入了分段锁。
Hashtable 不建议在新代码中使用,不需要线程安全的场合可以用HashMap 替换,需要线程安全的场合可以用ConcurrentHashMap 替换。

TreeMap(可排序)

	TreeMap 实现SortedMap 接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,
也可以指定排序的比较器,当用Iterator 遍历TreeMap 时,得到的记录是排过序的。
	如果使用排序的映射,建议使用TreeMap。
	在使用TreeMap 时,key 必须实现Comparable 接口或者在构造TreeMap 传入自定义的Comparator,
否则会在运行时抛出java.lang.ClassCastException 类型的异常。
	参考:https://www.ibm.com/developerworks/cn/java/j-lo-tree/index.html

LinkedHashMap(记录插入顺序)

LinkedHashMap 是HashMap 的一个子类,保存了记录的插入顺序,在用Iterator 遍历
LinkedHashMap 时,先得到的记录肯定是先插入的,也可以在构造时带参数,按照访问次序排序。

Hashtable与HashMap的区别

 	HashMap不是线程安全的,HashTable是线程安全。
    HashMap允许空(null)的键和值(key),HashTable则不允许。
 	HashMap性能优于Hashtable。
Map
1.Map是一个以键值对存储的接口。Map下有两个具体的实现,分别是HashMap和HashTable.
2.HashMap是线程非安全的,HashTable是线程安全的,所以HashMap的效率高于HashTable.
3.HashMap允许键或值为空,而HashTable不允许键或值为空.

4、Java多线程并发

java并发知识库

技术分享图片

Java线程实现/创建方式

1、继承Thread类
    Thread 类本质上是实现了Runnable 接口的一个实例,代表一个线程的实例。启动线程的唯一方
法就是通过Thread 类的start()实例方法。start()方法是一个native 方法,它将启动一个新线
程,并执行run()方法。
    public class MyThread extends Thread {
		public void run() {
			System.out.println("MyThread.run()");
		}
	}
	MyThread myThread1 = new MyThread();
	myThread1.start();

2、实现Runnable接口
    如果自己的类已经extends 另一个类,就无法直接extends Thread,此时,可以实现一个Runnable 接口。
public class MyThread extends OtherClass implements Runnable {
	public void run() {
		System.out.println("MyThread.run()");
	}
}
//启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread 实例:
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
//事实上,当传入一个Runnable target 参数给Thread 后,Thread 的run()方法就会调用
target.run()
public void run() {
	if (target != null) {
		target.run();
	}
}
3、ExecutorService、Callable<Class>、Future 有返回值线程
   有返回值的任务必须实现Callable 接口,类似的,无返回值的任务必须Runnable 接口。
执行Callable 任务后,可以获取一个Future 的对象,在该对象上调用get 就可以获取到Callable 任务返回的Object 了,
再结合线程池接口ExecutorService 就可以实现传说中有返回结果的多线程了。 
    //创建一个线程池
	ExecutorService pool = Executors.newFixedThreadPool(taskSize);
	// 创建多个有返回值的任务
	List<Future> list = new ArrayList<Future>();
	for (int i = 0; i < taskSize; i++) {
		Callable c = new MyCallable(i + " ");
		// 执行任务并获取Future 对象
		Future f = pool.submit(c);
		list.add(f);
	}
	// 关闭线程池
	pool.shutdown();
	// 获取所有并发任务的运行结果
	for (Future f : list) {
		// 从Future 对象上获取任务的返回值,并输出到控制台
		System.out.println("res:" + f.get().toString());
	}

4、基于线程池的方式
      线程和数据库连接这些资源都是非常宝贵的资源。
      那么每次需要的时候创建,不需要的时候销毁,是非常浪费资源的。
      那么我们就可以使用缓存的策略,也就是使用线程池。
    // 创建线程池
	ExecutorService threadPool = Executors.newFixedThreadPool(10);
	while(true) {
		threadPool.execute(new Runnable() { // 提交多个线程任务,并执行
            @Override
            public void run() {
				System.out.println(Thread.currentThread().getName() + " is running ..");
				try {
					Thread.sleep(3000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
	}

4种线程池

	Java 里面线程池的顶级接口是Executor,但是严格意义上讲Executor 并不是一个线程池,
而只是一个执行线程的工具。真正的线程池接口是ExecutorService。

技术分享图片

1、newCachedThreadPool
	创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。
对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。
调用 execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。
终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。
    
2、newFixedThreadPool
	创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
在任意点,在大多数 nThreads 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,
则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何线程终止,
那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在。
    
3、newScheduledThreadPool
	创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
	ScheduledExecutorService scheduledThreadPool= Executors.newScheduledThreadPool(3);
	scheduledThreadPool.schedule(newRunnable(){
        @Override
        public void run() {
            System.out.println("延迟三秒");
        }
	}, 3, TimeUnit.SECONDS);
	scheduledThreadPool.scheduleAtFixedRate(newRunnable(){
		@Override
		public void run() {
			System.out.println("延迟1 秒后每三秒执行一次");
		}
	},1,3,TimeUnit.SECONDS);

4、newSingleThreadExecutor
	Executors.newSingleThreadExecutor()返回一个线程池(这个线程池只有一个线程),
这个线程池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去!

线程生命周期(状态)

	当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。
	在线程的生命周期中,
            它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5 种状态。
        尤其是当线程启动以后,它不可能一直"霸占"着CPU 独自运行,所以CPU 需要在多条线程之间切换,
        于是线程状态也会多次在运行、阻塞之间切换
    
   1、新建状态(NEW)
	当程序使用new 关键字创建了一个线程之后,该线程就处于新建状态,此时仅由JVM 为其分配内存,并初始化其成员变量的值

   2、就绪状态(RUNNABLE)
        当线程对象调用了start()方法之后,该线程处于就绪状态。Java 虚拟机会为其创建方法调用栈和程序计数器,等待调度运行。

   3、运行状态(RUNNING)
        如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态。

   4、阻塞状态(BLOCKED)
        阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。
        直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。
        阻塞的情况分三种:
	    等待阻塞(o.wait->等待队列):
        	运行(running)的线程执行o.wait()方法,JVM 会把该线程放入等待队列(waitting queue)中。
	    同步阻塞(lock->锁池):
		运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM 会把该线程放入锁池(lock pool)中。
	    其他阻塞(sleep/join):
		运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O 请求时,
                JVM 会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、
                或者I/O处理完毕时,线程重新转入可运行(runnable)状态。    
   5、线程死亡(DEAD)
    线程会以下面三种方式结束,结束后就是死亡状态。
	正常结束
		1. run()或call()方法执行完成,线程正常结束。
	异常结束
		2. 线程抛出一个未捕获的Exception 或Error。
	调用 stop
                3. 直接调用该线程的stop()方法来结束该线程—该方法通常容易导致死锁,不推荐使用

技术分享图片

终止线程的4种方式

1、正常运行结束
    程序运行结束,线程自动结束。

2、使用退出标志退出线程
    一般run()方法执行完,线程就会正常结束,然而,常常有些线程是伺服线程。它们需要长时间的运行,
只有在外部某些条件满足的情况下,才能关闭这些线程。使用一个变量来控制循环,例如:
最直接的方法就是设一个boolean 类型的标志,并通过设置这个标志为true 或false 来控制while
循环是否退出,代码示例:
    public class ThreadSafe extends Thread {
public volatile boolean exit = false;
public void run() {
while (!exit){
//do something
}
}
}
定义了一个退出标志exit,当exit 为true 时,while 循环退出,exit 的默认值为false.在定义exit时,
使用了一个Java 关键字volatile,这个关键字的目的是使exit 同步,也就是说在同一时刻只
能由一个线程来修改exit 的值。

3、Interrupt方法结束线程
    使用interrupt()方法来中断线程有两种情况:
          1. 线程处于阻塞状态:如使用了sleep,同步锁的wait,socket 中的receiver,accept 等方法时,
      会使线程处于阻塞状态。当调用线程的interrupt()方法时,会抛出InterruptException 异常。
      阻塞中的那个方法抛出这个异常,通过代码捕获该异常,然后break 跳出循环状态,
      从而让我们有机会结束这个线程的执行。通常很多人认为只要调用interrupt 方法线程就会结束,
      实际上是错的, 一定要先捕获InterruptedException 异常之后通过break 来跳出循环,
      才能正常结束run 方法。
          2. 线程未处于阻塞状态:使用isInterrupted()判断线程的中断标志来退出循环。
      当使用interrupt()方法时,中断标志就会置true,和使用自定义的标志来控制循环是一样的道理。
      public class ThreadSafe extends Thread {
            public void run() {
                  while (!isInterrupted()){ //非阻塞过程中通过判断中断标志来退出
                        try{
                              Thread.sleep(5*1000);//阻塞过程捕获中断异常来退出
                        }catch(InterruptedException e){
                              e.printStackTrace();
                              break;//捕获到异常之后,执行break 跳出循环
                        }
                  }
            }
      }
4、stop方法终止线程(线程不安全)
    程序中可以直接使用thread.stop()来强行终止线程,但是stop 方法是很危险的,就象突然关
闭计算机电源,而不是按正常程序关机一样,可能会产生不可预料的结果,不安全主要是:
thread.stop()调用之后,创建子线程的线程就会抛出ThreadDeatherror 的错误,并且会释放子
线程所持有的所有锁。一般任何进行加锁的代码块,都是为了保护数据的一致性,如果在调用
thread.stop()后导致了该线程所持有的所有锁的突然释放(不可控制),那么被保护数据就有可能呈
现不一致性,其他线程在使用这些被破坏的数据时,有可能导致一些很奇怪的应用程序错误。因
此,并不推荐使用stop 方法来终止线程。

Sleep与wait 区别

1. 对于sleep()方法,我们首先要知道该方法是属于Thread 类中的。而wait()方法,则是属于Object 类中的。

2. sleep()方法导致了程序暂停执行指定的时间,让出cpu 该其他线程,但是他的监控状态依然保持者,
      当指定的时间到了又会自动恢复运行状态。

3. 在调用sleep()方法的过程中,线程不会释放对象锁。

4. 而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,
      只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。

start与run区别

1. start()方法来启动线程,真正实现了多线程运行。这时无需等待run 方法体代码执行完毕,
      可以直接继续执行下面的代码。
2. 通过调用Thread 类的start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。

3. 方法run()称为线程体,它包含了要执行的这个线程的内容,线程就进入了运行状态,
      开始运行run 函数当中的代码。 Run 方法运行结束, 此线程终止。然后CPU 再调度其它线程。

java后台线程

1. 定义:守护线程--也称“服务线程”,他是后台线程,它有一个特性,
      即为用户线程 提供公共服务,在没有用户线程可服务时会自动离开。

2. 优先级:守护线程的优先级比较低,用于为系统中的其它对象和线程提供服务。

3. 设置:通过setDaemon(true)来设置线程为“守护线程”;
      将一个用户线程设置为守护线程的方式是在 线程对象创建 之前 用线程对象的setDaemon 方法。

4. 在Daemon 线程中产生的新线程也是Daemon 的。

5. 线程则是JVM 级别的,以Tomcat 为例,如果你在Web 应用中启动一个线程,
      这个线程的生命周期并不会和Web 应用程序保持同步。
      也就是说,即使你停止了Web 应用,这个线程依旧是活跃的。

6. example: 垃圾回收线程就是一个经典的守护线程,当我们的程序中不再有任何运行的Thread,
      程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是JVM 上仅剩的线程时,
      垃圾回收线程会自动离开。它始终在低级别的状态中运行,用于实时监控和管理系统中的可回收资源。

7. 生命周期:守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周
      期性地执行某种任务或等待处理某些发生的事件。也就是说守护线程不依赖于终端,但是依
      赖于系统,与系统“同生共死”。当JVM 中所有的线程都是守护线程的时候,JVM 就可以退
      出了;如果还有一个或以上的非守护线程则JVM 不会退出。

java锁

1、乐观锁
      乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,
  所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,采取在写时先读出当前版本号,
  然后加锁操作(比较跟上一次的版本号,如果一样则更新),如果失败则要重复读-比较-写的操作。
java 中的乐观锁基本都是通过CAS 操作实现的,
      CAS 是一种更新的原子操作,比较当前值跟传入值是否一样,一样则更新,否则失败。
      CAS:Compare and Swap,即比较再交换。
      对CAS的理解,CAS是一种无锁算法,CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。
      当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
    
2、悲观锁
    悲观锁是就是悲观思想,即认为写多,遇到并发写的可能性高,每次去拿数据的时候都认为别人会修改,
所以每次在读写数据的时候都会上锁,这样别人想读写这个数据就会block 直到拿到锁。
    java 中的悲观锁就是Synchronized,AQS 框架下的锁则是先尝试cas 乐观锁去获取锁,获取不到,
才会转换为悲观锁,如RetreenLock。
    
3、自旋锁
    自旋锁原理非常简单,如果持有锁的线程能在很短时间内释放锁资源,
那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋),
等持有锁的线程释放锁后即可立即获取锁,这样就避免用户线程和内核的切换的消耗。
线程自旋是需要消耗cup 的,说白了就是让cup 在做无用功,如果一直获取不到锁,那线程也不能一直占用cup
自旋做无用功,所以需要设定一个自旋等待的最大时间。
    如果持有锁的线程执行的时间超过自旋等待的最大时间扔没有释放锁,
就会导致其它争用锁的线程在最大等待时间内还是获取不到锁,这时争用线程会停止自旋进入阻塞状态。
    
自旋锁的优缺点
	自旋锁尽可能的减少线程的阻塞,这对于锁的竞争不激烈,且占用锁时间非常短的代码块来说性能能大幅度的提升,
因为自旋的消耗会小于线程阻塞挂起再唤醒的操作的消耗,这些操作会导致线程发生两次上下文切换!
	但是如果锁的竞争激烈,或者持有锁的线程需要长时间占用锁执行同步块,这时候就不适合使用自旋锁了,
因为自旋锁在获取锁前一直都是占用cpu 做无用功,占着XX 不XX,同时有大量线程在竞争一个锁,会导致获取锁的时间很长,
线程自旋的消耗大于线程阻塞挂起操作的消耗,其它需要cup 的线程又不能获取到cpu,造成cpu 的浪费。
所以这种情况下我们要关闭自旋锁;
    
自旋锁时间阀值(1.6引入了适应性自旋锁)
	自旋锁的目的是为了占着CPU 的资源不释放,等到获取到锁立即进行处理。但是如何去选择自旋的执行时间呢?
如果自旋执行时间太长,会有大量的线程处于自旋状态占用CPU 资源,进而会影响整体系统的性能。因此自旋的周期选的额外重要!
    JVM 对于自旋周期的选择,jdk1.5 这个限度是一定的写死的,在1.6 引入了适应性自旋锁,
适应性自旋锁意味着自旋的时间不在是固定的了,而是由前一次在同一个锁上的自旋时间以及锁的拥有者的状态来决定,
基本认为一个线程上下文切换的时间是最佳的一个时间,同时JVM 还针对当前CPU 的负荷情况做了较多的优化,
如果平均负载小于CPUs 则一直自旋,如果有超过(CPUs/2)个线程正在自旋,则后来线程直接阻塞,
如果正在自旋的线程发现Owner 发生了变化则延迟自旋时间(自旋计数)或进入阻塞,如果CPU 处于节电模式则停止自旋,
自旋时间的最坏情况是CPU的存储延迟(CPU A 存储了一个数据,到CPU B 得知这个数据直接的时间差),
自旋时会适当放弃线程优先级之间的差异。
    
自旋锁的开启
JDK1.6 中-XX:+UseSpinning 开启;
-XX:PreBlockSpin=10 为自旋次数;
JDK1.7 后,去掉此参数,由jvm 控制;
    
4、Synchronized同步锁
    synchronized 它可以把任意一个非NULL 的对象当作锁。他属于独占式的悲观锁,同时属于可重
入锁。
    Synchronized作用范围
    1.作用于方法时,锁住的是对象的实例(this);
    2. 当作用于静态方法时,锁住的是Class 实例,又因为Class 的相关数据存储在永久带PermGen
(jdk1.8 则是metaspace),永久带是全局共享的,因此静态方法锁相当于类的一个全局锁,会锁所有调用该方法的线程;
	3. synchronized 作用于一个对象实例时,锁住的是所有以该对象为锁的代码块。它有多个队列,
当多个线程一起访问某个对象监视器的时候,对象监视器会将这些线程存储在不同的容器中。
Synchronized 核心组件
    1) Wait Set:哪些调用wait 方法被阻塞的线程被放置在这里;
    2) Contention List:竞争队列,所有请求锁的线程首先被放在这个竞争队列中;
    3) Entry List:Contention List 中那些有资格成为候选资源的线程被移动到Entry List 中;
    4) OnDeck:任意时刻,最多只有一个线程正在竞争锁资源,该线程被成为OnDeck;
    5) Owner:当前已经获取到所资源的线程被称为Owner;
    6) !Owner:当前释放锁的线程。
    
    Synchronized实现
        1. JVM 每次从队列的尾部取出一个数据用于锁竞争候选者(OnDeck),但是并发情况下,ContentionList 会被大量的并发线程进行CAS 访问,
为了降低对尾部元素的竞争,JVM会将一部分线程移动到EntryList 中作为候选竞争线程。

	2. Owner 线程会在unlock 时,将ContentionList 中的部分线程迁移到EntryList 中,并指定
EntryList 中的某个线程为OnDeck 线程(一般是最先进去的那个线程)。

	3. Owner 线程并不直接把锁传递给OnDeck 线程,而是把锁竞争的权利交给OnDeck,OnDeck 需要重新竞争锁。这样虽然牺牲了一些公平性,
但是能极大的提升系统的吞吐量,在JVM 中,也把这种选择行为称之为“竞争切换”。

	4. OnDeck 线程获取到锁资源后会变为Owner 线程,而没有得到锁资源的仍然停留在EntryList中。如果Owner 线程被wait 方法阻塞,
则转移到WaitSet 队列中,直到某个时刻通过notify或者notifyAll 唤醒,会重新进去EntryList 中。

	5. 处于ContentionList、EntryList、WaitSet 中的线程都处于阻塞状态,该阻塞是由操作系统
来完成的(Linux 内核下采用pthread_mutex_lock 内核函数实现的)。

	6. Synchronized 是非公平锁。 Synchronized 在线程进入ContentionList 时,等待的线程会先尝试自旋获取锁,如果获取不到就进入ContentionList,
这明显对于已经进入队列的线程是不公平的,还有一个不公平的事情就是自旋获取锁的线程还可能直接抢占OnDeck 线程的锁资源。
参考:https://blog.csdn.net/zqz_zqz/article/details/70233767

	7. 每个对象都有个monitor 对象,加锁就是在竞争monitor 对象,代码块加锁是在前后分别加
上monitorenter 和monitorexit 指令来实现的,方法加锁是通过一个标记位来判断的

	8. synchronized 是一个重量级操作,需要调用操作系统相关接口,性能是低效的,有可能给线
程加锁消耗的时间比有用操作消耗的时间更多。

	9. Java1.6,synchronized 进行了很多的优化,有适应自旋、锁消除、锁粗化、轻量级锁及偏向
锁等,效率有了本质上的提高。在之后推出的Java1.7 与1.8 中,均对该关键字的实现机理做了优化。引入了偏向锁和轻量级锁。都是在对象头中有标记位,不需要经过操作系统加锁。

	10. 锁可以从偏向锁升级到轻量级锁,再升级到重量级锁。这种升级过程叫做锁膨胀;

	11. JDK 1.6 中默认是开启偏向锁和轻量级锁,可以通过-XX:-UseBiasedLocking 来禁用偏向锁。
    
    5、ReentrantLock
    ReentantLock 继承接口Lock 并实现了接口中定义的方法,他是一种可重入锁,除了能完成synchronized 所能完成的所有工作外,
还提供了诸如可响应中断锁、可轮询锁请求、定时锁等避免多线程死锁的方法。

	Lock 接口的主要方法
	1. void lock(): 执行此方法时, 如果锁处于空闲状态, 当前线程将获取到锁. 相反, 如果锁已经
被其他线程持有, 将禁用当前线程, 直到当前线程获取到锁.

	2. boolean tryLock():如果锁可用, 则获取锁, 并立即返回true, 否则返回false. 该方法和
lock()的区别在于, tryLock()只是"试图"获取锁, 如果锁不可用, 不会导致当前线程被禁用,当前线程仍然继续往下执行代码. 
而lock()方法则是一定要获取到锁, 如果锁不可用, 就一直等待, 在未获得锁之前,当前线程并不继续向下执行.

	3. void unlock():执行此方法时, 当前线程将释放持有的锁. 锁只能由持有者释放, 如果线程
并不持有锁, 却执行该方法, 可能导致异常的发生.

	4. Condition newCondition():条件对象,获取等待通知组件。该组件和当前的锁绑定,
当前线程只有获取了锁,才能调用该组件的await()方法,而调用后,当前线程将缩放锁。

	5. getHoldCount() :查询当前线程保持此锁的次数,也就是执行此线程执行lock 方法的次数。

	6. getQueueLength():返回正等待获取此锁的线程估计数,比如启动10 个线程,1 个线程获得锁,此时返回的是9

	7. getWaitQueueLength:(Condition condition)返回等待与此锁相关的给定条件的线程估计数。
比如10 个线程,用同一个condition 对象,并且此时这10 个线程都执行了condition 对象的await 方法,那么此时执行此方法返回10

	8. hasWaiters(Condition condition) : 查询是否有线程等待与此锁有关的给定条件(condition),对于指定contidion 对象,有多少线程执行了condition.await 方法

	9. hasQueuedThread(Thread thread):查询给定线程是否等待获取此锁

	10. hasQueuedThreads():是否有线程等待此锁

	11. isFair():该锁是否公平锁

	12. isHeldByCurrentThread(): 当前线程是否保持锁锁定,线程的执行lock 方法的前后分别是false 和true

	13. isLock():此锁是否有任意线程占用

	14. lockInterruptibly():如果当前线程未被中断,获取锁

	15. tryLock():尝试获得锁,仅在调用时锁未被线程占用,获得锁

	16. tryLock(long timeout TimeUnit unit):如果锁在给定等待时间内没有被另一个线程保持,则获取该锁。
        
	非公平锁
	   JVM 按随机、就近原则分配锁的机制则称为不公平锁,ReentrantLock 在构造函数中提供了是否公平锁的初始化方式,默认为非公平锁。
非公平锁实际执行的效率要远远超出公平锁,除非程序有特殊需要,否则最常用非公平锁的分配机制。
    
        公平锁
           公平锁指的是锁的分配机制是公平的,通常先对锁提出获取请求的线程会先被分配到锁,ReentrantLock 在构造函数中提供了是否公平锁的初始化方式来定义公平锁。
        
ReentrantLock 与synchronized

	1. ReentrantLock 通过方法lock()与unlock()来进行加锁与解锁操作,与synchronized 会被JVM 自动解锁机制不同,ReentrantLock 加锁后需要手动进行解锁。
为了避免程序出现异常而无法正常解锁的情况,使用ReentrantLock 必须在finally 控制块中进行解锁操作。

	2. ReentrantLock 相比synchronized 的优势是可中断、公平锁、多个锁。这种情况下需要
使用ReentrantLock。

        ReentrantLock 实现:

            public class MyService {
                  private Lock lock = new ReentrantLock();
                        //Lock lock=new ReentrantLock(true);//公平锁
                        //Lock lock=new ReentrantLock(false);//非公平锁
                        private Condition condition=lock.newCondition();//创建Condition
                        public void testMethod() {
                              try {
                                    lock.lock();//lock 加锁

                                    //1:wait 方法等待:
                                    //System.out.println("开始wait");
                                    condition.await();
                                    //通过创建Condition 对象来使线程wait,必须先执行lock.lock 方法获得锁

                                    //:2:signal 方法唤醒
                                    condition.signal();//condition 对象的signal 方法可以唤醒wait 线程
                                    for (int i = 0; i < 5; i++) {
                                          System.out.println("ThreadName=" + Thread.currentThread().getName()+ (" " + (i + 1)));
                                    }
                              } catch (InterruptedException e) {
                                    e.printStackTrace();
                              }finally{
                                    lock.unlock();
                              }
                        }
            }

Condition 类和 Object 类锁方法区别
	1. Condition 类的awiat 方法和Object 类的wait 方法等效
        2. Condition 类的signal 方法和Object 类的notify 方法等效
        3. Condition 类的signalAll 方法和Object 类的notifyAll 方法等效
        4. ReentrantLock 类可以唤醒指定条件的线程,而object 的唤醒是随机的
    
tryLock 和 lock 和 lockInterruptibly 的区别

	1. tryLock 能获得锁就返回true,不能就立即返回false,tryLock(long timeout,TimeUnitunit),
                 可以增加时间限制,如果超过该时间段还没获得锁,返回false
	2. lock 能获得锁就返回true,不能的话一直等待获得锁
	3. lock 和lockInterruptibly,如果两个线程分别执行这两个方法,但此时中断这两个线程,lock 不会抛出异常,而lockInterruptibly 会抛出异常。

    Semaphore 信号量
	Semaphore 是一种基于计数的信号量。它可以设定一个阈值,基于此,多个线程竞争获取许可信号,做完自己的申请后归还,超过阈值后,
线程申请许可信号将会被阻塞。Semaphore 可以用来构建一些对象池,资源池之类的,比如数据库连接池实现互斥锁(计数器为1)
我们也可以创建计数为1 的Semaphore,将其作为一种类似互斥锁的机制,这也叫二元信号量,表示两种互斥状态。

代码实现
它的用法如下:
// 创建一个计数阈值为5 的信号量对象
// 只能5 个线程同时访问
Semaphore semp = new Semaphore(5);
      try { // 申请许可
            semp.acquire();
            try {
                  // 业务逻辑
            } catch (Exception e) {
            } finally {
                  // 释放许可
                  semp.release();
            }
      } catch (InterruptedException e) {
      }

Semaphore ?? ReentrantLock
	Semaphore 基本能完成ReentrantLock 的所有工作,使用方法也与之类似,通过acquire()与release()方法来获得和释放临界资源。
经实测,Semaphone.acquire()方法默认为可响应中断锁,与ReentrantLock.lockInterruptibly()作用效果一致,也就是说在等待临界资源的过程中可以被
Thread.interrupt()方法中断。

	此外,Semaphore 也实现了可轮询的锁请求与定时锁的功能,除了方法名tryAcquire 与tryLock不同,其使用方法与ReentrantLock 几乎一致。
Semaphore 也提供了公平与非公平锁的机制,也可在构造函数中进行设定。

	Semaphore 的锁释放操作也由手动进行,因此与ReentrantLock 一样,为避免线程因抛出异常而
无法正常释放锁的情况发生,释放锁的操作也必须在finally 代码块中完成。
    
 AtomicInteger
      首先说明, 此处AtomicInteger,一个提供原子操作的Integer的类,常见的还有AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference 等,
他们的实现原理相同,区别在与运算对象类型的不同。令人兴奋地,还可以通过AtomicReference<V>将一个对象的所有操作转化成原子操作。
	我们知道,在多线程程序中,诸如++i 或 i++等运算不具有原子性,是不安全的线程操作之一。
通常我们会使用synchronized 将该操作变成一个原子操作,但JVM 为此类操作特意提供了一些同步类,使得使用更方便,且使程序运行效率变得更高。
通过相关资料显示,通常AtomicInteger的性能是ReentantLock 的好几倍。
    
可重入锁(递归锁)
	本文里面讲的是广义上的可重入锁,而不是单指JAVA 下的ReentrantLock。可重入锁,也叫做递归锁,指的是同一线程外层函数获得锁之后,
内层递归函数仍然有获取该锁的代码,但不受影响。在JAVA 环境下 ReentrantLock 和synchronized 都是 可重入锁。

公平锁与非公平锁

公平锁(Fair)
	加锁前检查是否有排队等待的线程,优先排队等待的线程,先来先得
非公平锁(Nonfair)
	加锁时不考虑排队等待问题,直接尝试获取锁,获取不到自动到队尾等待
	1. 非公平锁性能比公平锁高5~10 倍,因为公平锁需要在多核的情况下维护一个队列
	2. Java 中的synchronized 是非公平锁,ReentrantLock 默认的lock()方法采用的是非公平锁。

 ReadWriteLock 读写锁
	为了提高性能,Java 提供了读写锁,在读的地方使用读锁,在写的地方使用写锁,灵活控制,如果没有写锁的情况下,
读是无阻塞的,在一定程度上提高了程序的执行效率。读写锁分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm 自己控制的,你只要上好相应的锁即可。

读锁
	如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁
写锁
	如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。总之,读的时候上读锁,写的时候上写锁!

Java 中读写锁有个接口java.util.concurrent.locks.ReadWriteLock,也有具体的实现ReentrantReadWriteLock。
    
    共享锁和独占锁
		java 并发包提供的加锁模式分为独占锁和共享锁。

独占锁
	独占锁模式下,每次只能有一个线程能持有锁,ReentrantLock 就是以独占方式实现的互斥锁。
	独占锁是一种悲观保守的加锁策略,它避免了读/读冲突,如果某个只读线程获取锁,则其他读线
程都只能等待,这种情况下就限制了不必要的并发性,因为读操作并不会影响数据的一致性。

共享锁
	共享锁则允许多个线程同时获取锁,并发访问共享资源,如:ReadWriteLock。共享锁则是一种乐观锁,
            它放宽了加锁策略,允许多个执行读操作的线程同时访问共享资源。

	1. AQS 的内部类Node 定义了两个常量SHARED 和EXCLUSIVE,他们分别标识 AQS 队列中等待线程的锁获取模式。

	2. java 的并发包中提供了ReadWriteLock,读-写锁。它允许一个资源可以被多个读操作访问,或者被一个写操作访问,但两者不能同时进行。

重量级锁(Mutex Lock)
	Synchronized 是通过对象内部的一个叫做监视器锁(monitor)来实现的。但是监视器锁本质又是依赖于底层的操作系统的Mutex Lock 来实现的。
而操作系统实现线程之间的切换这就需要从用户态转换到核心态,这个成本非常高,状态之间的转换需要相对比较长的时间,这就是为什么Synchronized 效率低的原因。
因此,这种依赖于操作系统Mutex Lock 所实现的锁我们称之为“重量级锁”。JDK 中对Synchronized 做的种种优化,其核心都是为了减少这种重量级锁的使用。
	JDK1.6 以后,为了减少获得锁和释放锁所带来的性能消耗,提高性能,引入了“轻量级锁”和“偏向锁”。
    
轻量级锁
	锁的状态总共有四种:无锁状态、偏向锁、轻量级锁和重量级锁。
锁升级
	随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁(但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级)。
	“轻量级”是相对于使用操作系统互斥量来实现的传统锁而言的。但是,首先需要强调一点的是,轻量级锁并不是用来代替重量级锁的,
它的本意是在没有多线程竞争的前提下,减少传统的重量级锁使用产生的性能消耗。在解释轻量级锁的执行过程之前,
先明白一点,轻量级锁所适应的场景是线程交替执行同步块的情况,如果存在同一时间访问同一锁的情况,就会导致轻量级锁膨胀为重量级锁。
    
偏向锁
	Hotspot 的作者经过以往的研究发现大多数情况下锁不仅不存在多线程竞争,而且总是由同一线程多次获得。
偏向锁的目的是在某个线程获得锁之后,消除这个线程锁重入(CAS)的开销,看起来让这个线程得到了偏护。
引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行路径,因为轻量级锁的获取及释放依赖多次CAS 原子指令,
而偏向锁只需要在置换ThreadID 的时候依赖一次CAS 原子指令(由于一旦出现多线程竞争的情况就必须撤销偏向锁,
所以偏向锁的撤销操作的性能损耗必须小于节省下来的CAS 原子指令的性能消耗)。上面说过,轻量级锁是为了在线程交替执行同步块时提高性能,
而偏向锁则是在只有一个线程执行同步块时进一步提高性能。
    
分段锁
	分段锁也并非一种实际的锁,而是一种思想ConcurrentHashMap 是学习分段锁的最好实践
锁优化
	减少锁持有时间
	只用在有线程安全要求的程序上加锁
	减少锁粒度
	将大对象(这个对象可能会被很多线程访问),拆成小对象,大大增加并行度,降低锁竞争。
降低了锁的竞争,偏向锁,轻量级锁成功率才会提高。最最典型的减小锁粒度的案例就是ConcurrentHashMap。
    
锁分离
	最常见的锁分离就是读写锁ReadWriteLock,根据功能进行分离成读锁和写锁,这样读读不互斥,读写互斥,写写互斥,即保证了线程安全,
又提高了性能,具体也请查看[高并发Java五]JDK 并发包1。读写分离思想可以延伸,只要操作互不影响,锁就可以分离。
比如LinkedBlockingQueue 从头部取出,从尾部放数据
    
锁粗化
	通常情况下,为了保证多线程间的有效并发,会要求每个线程持有锁的时间尽量短,即在使用完公共资源后,应该立即释放锁。
但是,凡事都有一个度,如果对同一个锁不停的进行请求、同步和释放,其本身也会消耗系统宝贵的资源,反而不利于性能的优化 。
    	
锁消除
	锁消除是在编译器级别的事情。在即时编译器时,如果发现不可能被共享的对象,则可以消除这些对象的锁操作,多数是因为程序员编码不规范引起。
参考:https://www.jianshu.com/p/39628e1180a9

线程的基本方法

技术分享图片

线程相关的基本方法有wait,notify,notifyAll,sleep,join,yield 等。
    
      1.线程等待(wait)
            调用该方法的线程进入WAITING 状态,只有等待另外线程的通知或被中断才会返回,需要注意的是调用wait()方法后,会释放对象的锁。
            因此,wait 方法一般用在同步方法或同步代码块中。

      2. 线程睡眠(sleep)
            sleep 导致当前线程休眠,与wait 方法不同的是sleep 不会释放当前占有的锁,sleep(long)会导致
            线程进入TIMED-WATING 状态,而wait()方法会导致当前线程进入WATING 状态

      3. 线程让步(yield)
            yield 会使当前线程让出CPU 执行时间片,与其他线程一起重新竞争CPU 时间片。一般情况下,优先级高的线程有更大的可能性成功竞争得到CPU 时间片,
            但这又不是绝对的,有的操作系统对线程优先级并不敏感。

      4. 线程中断(interrupt)
            中断一个线程,其本意是给这个线程一个通知信号,会影响这个线程内部的一个中断标识位。这个线程本身并不会因此而改变状态(如阻塞,终止等)。

            1. 调用interrupt()方法并不会中断一个正在运行的线程。也就是说处于Running 状态的线程并不会因为被中断而被终止,仅仅改变了内部维护的中断标识位而已。

            2. 若调用sleep()而使线程处于TIMED-WATING 状态,这时调用interrupt()方法,会抛出InterruptedException,从而使线程提前结束TIMED-WATING 状态。

            3. 许多声明抛出InterruptedException 的方法(如Thread.sleep(long mills 方法)),抛出异常前,都会清除中断标识位,
                  所以抛出异常后,调用isInterrupted()方法将会返回false。

            4. 中断状态是线程固有的一个标识位,可以通过此标识位安全的终止线程。比如,你想终止一个线程thread 的时候,可以调用thread.interrupt()方法,
                  在线程的run 方法内部可以根据thread.isInterrupted()的值来优雅的终止线程。

      5. Join 等待其他线程终止
            join() 方法,等待其他线程终止,在当前线程中调用一个线程的 join() 方法,则当前线程转为阻塞状态,
            回到另一个线程结束,当前线程再由阻塞状态变为就绪状态,等待 cpu 的宠幸。

      6. 为什么要用 join()方法?
            很多情况下,主线程生成并启动了子线程,需要用到子线程返回的结果,也就是需要主线程需要在子线程结束后再结束,这时候就要用到 join() 方法。

            System.out.println(Thread.currentThread().getName() + "线程运行开始!");
            Thread6 thread1 = new Thread6();
            thread1.setName("线程B");
            thread1.join();
            System.out.println("这时thread1 执行完毕之后才能执行主线程");

      7. 线程唤醒(notify)
            Object 类中的 notify() 方法,唤醒在此对象监视器上等待的单个线程,如果所有线程都在此对象上等待,则会选择唤醒其中一个线程,
            选择是任意的,并在对实现做出决定时发生,线程通过调用其中一个 wait() 方法,在对象的监视器上等待,直到当前的线程放弃此对象上的锁定,
            才能继续执行被唤醒的线程,被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争。类似的方法还有 notifyAll() ,
            唤醒再次监视器上等待的所有线程。

      8. 其他方式
            1. sleep():强迫一个线程睡眠N毫秒。
            2. isAlive(): 判断一个线程是否存活。
            3. join(): 等待线程终止。
            4. activeCount(): 程序中活跃的线程数。
            5. enumerate(): 枚举程序中的线程。
            6. currentThread(): 得到当前线程。
            7. isDaemon(): 一个线程是否为守护线程。
            8. setDaemon(): 设置一个线程为守护线程。(用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束)
            9. setName(): 为线程设置一个名称。
            10. wait(): 强迫一个线程等待。
            11. notify(): 通知一个线程继续运行。
            12. setPriority(): 设置一个线程的优先级。
            13. getPriority()::获得一个线程的优先级。
    
    查看JAVA核心面试知识整理-最全@www.java1234.com.pdf

5、JAVA基础

CountDownLantch

CyclicBarrier

JAVA 异常分类及处理

概念
    如果某个方法不能按照正常的途径完成任务,就可以通过另一种路径退出方法。在这种情况下会抛出一个封装了错误信息的对象。
此时,这个方法会立刻退出同时不返回任何值。另外,调用这个方法的其他代码也无法继续执行,异常处理机制会将代码执行交给异常处理器。

技术分享图片

异常分类

异常分类
    Throwable 是 Java 语言中所有错误或异常的超类。下一层分为Error 和Exception

    1. Error 类是指java 运行时系统的内部错误和资源耗尽错误。应用程序不会抛出该类对象。如果
出现了这样的错误,除了告知用户,剩下的就是尽力使程序安全的终止。

Exception(RuntimeException、CheckedException)

    2. Exception 又有两个分支, 一个是运行时异常RuntimeException , 一个是CheckedException。
	RuntimeException 如: NullPointerException 、ClassCastException ;

        一个是检查异常CheckedException,如I/O 错误导致的IOException、SQLException。 
                  RuntimeException 是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类。 
                  如果出现RuntimeException,那么一定是程序员的错误.

    	运行时异常: 都是RuntimeException类及其子类异常:
                        IndexOutOfBoundsException 索引越界异常
			ArithmeticException:数学计算异常
			NullPointerException:空指针异常
			ArrayOutOfBoundsException:数组索引越界异常
			ClassNotFoundException:类文件未找到异常
			ClassCastException:造型异常(类型转换异常)

    检查异常CheckedException:一般是外部错误,这种异常都发生在编译阶段,Java 编译器会强制程序去捕获此类异常,
即会出现要求你把这段可能出现异常的程序进行try catch,该类异常一般包括几个方面:
	1. 试图在文件尾部读取数据
	2. 试图打开一个错误格式的URL
	3. 试图根据给定的字符串查找class 对象,而这个字符串表示的类并不存在
异常的处理方式
    遇到问题不进行具体处理,而是继续抛给调用者(throw,throws)抛出异常有三种形式,一是throw,一个throws,还有一种系统自动抛异常。
public static void main(String[] args) {
	String s = "abc";
	if(s.equals("abc")) {
		throw new NumberFormatException();
	} else {
		System.out.println(s);
	}
}
int div(int a,int b) throws Exception{
	return a/b;
}
try catch 捕获异常针对性处理方式

Throw和throws的区别

位置不同
	1. throws 用在函数上,后面跟的是异常类,可以跟多个;而throw 用在函数内,后面跟的是异常对象。
功能不同
	2. throws 用来声明异常,让调用者只知道该功能可能出现的问题,可以给出预先的处理方式;throw 抛出具体的问题对象,
            执行到throw,功能就已经结束了,跳转到调用者,并将具体的问题对象抛给调用者。也就是说throw 语句独立存在时,下面不要定义其他语句,因为执行不到。
	3. throws 表示出现异常的一种可能性,并不一定会发生这些异常;throw 则是抛出了异常,执行throw 则一定抛出了某种异常对象。
	4. 两者都是消极处理异常的方式,只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。

JAVA反射

1. 动态语言
	动态语言,是指程序在运行时可以改变其结构:新的函数可以引进,已有的函数可以被删除等结构上的变化。
比如常见的JavaScript 就是动态语言,除此之外Ruby,Python 等也属于动态语言,而C、C++则不属于动态语言。从反射角度说JAVA 属于半动态语言。

2. 反射机制概念 (运行状态中知道类所有的属性和方法)
	在Java 中的反射机制是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;并且对于任意一个对象,
都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象方法的功能成为Java 语言的反射机制。

3. 反射的应用场合
	编译时类型和运行时类型
	      在Java 程序中许多对象在运行是都会出现两种类型:编译时类型和运行时类型。 
              编译时的类型由声明对象时实用的类型来决定,运行时的类型由实际赋值给对象的类型决定 。如:
	            Person p=new Student();
                    其中编译时类型为Person,运行时类型为Student。

	编译时类型无法获取具体方法
	      程序在运行时还可能接收到外部传入的对象,该对象的编译时类型为Object,但是程序有需要调用该对象的运行时类型的方法。
为了解决这些问题,程序需要在运行时发现对象和类的真实信息。然而,如果编译时根本无法预知该对象和类属于哪些类,
程序只能依靠运行时信息来发现该对象和类的真实信息,此时就必须使用到反射了。

4.Java 反射API
	反射 API用来生成JVM中的类、接口或者对象的信息。
	1. Class 类:反射的核心类,可以获取类的属性,方法等信息。
	2. Field 类:Java.lang.reflect 包中的类,表示类的成员变量,可以用来获取和设置类之中的属性值。
	3. Method 类: Java.lang.reflec 包中的类,表示类的方法,它可以用来获取类中的方法信息或者执行方法。
	4. Constructor 类: Java.lang.reflec 包中的类,表示类的构造方法。

5. 反射使用步骤(获取Class 对象、调用对象方法)
	1. 获取想要操作的类的Class 对象,他是反射的核心,通过Class 对象我们可以任意调用类的方法。
	2. 调用Class 类中的方法,既就是反射的使用阶段。
	3. 使用反射API 来操作这些信息。

6. 获取Class 对象的3 种方法
	调用某个对象的getClass()方法
		Person p=new Person();
		Class clazz=p.getClass();
	调用某个类的 class属性类型获取该类对应的Class对象
		Class clazz=Person.class;
	使用Class类中的forName()静态方法(最安全/性能最好)
		Class clazz=Class.forName("类的全路径"); (最常用)
当我们获得了想要操作的类的Class 对象后,可以通过Class 类中的方法获取并查看该类中的方法和属性。
	//获取Person 类的Class 对象
	Class clazz=Class.forName("reflection.Person");

	//获取Person 类的所有方法信息
	Method[] method=clazz.getDeclaredMethods();
	for(Method m:method){
		System.out.println(m.toString());
	}
	//获取Person 类的所有成员属性信息
	Field[] field=clazz.getDeclaredFields();
	for(Field f:field){
		System.out.println(f.toString());
	}
	//获取Person 类的所有构造方法信息
	Constructor[] constructor=clazz.getDeclaredConstructors();
	for(Constructor c:constructor){
		System.out.println(c.toString());
	}

7. 创建对象的两种方法
	Class对象的 newInstance()
		1. 使用Class 对象的newInstance()方法来创建该Class 对象对应类的实例,但是这种方法要求该Class 对象对应的类有默认的空构造器。
	                调用Constructor对象的 newInstance()
		2. 先使用Class 对象获取指定的Constructor 对象,再调用Constructor 对象的newInstance()方法来创建 Class 对象对应类的实例,
                        通过这种方法可以选定构造方法创建实例。

	//获取Person 类的Class 对象
	Class clazz=Class.forName("reflection.Person");

	//使用.newInstane 方法创建对象
	Person p=(Person) clazz.newInstance();

	//获取构造方法并创建对象
	Constructor c=clazz.getDeclaredConstructor(String.class,String.class,int.class);

	//创建对象并设置属性
	Person p1=(Person) c.newInstance("李四","男",20);

技术分享图片

JAVA注解

1、概念
	Annotation(注解)是Java 提供的一种对元程序中元素关联信息和元数据(metadata)的途径和方法。
Annatation(注解)是一个接口,程序可以通过反射来获取指定程序中元素的Annotation对象,然后通过该Annotation 对象来获取注解中的元数据信息。
    
2、4种标准元注解
	元注解的作用是负责注解其他注解。 Java5.0 定义了4 个标准的meta-annotation 类型,它们被用来提供对其它 annotation 类型作说明。

	1.@Target 修饰的对象范围
	      @Target 说明了Annotation 所修饰的对象范围:
    	            Annotation 可被用于 packages、types(类、接口、枚举、Annotation 类型)、类型成员(方法、构造方法、成员变量、枚举值)、
                    方法参数和本地变量(如循环变量、catch 参数)。在Annotation 类型的声明中使用了target 可更加明晰其修饰的目标
    
	2.@Retention 定义 被保留的时间长短
		Retention 定义了该Annotation 被保留的时间长短:表示需要在什么级别保存注解信息,用于描述注解的生命周期
               (即:被描述的注解在什么范围内有效),取值(RetentionPoicy)由:
		      ?? SOURCE:在源文件中有效(即源文件保留)
		      ?? CLASS:在class 文件中有效(即class 保留)
		      ?? RUNTIME:在运行时有效(即运行时保留)
            
	3.@Documented 描述-javadoc
	      @ Documented 用于描述其它类型的annotation 应该被作为被标注的程序成员的公共API,因此可以被例如javadoc 此类的工具文档化。
            
	4.@Inherited 阐述了某个被标注的类型是被继承的
	      @Inherited 元注解是一个标记注解,@Inherited 阐述了某个被标注的类型是被继承的。
              如果一个使用了@Inherited 修饰的annotation 类型被用于一个class,则这个annotation 将被用于该class 的子类。

3. 注解处理器
	如果没有用来读取注解的方法和工作,那么注解也就不会比注释更有用处了。使用注解的过程中,很重要的一部分就是创建于使用注解处理器。
        Java SE5 扩展了反射机制的API,以帮助程序员快速的构造自定义注解处理器。下面实现一个注解处理器。

/1:*** 定义注解*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
	/**供应商编号*/
	public int id() default -1;
	/*** 供应商名称*/
	public String name() default "";
	/** * 供应商地址*/
	public String address() default "";
}

//2:注解使用
public class Apple {
	@FruitProvider(id = 1, name = "陕西红富士集团", address = "陕西省西安市延安路")
	private String appleProvider;
	public void setAppleProvider(String appleProvider) {
		this.appleProvider = appleProvider;
	}
	public String getAppleProvider() {
		return appleProvider;
	}
}

/3:*********** 注解处理器 ***************/
public class FruitInfoUtil {
	public static void getFruitInfo(Class<?> clazz) {
		String strFruitProvicer = "供应商信息:";
		Field[] fields = clazz.getDeclaredFields();//通过反射获取处理注解
		for (Field field : fields) {
			if (field.isAnnotationPresent(FruitProvider.class)) {
				FruitProvider fruitProvider = (FruitProvider)field.getAnnotation(FruitProvider.class);
				//注解信息的处理地方
				strFruitProvicer = " 供应商编号:" + fruitProvider.id() + " 供应商名称:"
				+ fruitProvider.name() + " 供应商地址:"+ fruitProvider.address();
				System.out.println(strFruitProvicer);
			}
		}
	}
}

public class FruitRun {
	public static void main(String[] args) {
		FruitInfoUtil.getFruitInfo(Apple.class);
		/***********输出结果***************/
		// 供应商编号:1 供应商名称:陕西红富士集团 供应商地址:陕西省西安市延
	}
}

技术分享图片

JAVA内部类

	Java 类中不仅可以定义变量和方法,还可以定义类,这样定义在类内部的类就被称为内部类。根据定义的方式不同,内部类分为静态内部类,成员内部类,局部内部类,匿名内部类四种。

1.静态内部类
	定义在类内部的静态类,就是静态内部类。
public class Out {
	private static int a;
	private int b;
	public static class Inner {
		public void print() {
			System.out.println(a);
		}
	}
}
	1. 静态内部类可以访问外部类所有的静态变量和方法,即使是private 的也一样。
	2. 静态内部类和一般类一致,可以定义静态变量、方法,构造方法等。
	3. 其它类使用静态内部类需要使用“外部类.静态内部类”方式,如下所示:
        Out.Inner inner = new Out.Inner();inner.print();
	4. Java集合类HashMap内部就有一个静态内部类Entry。Entry是HashMap存放元素的抽象,HashMap 内部维护Entry 数组用了存放元素,
但是Entry 对使用者是透明的。像这种和外部类关系密切的,且不依赖外部类实例的,都可以使用静态内部类。

2.成员内部类
	定义在类内部的非静态类,就是成员内部类。成员内部类不能定义静态方法和变量(final 修饰的除外)。
这是因为成员内部类是非静态的,类初始化的时候先初始化静态成员,如果允许成员内部类定义静态变量,那么成员内部类的静态变量初始化顺序是有歧义的。

public class Out {
	private static int a;
	private int b;
	public class Inner {
		public void print() {
			System.out.println(a);
			System.out.println(b);
		}
	}
}

3.局部内部类(定义在方法中的类)
	定义在方法中的类,就是局部类。如果一个类只在某个方法中使用,则可以考虑使用局部类。
public class Out {
	private static int a;
	private int b;
	public void test(final int c) {
		final int d = 1;
		class Inner {
			public void print() {
				System.out.println(c);
			}
		}
	}
}

4. 匿名内部类(要继承一个父类或者实现一个接口、直接使用new来生成一个对象的引用)
	匿名内部类我们必须要继承一个父类或者实现一个接口,当然也仅能只继承一个父类或者实现一个接口。
        同时它也是没有class 关键字,这是因为匿名内部类是直接使用new 来生成一个对象的引用。

public abstract class Bird {
	private String name;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public abstract int fly();
}

public class Test {
	public void test(Bird bird){
		System.out.println(bird.getName() + "能够飞 " + bird.fly() + "米");
	}
	public static void main(String[] args) {
		Test test = new Test();
		test.test(new Bird() {
			public int fly() {
				return 10000;
			}
			public String getName() {
				return "大雁";
			}
		});
	}
}

JAVA泛型

	泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
        泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
            比如我们要写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,我们就可以使用Java 泛型。

1. 泛型方法(<E>)
	你可以写一个泛型方法,该方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。
	// 泛型方法 printArray
	public static < E > void printArray( E[] inputArray ){
		for ( E element : inputArray ){
			System.out.printf( "%s ", element );
		}
	}

1. <? extends T>表示该通配符所代表的类型是T 类型的子类。
2. <? super T>表示该通配符所代表的类型是T 类型的父类。

2. 泛型类<T>
	泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。
        和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。
        一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。

public class Box<T> {
	private T t;
	public void add(T t) {
		this.t = t;
	}
	public T get() {
		return t;
	}
}

3. 类型通配符?
	类型通配符一般是使用? 代替具体的类型参数。例如 List<?> 在逻辑上是List<String>,List<Integer> 等所有List<具体类型实参>的父类。

4. 类型擦除
	Java 中的泛型基本上都是在编译器这个层次来实现的。在生成的Java 字节代码中是不包含泛型中的类型信息的。
使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉。这个过程就称为类型擦除。如在代码中定义的List<Object>和List<String>等类型,
在编译之后都会变成List。JVM看到的只是List,而由泛型附加的类型信息对JVM来说是不可见的。
类型擦除的基本过程也比较简单,首先是找到用来替换类型参数的具体类。这个具体类一般
是Object。如果指定了类型参数的上界的话,则使用这个上界。把代码中的类型参数都替换成具体的类。

 泛型的使用
* 1.jdk 5.0新增的特性

* 2.在集合中使用泛型:
*      总结:1.集合接口或集合类在jdk5.0时都修改为带泛型的结构。
*          2.在实例化集合类时,可以指明具体的泛型类型
*          3.指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型。
*          比如:add(E e) --->实例化以后:add(Integer e)
*          4.注意点:泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,必须是包装类
*          5.如果实例化时,没有指明泛型的类型。默认类型为java.lang.Object类型。

* 3.如何自定义泛型结构:泛型类、泛型接口;泛型方法

* 泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系。
			泛型方法所属的类是不是泛型类都没有关系。
           可以声明为静态的。原因:泛型参数是在调用方法时确定的。并非在实例化类时确定。

JAVA序列化(创建可复用的Java对象)

	保存(持久化)对象及其状态到内存或者磁盘
	Java 平台允许我们在内存中创建可复用的Java 对象,但一般情况下,只有当JVM 处于运行时,这些对象才可能存在,
        即,这些对象的生命周期不会比JVM 的生命周期更长。但在现实应用中,就可能要求在JVM停止运行之后能够保存(持久化)指定的对象,并在将来重新读取被保存的对象。
        
	Java 对象序列化就能够帮助我们实现该功能。
	序列化对象以字节数组保持-静态成员不保存
	使用Java 对象序列化,在保存对象时,会把其状态保存为一组字节,在未来,再将这些字节组装成对象。
        必须注意地是,对象序列化保存的是对象的”状态”,即它的成员变量。由此可知,对象序列化不会关注类中的静态变量。

    序列化用户远程对象传输
	除了在持久化对象时会用到对象序列化之外,当使用RMI(远程方法调用),或在网络中传递对象时,都会用到对象序列化。
        Java 序列化API 为处理对象序列化提供了一个标准机制,该API 简单易用。
        
Serializable 实现序列化
	在Java 中,只要一个类实现了java.io.Serializable 接口,那么它就可以被序列化。
		ObjectOutputStream 和 ObjectInputStream 对对象进行序列化及反序列化
		通过ObjectOutputStream 和ObjectInputStream 对对象进行序列化及反序列化。
	writeObject 和 readObject 自定义序列化策略
		在类中增加writeObject 和 readObject 方法可以实现自定义序列化策略。
        
序列化ID
	虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID)

序列化并不保存静态变量
序列化子父类说明
要想将父类对象也序列化,就需要让父类也实现Serializable 接口。
        
	Transient 关键字阻止该变量被序列化到文件中
	1. 在变量声明前加上Transient 关键字,可以阻止该变量被序列化到文件中,
            在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。
	2. 服务器端给客户端发送序列化对象数据,对象中有一些数据是敏感的,比如密码字符串等,希望对该密码字段在序列化时,
            进行加密,而客户端如果拥有解密的密钥,只有在客户端进行反序列化时,才可以对密码进行读取,这样可以一定程度保证序列化对象的数据安全。

java复制

	将一个对象的引用复制给另外一个对象,一共有三种方式。
            第一种方式是直接赋值,第二种方式是浅拷贝,第三种是深拷贝。所以大家知道了哈,这三种概念实际上都是为了拷贝对象。

1. 直接赋值复制
	直接赋值。在Java 中,A a1 = a2,我们需要理解的是这实际上复制的是引用,也就是说a1 和a2 指向的是同一个对象。
            因此,当a1 变化的时候,a2 里面的成员变量也会跟着变化。

2. 浅复制(复制引用但不复制引用的对象)
	创建一个新对象,然后将当前对象的非静态字段复制到该新对象,如果字段是值类型的,那么对该字段执行复制;
              如果该字段是引用类型的话,则复制引用但不复制引用的对象。因此,原始对象及其副本引用同一个对象。

class Resume implements Cloneable{
	public Object clone() {
		try {
			return (Resume)super.clone();
		} catch (Exception e) {
			e.printStackTrace();
		return null;
		}
	}
}

3. 深复制(复制对象和其应用对象)
	深拷贝不仅复制对象本身,而且复制对象包含的引用指向的所有对象。
class Student implements Cloneable {
	String name;
	int age;
	Professor p;
	Student(String name, int age, Professor p) {
		this.name = name;
		this.age = age;
		this.p = p;
	}
	public Object clone() {
		Student o = null;
		try {
			o = (Student) super.clone();
		} catch (CloneNotSupportedException e) {
			System.out.println(e.toString());
		}
		o.p = (Professor) p.clone();
		return o;
	}
}

4. 序列化(深clone 一中实现)
	在Java 语言里深复制一个对象,常常可以先使对象实现Serializable 接口,
            然后把对象(实际上只是对象的一个拷贝)写到一个流里,再从流里读出来,便可以重建对象。

java

 * 方法的参数传递机制:
 * 1、形参是基本数据类型
 * 		传递数据值
 * 2、实参是引用数据类型
 * 		传递地址值
 * 		特殊的类型:String、包装类等对象不可变性
     
* 考点
 * 1、就近原则
 * 2、变量的分类
 * 		成员变量:类变量、实例变量
 * 		局部变量
 * 3、非静态代码块的执行:每次创建实例对象都会执行
 * 4、方法的调用规则:调用一次执行一次
 * 
 * 局部变量与成员变量的区别
 * 1、声明的位置
 * 		局部变量:方法体{}中,形参,代码块{}中
 * 		成员变量:类中方法外	
 * 			类变量:有static修饰
 * 			实例变量:没有static修饰
 * 2、修饰符
 * 		局部变量:final
 * 		成员变量:public、protected、private、final、static、volatile、transient
 * 3、值存储的位置
 * 		局部变量:栈
 * 		实例变量:堆
 * 		类变量:方法区
 * 4、作用域
 * 		局部变量:从声明处开始,到所属的}结束
 * 		实例变量:在当前类中"this."(有时this.可以缺省),在其他类中"对象名."访问
 * 		类变量:在当前类中"类名."(有时类名.可以省略),在其他类中"类名."或"对象名."访问
 * 5、生命周期
 * 		局部变量:每一个线程,每一次调用执行都是新的生命周期
 * 		实例变量:随着对象的创建而初始化,随着对象的被回收而消亡,每一个对象的实例变量是独立的
 * 		类变量:随着类的初始化而初始化,随着类的卸载而消亡,该类的所有对象的类变量是共享的。     

String类

valueOf(); lastIndexOf();join(,);isEmpty();indexOf()
toString();hashCode();getChars();getBytes();equals()
trim();contains();concat();cahrAt()
substring();
split();
replace();
length();

java单例模式

/**
 * 饿汉式:直接创建对象,不存在线程安全问题
 * 1.直接实例化饿汉式(简洁直观)
 * @author Administrator
 *
 */
public class Singleton1 {
	public static final Singleton1 INSTANCE = new Singleton1();
	private Singleton1(){
		
	}
}

/*
 * 1、构造器私有化
 * 2、用一个静态变量保存这个唯一的实例
 * 3、提供一个静态方法,获取这个实例对象
 */
public class Singleton4 {

	private static Singleton4 instance;
	private Singleton4(){
		
	}
	public static Singleton4 getInstance(){
		if(instance == null){
			synchronized (Singleton4.class) {
				if(instance ==null){
					
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					instance = new Singleton4();
				}
			}
		}
		return instance;
	}
}

6、Spring

推荐视频地址:https://www.bilibili.com/video/BV1nx411g7ja?from=search&seid=15231419141912161282
技术分享图片
技术分享图片
技术分享图片
技术分享图片
技术分享图片
技术分享图片
技术分享图片
技术分享图片

整理Spring IOC:

? IoC

<bean id="" class=""></bean>

? DI

<bean id="" class="">
	<property name="" ref=""></property>
</bean>

? xml配置
? 实例化方式

3种实例化方式:
    1、默认构造
    	<bean id="" class="">
    2、静态工厂
        <bean id="" class="" factory-method="静态方法"></bean>
    3、实例工厂
        <bean id = "工厂实例id" class=""></bean>
        <bean id ="" class="" factory-bean="工厂实例id" factory-method="普通方法"></bean>

BeanFactory 和 FactoryBean 对比?

BeanFactory:工厂,用于生成任意bean。
FactoryBean:特殊bean,用于生成另一个特定的bean。例如:ProxyFactoryBean ,此工厂bean用于生产代理。<bean id="" class="....ProxyFactoryBean"> 获得代理对象实例。AOP使用
    

? 作用域

<bean id="" class="" scope=""></bean>
单例:singleton:默认值。当IOC容器-创建就会创建bean的实例,而且是单例的,每次得到的都是同一个
多例:prototype:原型的。当IOC容器-创建不再实例化该bean,每次调用getBean方法时再实例化该bean
request:每次请求实例化一个bean
session:在一次会话中共享一个bean

? 生命周期

<bean id="" class="" init-method="初始化方法名称" destroy-method="销毁的方法名称"></bean>

后处理bean:
BeanPostProcessor
	前方法:postProcessBeforeInitialization
	后方法:postProcessAfterInitialization
将后处理的实现类注册给spring:<bean class="MyBeanPostProcessor"></bean>

? 属性注入:构造方法、setter、、集合

?	依赖注入方式:
		手动注入:
			基于xml装配:构造方法:<constructor-arg></constructor-arg>
								name value/ref; index type;
						setter方法:
								<bean>
									<property name="" value="">
                                        	value=""等同于<value>1234</value>
                                    </property>
                                    <property name="" ref="另一个bean">
                                    		ref="另一个bean"等同于<ref bean="id"></ref>
                                    </property>
								</bean>
						集合:
								<bean id="" class="">
									<property name=""></property>
								</bean>
			基于注解装配:
				1.@Component取代<bean class=""> 
                  @Component("id") 取代 <bean id="" class="">
               	2.	web层:@Control  取代<bean class="">
                	service层:@Service 
                	dao层:@Repository
                3. 普通值:@Value("")
				   引用值:
                    方式1:按照【类型】注入
                        @Autowired
                    方式2:按照【名称】注入1
                        @Autowired
                        @Qualifier("名称")
                    方式3:按照【名称】注入2
                        @Resource("名称")
                 4.生命周期
                        初始化:@PostConstruct
                        销毁:@PreDestroy
                 5.作用域
                    @Scope("prototype") 多例
           	<!-- 组件扫描,扫描含有注解的类 -->
                    <context:component-scan base-package=""></context:component-scan>
		自动注入:
                @Autowired
				默认不生效。为了生效,需要在xml配置:<context:annotation-config>
    总结:
    注解1:<context:component-scan base-package=" ">
    注解2:<context:annotation-config>
		1.一般情况两个注解不一起使用。
		2.“注解1”扫描含有注解(@Component 等)类,注入注解自动生效。
		“注解2”只在xml和注解(注入)混合使用时,使注入注解生效。

集合的注入都是给<property>添加子标签
			数组:<array>
			List:<list>
			Set:<set>
			Map:<map> ,map存放k/v 键值对,使用<entry>描述
			Properties:<props>  <prop key=""></prop>  【】
			
		普通数据:<value>
		引用数据:<ref>
								<bean id="" class="">
									<property name="">
                                    	<array>
                                        	<value>1</value>
                                            <value>2</value>
                                        </array>
                                    </property>
                                    <property name="mapData">
                                        <map>
                                            <entry key="jack" value="杰克"></entry>
                                            <entry>
                                                <key><value>rose</value></key>
                                                <value>肉丝</value>
                                            </entry>
                                        </map>
									</property>

								</bean>
    
    p命名空间:简化<property>   <bean p:属性名="普通值"  p:属性名-ref="引用值">  注意声明命名空间
  	SpEL:<property name="" value="#{表达式}">
    	 #{123}  #{‘abc‘}
     	 #{beanId.propName?.methodName()}
    	 #{T(类).静态方法|字段}

整理Spring AOP:

1、AOP实现原理:

?	aop底层将采用代理机制进行实现。
?	接口 + 实现类:spring采用 jdk 的动态代理Proxy。 有接口
?	实现类:spring 采用 cglib字节码增强。			无接口

2、AOP术语:

1. target目标类:需要被代理的类。例如:UserService
2. Joinpoint连接点:所谓连接点是指那些可能被拦截到的方法。例如:所有的方法
3. PointCut切入点:已经被增强的连接点。例如:addUser()
4. advice通知/增强,增强代码。例如:after、before
5. Weaving织入:是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程.
6. proxy代理类
7. Aspect切面:是切入点pointcut和通知advice的结合
一个线是一个特殊的面。
一个切入点和一个通知,组成成一个特殊的面。

3、3种编程方式:

手动方式(jdk动态代理和CGLIB字节码增强)

jdk动态代理:Proxy.newProxyInstance
    参数1:loader ,类加载器
    参数2:Class[] interfaces 代理类需要实现的所有接口
    参数3:InvocationHandler  处理类,接口,必须进行实现类,一般采用匿名内部
    	提供 invoke 方法,代理类的每一个方法执行时,都将调用一次invoke
    	参数31:Object proxy :代理对象
    	参数32:Method method : 代理对象当前执行的方法的描述对象(反射)
        参数33:Object[] args :方法实际参数
CGLIB字节码增强
    核心:hibernate-distribution-3.6.10.Final\lib\bytecode\cglib\cglib-2.2.jar
		依赖:struts-2.3.15.3\apps\struts2-blank\WEB-INF\lib\asm-3.3.jar
	spring-core..jar 已经整合以上两个内容
	代理类 ,采用cglib,底层创建目标类的子类
    1 核心类:Enhancer enhancer = new Enhancer();
	2 确定父类 enhancer.setSuperclass(userService.getClass());

	3 设置回调函数 ,enhancer.setCallback(new MethodInterceptor()
        MethodInterceptor接口 等效 jdk InvocationHandler接口
        intercept() 等效 jdk  invoke()
		 参数1、参数2、参数3:以invoke一样 ;参数4:methodProxy 方法的代理
			执行代理类的父类methodProxy.invokeSuper(proxy, args)
     4 enhance.creat()。
jdk动态代理详细代码:
public class MyBeanFactory {
	
	public static UserService createService(){
		//1 目标类
		final UserService userService = new UserServiceImpl();
		//2切面类
		final MyAspect myAspect = new MyAspect();
		/* 3 代理类:将目标类(切入点)和 切面类(通知) 结合 --> 切面
		 * 	Proxy.newProxyInstance
		 * 		参数1:loader ,类加载器,动态代理类 运行时创建,任何类都需要类加载器将其加载到内存。
		 * 			一般情况:当前类.class.getClassLoader();
		 * 					目标类实例.getClass().get...
		 * 		参数2:Class[] interfaces 代理类需要实现的所有接口
		 * 			方式1:目标类实例.getClass().getInterfaces()  ;注意:只能获得自己接口,不能获得父元素接口
		 * 			方式2:new Class[]{UserService.class}   
		 * 			例如:jdbc 驱动  --> DriverManager  获得接口 Connection
		 * 		参数3:InvocationHandler  处理类,接口,必须进行实现类,一般采用匿名内部
		 * 			提供 invoke 方法,代理类的每一个方法执行时,都将调用一次invoke
		 * 				参数31:Object proxy :代理对象
		 * 				参数32:Method method : 代理对象当前执行的方法的描述对象(反射)
		 * 					执行方法名:method.getName()
		 * 					执行方法:method.invoke(对象,实际参数)
		 * 				参数33:Object[] args :方法实际参数
		 * 
		 */
		UserService proxService = (UserService)Proxy.newProxyInstance(
								MyBeanFactory.class.getClassLoader(), 
								userService.getClass().getInterfaces(), 
								new InvocationHandler() {
									
									@Override
									public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
										
										//前执行
										myAspect.before();
										
										//执行目标类的方法
										Object obj = method.invoke(userService, args);
										
										//后执行
										myAspect.after();
										
										return obj;
									}
								});
		return proxService;
	}
}
CGLIB字节码增强详细代码
public class MyBeanFactory {
	
	public static UserServiceImpl createService(){
		//1 目标类
		final UserServiceImpl userService = new UserServiceImpl();
		//2切面类
		final MyAspect myAspect = new MyAspect();
		// 3.代理类 ,采用cglib,底层创建目标类的子类
		//3.1 核心类
		Enhancer enhancer = new Enhancer();
		//3.2 确定父类
		enhancer.setSuperclass(userService.getClass());

		/* 3.3 设置回调函数 , MethodInterceptor接口 等效 jdk InvocationHandler接口
		 * 	intercept() 等效 jdk  invoke()
		 * 		参数1、参数2、参数3:以invoke一样
		 * 		参数4:methodProxy 方法的代理
		 */
		enhancer.setCallback(new MethodInterceptor(){

			@Override
			public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
				
				//前
				myAspect.before();
				
				//执行目标类的方法
				Object obj = method.invoke(userService, args);
				// * 执行代理类的父类 ,执行目标类 (目标类和代理类 父子关系)
				methodProxy.invokeSuper(proxy, args);
				
				//后
				myAspect.after();
				
				return obj;
			}
		});
		//3.4 创建代理
		UserServiceImpl proxService = (UserServiceImpl) enhancer.create();
		
		return proxService;
	}
}

半自动(spring编写代理)

?	让spring 创建代理对象,从spring容器中手动的获取代理对象
	核心:4+1 和 AOP:AOP联盟(规范)、spring-aop (实现)
    切面类中确定通知 public class MyAspect implements MethodInterceptor环绕通知
	spring配置:ProxyFactoryBean 
        interfaces : 确定接口们 通过<array>可以设置多个值
        target :确定目标类
        interceptorNames : 通知 切面类的名称,类型String[],如果设置一个值 value=""
        optimize :强制使用cglib

![img](D:\Program Files\Typora\my_typora\image\java核心面试整理\clip_image001.png)

spring配置(半自动)

	目标类
public interface UserService {
	public void addUser();
	public void updateUser();
	public void deleteUser();
}
10.4.2	切面类
/**
 * 切面类中确定通知,需要实现不同接口,接口就是规范,从而就确定方法名称。
 * * 采用“环绕通知” MethodInterceptor
 */
public class MyAspect implements MethodInterceptor {
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {	
		System.out.println("前3");		
		//手动执行目标方法
		Object obj = mi.proceed();		
		System.out.println("后3");
		return obj;
	}
}

spring配置

<!-- 1 创建目标类 -->
	<bean id="userServiceId" class="com.itheima.b_factory_bean.UserServiceImpl"></bean>
	<!-- 2 创建切面类 -->
	<bean id="myAspectId" class="com.itheima.b_factory_bean.MyAspect"></bean>

	<!-- 3 创建代理类 
		* 使用工厂bean FactoryBean ,底层调用 getObject() 返回特殊bean
		* ProxyFactoryBean 用于创建代理工厂bean,生成特殊代理对象
			interfaces : 确定接口们
				通过<array>可以设置多个值
				只有一个值时,value=""
			target : 确定目标类
			interceptorNames : 通知 切面类的名称,类型String[],如果设置一个值 value=""
			optimize :强制使用cglib
				<property name="optimize" value="true"></property>
		底层机制
			如果目标类有接口,采用jdk动态代理
			如果没有接口,采用cglib 字节码增强
			如果声明 optimize = true ,无论是否有接口,都采用cglib
	-->
	<bean id="proxyServiceId" class="org.springframework.aop.framework.ProxyFactoryBean">
		<property name="interfaces" value="com.itheima.b_factory_bean.UserService"></property>
		<property name="target" ref="userServiceId"></property>
		<property name="interceptorNames" value="myAspectId"></property>
	</bean>

全自动(springAOP编程)【掌握】

![img](D:\Program Files\Typora\my_typora\image\java核心面试整理\clip_image002.jpg)

?	从spring容器获得目标类,如果配置aop,spring将自动生成代理。
?	要确定目标类,aspectj 切入点表达式,导入jar包
	spring-framework-3.0.2.RELEASE-dependencies\org.aspectj\com.springsource.org.aspectj.weaver\1.6.8.RELEASE

?	从spring容器获得目标类,如果配置aop,spring将自动生成代理。
    导入jar:com.springsource.org.aspectj.weaver\1.6.8.RELEASE
    	1 导入命名空间 xmlns:aop
        2 使用 <aop:config>进行配置 proxy-target-class="true" 声明时使用cglib代理
            		<aop:pointcut> 切入点 ,从目标对象获得具体方法 
                	<aop:advisor> 特殊的切面,只有一个通知 和 一个切入点
                        advice-ref 通知引用
						pointcut-ref 切入点引用
        3 切入点表达式
           execution(* com.itheima.c_spring_aop.*.*(..))
			选择方法 返回值任意   包         类名任意 方法名任意 参数任意

spring AOP编程(全自动)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       					   http://www.springframework.org/schema/beans/spring-beans.xsd
       					   http://www.springframework.org/schema/aop 
       					   http://www.springframework.org/schema/aop/spring-aop.xsd">
	<!-- 1 创建目标类 -->
	<bean id="userServiceId" class="com.itheima.c_spring_aop.UserServiceImpl"></bean>
	<!-- 2 创建切面类(通知) -->
	<bean id="myAspectId" class="com.itheima.c_spring_aop.MyAspect"></bean>
	<!-- 3 aop编程 
		3.1 导入命名空间
		3.2 使用 <aop:config>进行配置
				proxy-target-class="true" 声明时使用cglib代理
			<aop:pointcut> 切入点 ,从目标对象获得具体方法
			<aop:advisor> 特殊的切面,只有一个通知 和 一个切入点
				advice-ref 通知引用
				pointcut-ref 切入点引用
		3.3 切入点表达式
			execution(* com.itheima.c_spring_aop.*.*(..))
			选择方法         返回值任意   包             类名任意   方法名任意   参数任意
	-->
	<aop:config proxy-target-class="true">
		<aop:pointcut expression="execution(* com.itheima.c_spring_aop.*.*(..))" id="myPointCut"/>
		<aop:advisor advice-ref="myAspectId" pointcut-ref="myPointCut"/>
	</aop:config>
</beans>

4、AspectJ 一个基于Java语言的AOP框架

?	AspectJ是一个基于Java语言的AOP框架
?	Spring2.0以后新增了对AspectJ切点表达式支持
?	@AspectJ 是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面
新版本Spring框架,建议使用AspectJ方式来开发AOP
?	主要用途:自定义开发

11.2	切入点表达式【掌握】
1.execution()  用于描述方法 【掌握】
	语法:execution(修饰符  返回值  包.类.方法名(参数) throws异常)
		修饰符,一般省略
			public		公共方法
			*			任意
		返回值,不能省略
			void			返回没有值
			String		返回值字符串
			* 			任意
		包,[省略]
			com.itheima.crm			固定包
			com.itheima.crm.*.service	crm包下面子包任意 (例如:com.itheima.crm.staff.service)
			com.itheima.crm..			crm包下面的所有子包(含自己)
			com.itheima.crm.*.service..	crm包下面任意子包,固定目录service,service目录任意包
		类,[省略]
			UserServiceImpl			指定类
			*Impl					以Impl结尾
			User*					以User开头
			*						任意
		方法名,不能省略
			addUser					固定方法
			add*						以add开头
			*Do						以Do结尾
			*						任意
		(参数)
			()						无参
			(int)						一个整型
			(int ,int)					两个
			(..)						参数任意
		throws ,可省略,一般不写。

综合1
	execution(* com.itheima.crm.*.service..*.*(..))
综合2
	<aop:pointcut expression="execution(* com.itheima.*WithCommit.*(..)) || 
                          execution(* com.itheima.*Service.*(..))" id="myPointCut"/>
2.within:匹配包或子包中的方法(了解)
	within(com.itheima.aop..*)
3.this:匹配实现接口的代理对象中的方法(了解)
	this(com.itheima.aop.user.UserDAO)
4.target:匹配实现接口的目标对象中的方法(了解)
	target(com.itheima.aop.user.UserDAO)
5.args:匹配参数格式符合标准的方法(了解)
	args(int,int)
6.bean(id)  对指定的bean所有的方法(了解)
	bean(‘userServiceId‘)

 ?	导入4个jar:
		aop联盟规范
		spring aop 实现
		aspect 规范
		spring aspect 实现
	<aop:config>
		<aop:aspect ref="myAspectId">
			<aop:pointcut expression="" >
            <aop:before method="myBefore" pointcut= "expression()"/> 
            <aop:around method="myAround" pointcut-ref="myPointCut"/>
            <aop:after-returning method="" pointcut-ref="myPointCut" returning="ret" />          
	<context:component-scan base-package="">
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
        @Pointcut("execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))")
		@Around(value = "myPointCut()")
        @AfterReturning(value="myPointCut()" ,returning="ret")

![img](D:\Program Files\Typora\my_typora\image\java核心面试整理\00000001.png)

AOP联盟通知类型

	AOP联盟通知类型
?	AOP联盟为通知Advice定义了org.aopalliance.aop.Advice
?	Spring按照通知Advice在目标类方法的连接点位置,可以分为5类
?	前置通知 org.springframework.aop.MethodBeforeAdvice
?		在目标方法执行前实施增强
?	后置通知 org.springframework.aop.AfterReturningAdvice
?		在目标方法执行后实施增强
?	环绕通知 org.aopalliance.intercept.MethodInterceptor
?		在目标方法执行前后实施增强
?	异常抛出通知 org.springframework.aop.ThrowsAdvice
?		在方法抛出异常后实施增强
?	引介通知 org.springframework.aop.IntroductionInterceptor
?		在目标类中添加一些新的方法和属性

环绕通知,必须手动执行目标方法
try{
   //前置通知
   //执行目标方法
   //后置通知
} catch(){
   //抛出异常通知
}

基于xml

1.目标类:接口 + 实现
2.切面类:编写多个通知,采用aspectj 通知名称任意(方法名任意)
3.aop编程,将通知应用到目标类
	切面类
/**
 * 切面类,含有多个通知
 */
public class MyAspect {
	
	public void myBefore(JoinPoint joinPoint){
		System.out.println("前置通知 : " + joinPoint.getSignature().getName());
	}
	
	public void myAfterReturning(JoinPoint joinPoint,Object ret){
		System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret);
	}
	
	public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
		System.out.println("前");
		//手动执行目标方法
		Object obj = joinPoint.proceed();
		
		System.out.println("后");
		return obj;
	}
	
	public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
		System.out.println("抛出异常通知 : " + e.getMessage());
	}
	
	public void myAfter(JoinPoint joinPoint){
		System.out.println("最终通知");
	}

}
spring配置
<!-- 1 创建目标类 -->
	<bean id="userServiceId" class="com.itheima.d_aspect.a_xml.UserServiceImpl"></bean>
	<!-- 2 创建切面类(通知) -->
	<bean id="myAspectId" class="com.itheima.d_aspect.a_xml.MyAspect"></bean>
	<!-- 3 aop编程 
		<aop:aspect> 将切面类 声明“切面”,从而获得通知(方法)
			ref 切面类引用
		<aop:pointcut> 声明一个切入点,所有的通知都可以使用。
			expression 切入点表达式
			id 名称,用于其它通知引用
	-->
	<aop:config>
		<aop:aspect ref="myAspectId">
			<aop:pointcut expression="execution(* com.itheima.d_aspect.a_xml.UserServiceImpl.*(..))" id="myPointCut"/>
			
			<!-- 3.1 前置通知 
				<aop:before method="" pointcut="" pointcut-ref=""/>
					method : 通知,及方法名
					pointcut :切入点表达式,此表达式只能当前通知使用。
					pointcut-ref : 切入点引用,可以与其他通知共享切入点。
				通知方法格式:public void myBefore(JoinPoint joinPoint){
					参数1:org.aspectj.lang.JoinPoint  用于描述连接点(目标方法),获得目标方法名等
				例如:
			<aop:before method="myBefore" pointcut-ref="myPointCut"/>
			-->
			
			<!-- 3.2后置通知  ,目标方法后执行,获得返回值
				<aop:after-returning method="" pointcut-ref="" returning=""/>
					returning 通知方法第二个参数的名称
				通知方法格式:public void myAfterReturning(JoinPoint joinPoint,Object ret){
					参数1:连接点描述
					参数2:类型Object,参数名 returning="ret" 配置的
				例如:
			<aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret" />
			-->
			
			<!-- 3.3 环绕通知 
				<aop:around method="" pointcut-ref=""/>
				通知方法格式:public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
					返回值类型:Object
					方法名:任意
					参数:org.aspectj.lang.ProceedingJoinPoint
					抛出异常
				执行目标方法:Object obj = joinPoint.proceed();
				例如:
			<aop:around method="myAround" pointcut-ref="myPointCut"/>
			-->
			<!-- 3.4 抛出异常
				<aop:after-throwing method="" pointcut-ref="" throwing=""/>
					throwing :通知方法的第二个参数名称
				通知方法格式:public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
					参数1:连接点描述对象
					参数2:获得异常信息,类型Throwable ,参数名由throwing="e" 配置
				例如:
			<aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>
			-->
			<!-- 3.5 最终通知 -->			
			<aop:after method="myAfter" pointcut-ref="myPointCut"/>
			
		</aop:aspect>
	</aop:config>

基于注解

替换bean
    <!-- 1.扫描 注解类 -->
	<context:component-scan base-package="com.itheima.d_aspect.b_anno">				</context:component-scan>
    @Service("userServiceId") 
    public class UserServiceImpl implements UserService{}
	@Component
	@Aspect//声明切面
	public class MyAspect{}
替换aop
    <!-- 2.确定 aop注解生效 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
        
//切入点当前有效
	@Before("execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))")
	public void myBefore(JoinPoint joinPoint){
		System.out.println("前置通知 : " + joinPoint.getSignature().getName());
	}
//声明公共切入点
	@Pointcut("execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))")
	private void myPointCut(){
	}

	@AfterReturning(value="myPointCut()" ,returning="ret")
	public void myAfterReturning(JoinPoint joinPoint,Object ret){
		System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret);
	}
	@Around(value = "myPointCut()")
	public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
		System.out.println("前");
		//手动执行目标方法
		Object obj = joinPoint.proceed();
		System.out.println("后");
		return obj;
	}
	@AfterThrowing(value="execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))" ,throwing="e")
	public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
		System.out.println("抛出异常通知 : " + e.getMessage());
	}

切面类

/**
 * 切面类,含有多个通知
 */
@Component
@Aspect
public class MyAspect {
	
	//切入点当前有效
//	@Before("execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))")
	public void myBefore(JoinPoint joinPoint){
		System.out.println("前置通知 : " + joinPoint.getSignature().getName());
	}
	
	//声明公共切入点
	@Pointcut("execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))")
	private void myPointCut(){
	}
	
//	@AfterReturning(value="myPointCut()" ,returning="ret")
	public void myAfterReturning(JoinPoint joinPoint,Object ret){
		System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret);
	}
	
//	@Around(value = "myPointCut()")
	public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
		System.out.println("前");
		//手动执行目标方法
		Object obj = joinPoint.proceed();
		
		System.out.println("后");
		return obj;
	}
	
//	@AfterThrowing(value="execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))" ,throwing="e")
	public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
		System.out.println("抛出异常通知 : " + e.getMessage());
	}
	
	@After("myPointCut()")
	public void myAfter(JoinPoint joinPoint){
		System.out.println("最终通知");
	}

}

spring配置

<!-- 1.扫描 注解类 -->
	<context:component-scan base-package="com.itheima.d_aspect.b_anno">				</context:component-scan>
	
	<!-- 2.确定 aop注解生效 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

整理Spring 事务 (transaction)

导入jar包
?	核心:4+1
?	aop : 4 (aop联盟、spring aop、aspectj规范、spring aspect)
?	数据库:2  (jdbc/tx)
?	驱动:mysql oracle
?	连接池:c3p0  proxool  DBCP

事务的属性:

1、propagation:用来设置事务的传播行为
    事务的传播行为:一个方法运行在了一个开启了事务的方法中,当前方法时使用原来的事务还是开启一个新的事务
    -Propagation.REQUIRED:默认值,使用原来的事务。required
    -Propagation.REQUIRES_NEW:将原来的事务挂起,开启一个新的事务requires_new
2、isolation:用来设置事务的隔离级别
    -Isolation.REPEATABLE_READ:可重复读,MYSQL默认的隔离级别 repeatable_read
    -Isonlation.READ_COMMITTED:读已提交,ORACLE默认的隔离级别,开发时通常使用的隔离级别

手动管理事务

?	spring底层使用 TransactionTemplate 事务模板进行操作
    1.service 需要获得 TransactionTemplate 
	2.spring 配置模板,并注入给service
	3.模板需要注入事务管理器
	4.配置事务管理器:DataSourceTransactionManager ,需要注入DataSource

事务的四大特性是什么?

ACID(isolation,)
1.原子性: 整个事务当中所有的操作,要么全部成功,要么全部失败.
2.一致性: 在事务开始之前和事务结束之后,数据库的信息一定是正确的.
3.隔离性: 一个事务的成功或失败对于其他事务是没有任何影响的,2个事务之间是互相独?立的.
4.持久性: 在事务完成以后,这个事务所对数据库的操作会永久保存在数据库当中,不会被回滚.

四种隔离级别?

1.读未提交(read uncommitted):也就是脏读,事务可以读取其他事务未提交的数据
2.读已提交(read committed):一个事务读取到另一个事务已提交的数据(解决了脏读问题.oracle默认)
3.可重复读(repeatable read):在一个事务中读取到的数据始终保持一致,无论另一个事务是否提交
       						(解决脏读、不可重复读,mysql默认)
4.可串行化(serializable):同时只能执行一个事务,相当于事务中的单线程
default

spring事务的传播特性

多个事务存在是怎么处理的策略
propagation
	required需要:如果存在一个事务,则支持当前事务,如果没有则开启。
	supports支持:如果存在一个事务,支持当前事务,如果没有事务,则非事务的执行
	mandatory必要的:如果已经存在一个事务,支持当前事务,如果没有一个活动的事务,则抛出异常
	requires_new:总是开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起。
	not_support:总是非事务的执行,并挂起任何存在的事务。
	never 绝不:总是非事务地执行,如果存在一个活动事务,则抛出异常
	nested 嵌套的:如果有就嵌套,没有就开启事务。

JdbcTemplate

?	spring 提供用于操作JDBC工具类,类似:DBUtils。
?	依赖 连接池DataSource (数据源)
//1 创建数据源(连接池) dbcp
		BasicDataSource dataSource = new BasicDataSource();
//2  创建模板
		JdbcTemplate jdbcTemplate = new JdbcTemplate();
		jdbcTemplate.setDataSource(dataSource);

<!-- 创建模板 ,需要注入数据源-->
	<bean id="jdbcTemplateId" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSourceId"></property>
	</bean>

<!-- 创建数据源 c3p0-->
	<bean id="dataSourceId" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/ee19_spring_day02"></property>
		<property name="user" value="root"></property>
		<property name="password" value="1234"></property>
	</bean>

<!-- 创建数据源 dbcp-->        
 <bean id="dataSourceId" class="org.apache.commons.dbcp.BasicDataSource">
	<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
	<property name="url" value="jdbc:mysql://localhost:3306/ee19_spring_day02">
	<property name="username" value="root"></property>
	<property name="password" value="1234"></property>
</bean>

	使用JdbcDaoSupport
     <!-- 配置dao 
		* dao 继承 JdbcDaoSupport,之后只需要注入数据源,底层将自动创建模板
	-->
	<bean id="userDaoId" class="com.itheima.e_jdbcdaosupport.UserDao">
		<property name="dataSource" ref="dataSourceId"></property>
	</bean>

     

配置properties

 properties文件
	jdbc.driverClass=com.mysql.jdbc.Driver  										jdbc.jdbcUrl=jdbc:mysql://localhost:3306/ee19_spring_day02
	jdbc.user=root
    jdbc.password=1234 
spring配置
   <!-- 加载配置文件 
		"classpath:"前缀表示 src下
		在配置文件之后通过  ${key} 获得内容
	-->
	<context:property-placeholder 														location="classpath:com/itheima/f_properties/jdbcInfo.properties"/>
	
	<!-- 创建数据源 c3p0-->
	<bean id="dataSourceId" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driverClass}"></property>
		<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
		<property name="user" value="${jdbc.user}"></property>
		<property name="password"  value="${jdbc.password}"></property>
	</bean>
     
        

事务配置方式

三个顶级接口
    PlatformTransactionManager  平台事务管理器,spring要管理事务,必须使用事务管理器
进行事务配置时,必须配置事务管理器。
    
	TransactionDefinition:事务详情(事务定义、事务属性),spring用于确定事务具体详情,
		例如:隔离级别、是否只读、超时时间等
	进行事务配置时,必须配置详情。spring将配置项封装到该对象实例。
	
    TransactionStatus:事务状态,spring用于记录当前事务运行状态。例如:是否有保存点,事务是否完成。
	spring底层根据状态进行相应操作。

?	常见的事务管理器
	DataSourceTransactionManager  ,jdbc开发时事务管理器,采用JdbcTemplate
	HibernateTransactionManager,hibernate开发时事务管理器,整合hibernate

TransactionStatus getTransaction(TransactionDefinition definition) ,事务管理器 通过“事务详情”,获得“事务状态”,从而管理事务。
void commit(TransactionStatus status)  根据状态提交
void rollback(TransactionStatus status) 根据状态回滚

手动管理事务(了解)

手动管理事务(了解)
?	spring底层使用 TransactionTemplate 事务模板进行操作。
?	操作
1.service 需要获得 TransactionTemplate 
2.spring 配置模板,并注入给service
3.模板需要注入事务管理器
4.配置事务管理器:DataSourceTransactionManager ,需要注入DataSource

    dao层:
    import org.springframework.jdbc.core.support.JdbcDaoSupport;
public class AccountDaoImple extends JdbcDaoSupport implements AccountDao {
	@Override
	public void out(String outer, int money) {
		this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,outer);
	}
	@Override
	public void in(String inner, int money) {
		this.getJdbcTemplate().update("update account set money = money + ? where name = ?", money,inner);
	}
}
	
	service层:
     import org.springframework.transaction.TransactionStatus;
	import org.springframework.transaction.support.TransactionCallbackWithoutResult;
    import org.springframework.transaction.support.TransactionTemplate;
public class AccountServiceImpl implements AccountService {
	private AccountDao accountDao;
	public void setAccountDao(AccountDao accountDao) {
		this.accountDao = accountDao;
	}
	//需要spring注入模板
	private TransactionTemplate transactionTemplate;
	public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
		this.transactionTemplate = transactionTemplate;
	}
	@Override
	public void transfer(final String outer,final String inner,final Integer money) {
		transactionTemplate.execute(new TransactionCallbackWithoutResult() {	
			@Override
			protected void doInTransactionWithoutResult(TransactionStatus arg0) {
				accountDao.out(outer, money);
				//断电
//				int i = 1/0;
				accountDao.in(inner, money);
			}
		});
	}
}
spring配置
    <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
       					   http://www.springframework.org/schema/beans/spring-beans.xsd
       					   http://www.springframework.org/schema/aop 
       					   http://www.springframework.org/schema/aop/spring-aop.xsd
       					   http://www.springframework.org/schema/context 
       					   http://www.springframework.org/schema/context/spring-context.xsd">

	<!-- 创建数据源 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/study"></property>
		<property name="user" value="root"></property>
		<property name="password" value="10086"></property>
	</bean>
	
	<!-- 配置dao -->
	<bean id="accountDaoImple" class="cn.lm.tx01.AccountDaoImple">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 配置service -->
	<bean id="accountServiceImplId" class="cn.lm.tx01.AccountServiceImpl">
		<property name="accountDao" ref="accountDaoImple"></property>
		<property name="transactionTemplate" ref="transactionTemplate"></property>
	</bean>
	
	<!-- 创建模板 -->
	<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
		<property name="transactionManager" ref="txManager"></property>
	</bean>
	
	<!-- 配置事务管理器 ,管理器需要事务,事务从Connection获得,连接从连接池DataSource获得 -->
	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
</beans>  


工厂bean 生成代理:半自动

	spring提供 管理事务的代理工厂bean TransactionProxyFactoryBean
1.getBean() 获得代理对象
2.spring 配置一个代理
 
  Dao层
import org.springframework.jdbc.core.support.JdbcDaoSupport;
public class AccountDaoImple extends JdbcDaoSupport implements AccountDao {
	@Override
	public void out(String outer, int money) {
		this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,outer);
	}
	@Override
	public void in(String inner, int money) {
		this.getJdbcTemplate().update("update account set money = money + ? where name = ?", money,inner);
	}
}
Service类
    public class AccountServiceImpl implements AccountService {
	private AccountDao accountDao;
	public void setAccountDao(AccountDao accountDao) {
		this.accountDao = accountDao;
	}
	@Override
	public void transfer(String outer, String inner, int money) {
		accountDao.out(outer, money);
		int i = 1 / 0;
		accountDao.in(inner, money);
	}
}
spring配置
    <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance xmlns:context="http://www.springframework.org/schema/context"
	 xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
       					   http://www.springframework.org/schema/beans/spring-beans.xsd
       					   http://www.springframework.org/schema/aop 
       					   http://www.springframework.org/schema/aop/spring-aop.xsd
       					   http://www.springframework.org/schema/context 
       					   http://www.springframework.org/schema/context/spring-context.xsd">

	<!-- 创建数据源 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/study"></property>
		<property name="user" value="root"></property>
		<property name="password" value="10086"></property>
	</bean>
	
	<!-- 配置dao -->
	<bean id="accountDaoImple" class="cn.lm.tx02.AccountDaoImple">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 配置service -->
	<bean id="accountServiceImplId" class="cn.lm.tx02.AccountServiceImpl">
		<property name="accountDao" ref="accountDaoImple"></property>
	</bean>
	
	<!-- 配置事务管理器 -->
	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 4 service 代理对象 
		4.1 proxyInterfaces 接口 
		4.2 target 目标类
		4.3 transactionManager 事务管理器
		4.4 transactionAttributes 事务属性(事务详情)
			prop.key :确定哪些方法使用当前事务配置
			prop.text:用于配置事务详情
				格式:PROPAGATION,ISOLATION,readOnly,-Exception,+Exception
					传播行为		隔离级别	是否只读		异常回滚		异常提交
				例如:
					<prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop> 默认传播行为,和隔离级别
					<prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly</prop> 只读
					<prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,+java.lang.ArithmeticException</prop>  有异常扔提交 -->
	<bean id="proxyAccountService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
		<property name="proxyInterfaces" value="cn.lm.tx02.AccountService"></property>
		<property name="target" ref="accountServiceImplId"></property>
		<property name="transactionManager" ref="txManager"></property>
		<property name="transactionAttributes">
			<props>
				<prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop>
			</props>
		</property>
	</bean>
</beans>	

AOP 配置基于xml【掌握】

?	在spring xml 配置aop 自动生成代理,进行事务的管理
1.配置管理器
2.配置事务详情
3.配置aop
   	Dao
import org.springframework.jdbc.core.support.JdbcDaoSupport;
public class AccountDaoImple extends JdbcDaoSupport implements AccountDao {
	@Override
	public void out(String outer, int money) {
		this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,outer);
	}
	@Override
	public void in(String inner, int money) {
		this.getJdbcTemplate().update("update account set money = money + ? where name = ?", money,inner);
	}
}
	Service
public class AccountServiceImpl implements AccountService {
	private AccountDao accountDao;
	public void setAccountDao(AccountDao accountDao) {
		this.accountDao = accountDao;
	}
	@Override
	public void transfer(String outer, String inner, int money) {
		accountDao.out(outer, money);
		int i = 1 / 0;
		accountDao.in(inner, money);
	}
}

15.3.4.3	配置文件
	<!-- 创建数据源 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/study"></property>
		<property name="user" value="root"></property>
		<property name="password" value="10086"></property>
	</bean>
	
	<!-- 配置dao -->
	<bean id="accountDaoImple" class="cn.lm.tx03_xml.AccountDaoImple">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 配置service -->
	<bean id="accountServiceImplId" class="cn.lm.tx03_xml.AccountServiceImpl">
		<property name="accountDao" ref="accountDaoImple"></property>
	</bean>
	
	<!-- 配置事务管理器 -->
	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 配置 事务详情(事务通知)  , 在aop筛选基础上,对ABC三个确定使用什么样的事务。例如:AC读写、B只读 等
	<tx:attributes> 用于配置事务详情(属性属性)
		<tx:method name=""/> 详情具体配置
			propagation 传播行为 , REQUIRED:必须;REQUIRES_NEW:必须是新的
			isolation 隔离级别   -->
	<tx:advice id="txAdvice" transaction-manager="txManager">
		<tx:attributes>
			<tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT"/>
		</tx:attributes>
	</tx:advice>
	
	<!-- AOP编程,目标类有ABCD(4个连接点),切入点表达式 确定增强的连接器,从而获得切入点:ABC -->
	<aop:config>
		<aop:advisor advice-ref="txAdvice" pointcut="execution(* cn.lm.tx03_xml.*.*(..))"/>
	</aop:config>
</beans>

AOP配置基于注解【掌握】

?	1.配置事务管理器,将并事务管理器交予spring
?	2.在目标类或目标方法添加注解即可 @Transactional
   Service层
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT)
public class AccountServiceImpl implements AccountService {
   private AccountDao accountDao;
   public void setAccountDao(AccountDao accountDao) {
   	this.accountDao = accountDao;
   }
   @Override
   public void transfer(String outer, String inner, int money) {
   	accountDao.out(outer, money);
   	//int i = 1 / 0;
   	accountDao.in(inner, money);
   }
}
<!-- 4 事务管理 -->
   <!-- 4.1 事务管理器 -->
   <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
   	<property name="dataSource" ref="dataSource"></property>
   </bean>
   <!-- 4.2 将管理器交予spring 
   	* transaction-manager 配置事务管理器
   	* proxy-target-class
   		true : 底层强制使用cglib 代理
   -->
   <tx:annotation-driven transaction-manager="txManager"/>    

整合Junit

?	导入jar包
	基本 :4+1 
	测试:spring-test...jar

1.让Junit通知spring加载配置文件
2.让spring容器自动进行注入

?	修改测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class TestApp {
	
	@Autowired  //与junit整合,不需要在spring xml配置扫描
	private AccountService accountService;
	
	@Test
	public void demo01(){
//		String xmlPath = "applicationContext.xml";
//		ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
//		AccountService accountService =  (AccountService) applicationContext.getBean("accountService");
		accountService.transfer("jack", "rose", 1000);
	}

}

整合web

0.导入jar包
	spring-web.xml
	 
1.tomcat启动加载配置文件
	servlet --> init(ServletConfig) --> <load-on-startup>2
	filter --> init(FilterConfig)  --> web.xml注册过滤器自动调用初始化
	listener --> ServletContextListener --> servletContext对象监听【】
	spring提供监听器 ContextLoaderListener  --> web.xml  <listener><listener-class>....
		如果只配置监听器,默认加载xml位置:/WEB-INF/applicationContext.xml
		 java.io.FileNotFoundException:
2.确定配置文件位置,通过系统初始化参数
	ServletContext 初始化参数 web.xml  
		<context-param>
			<param-name>contextConfigLocation
			<param-value>classpath:applicationContext.xml
 <!-- 确定配置文件位置 -->
  <context-param>
  	<param-name>contextConfigLocation</param-name>
  	<param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  
  <!-- 配置spring 监听器,加载xml配置文件 -->
  <listener>
  	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

spring笔记

谈谈你对Spring的理理解

Spring 是一个轻量级开源的JAVAEE 框架, 它的核心是: IOC(控制反转) DI(注入) AOP(面向切面)
    控制反转(IOC):是面向对象编程中的一种设计原则,用来降低程序代码之间的耦合度,使整个程序体系结构更加灵活,
与此同时将类的创建和依赖关系写在配置文件里,由配置文件注入,达到松耦合的效果。与此同时IOC 也称为DI(依赖注入),
依赖注入是一种开发模式;依赖注入提倡使用接口编程; 依赖注入使得可以开发各个组件,然后根据组件之间的依赖关系注入组装。

IOC(控制反转): 把创建对象的权利交给Spring,
    Spring容?使用工厂模式为我们创建了所需要的对象,直接调用就可以了 (线程安全的)
	DI(注入) : Spring使用JavaBean对象的Set方法或者构造方法将其属性自动设置所需要的值;
	AOP(面向切面) : 将一个对象横向抽成一个切面,对这个切面进行一些如权限控制,事务管理,记录日志等,底层是动态代理;

AOP 实现流程
	1、aop:config 自定义标签解析
	2、自定义标签解析时封装对应的aop 入口类,类的类型就是BeanPostProcessor 接口类型
	3、Bean 实例化过程中会执行到aop 入口类中
	4、在aop 入口类中,判断当前正在实例化的类是否在pointcut 中,pointcut 可以理解为一个模糊匹配,是一个joinpoint 的集合
	5、如果当前正在实例化的类在pointcut 中,则返回该bean 的代理类,同时把所有配置的advice 封装成MethodInterceptor 对象加入到容器中,封装成一个过滤器链
	6、代理对象调用,jdk 动态代理会调到invocationHandler 中,cglib 型代理调到MethodInterceptor 的callback 类中,然后在invoke 方法中执行过滤器链。
        
Spring 框架中如何基于AOP 实现的事务管理?
	事务管理,是一个切面。在aop 环节中,其他环节都一样,事务管理就是由Spring 提供的advice,既是TransactionInterceptor,
它一样的会在过滤器链中被执行到,这个TransactionInterceptor 过滤器类是通过解析<tx:advice>自定义标签得到的。

Spring的启动过程
	1.创建一个全局的上下文环境,这个上下文就是ServletContext
	2.在web容?启动时,会触发容?初始化事件,Spring 会创建一个上下文,这个上下文被称为根上下文,就是WebApplicationContext
	3.监听器?初始完毕后,开始初始化web.html配置中的Servlet
    
Spring中的几种设计模式?
	1.单例模式:Spring的配置文件中设置bean默认为单例模式,
		Spring中有两种方式,若目标对象实现了若干接口,Spring使用JDK的类代理没有实现任何接口,Spring使?cglib类的子类
	2.模板方法模式:用来解决代码重复问题.
	3.前端控制?模式:Spring提供了前端控制?DispatherServlet来对请求进行分发
	4.视图帮助(view) : spring提供了一系列列的JSP标签,高效帮助将分散的代码整合在视图中
	5.依赖注入:DI贯穿BeanFactory/ApplicationContext接口的核心理念
	6.工厂模式: spring中使用beanFactory来创建对象实例.
        
bean的生命周期
	单例对象: 生命周期
		1.对象出生: 当应用加载,创建容?时,对象就被创建了
		2.对象活着:只要容?在,对象就一直活着
		3.对象死亡:当容?销毁时,对象就死亡了
	多例对象:生命周期
		1.对象出生:当使用对象时,创建新的对象实例(Bean)
		2.对象活着:只要对象在使用中就一直活着
		3.对象死亡:当对象长时间不使用,Java 的GC 就会自动回收
            
BeanFactory和FactoryBean的区别?
	BeanFactory是IOC最基本的容?,负责生产和管理bean,它为其他具体的IOC容?提供了最基本的规范
	FactoryBean是一个接口,在IOC容?中bean实现了FactoryBean之后,通过getBean(String BeanName)获取到Bean对象
            
spring的事务传播特性?
1.PROPAGATION_REQUIRED : 如果存在一个事务,则支持当前事务,如果没有事务则开启
2.PROPAGATION_SUPPORTS : 如果存在一个事务,则支持当前事务,如果没有事务,则非事务执行
3.PROPAGATION_MANDATORY: 如果存在一个事务,则支持当前事务,如果没有一个活动的事务,则抛出异常
4.PROPAGATION_REQUIRES_NEW : 总是开启一个新事务,如果一个事务已经存在,则将这个存在的事务挂起
5.PROPAGATION_NOT_SUPPORTED : 总是非事务执行,并挂起任何事务
6.PROPAGATION_NEVER : 总是非事务执行,如果存在一个事务则抛出异常
7.PROPAGATION_NESTED : 如果一个活动的事务存在,则运行在一个嵌套的事务中,如果没有活动事务,则按TransactionDefinition.PROPAGATION_REQUIRED执行 nested

Spring 如何解决循环依赖的问题

1 setter注入

SpringMVC

技术分享图片


Spring+SpringMVC

SpringMVC的工作原理:
1.用户发起请求找到DispatchServlet(控制器?)
2.DispatchServlet对URL进行解析,得到请求资源标识符(URL)然后根据URL调用HandlerMapping将请求映射到处理?HandlerExcutionChain
3.DispatchServlet根据获得Handler选择一个具体的HandlerAdapter适配处理器
4.Handler对数据处理完成返回一个ModelAndView()对象给DisPatchServlet(控制?)
5.Handler返回的ModelAndView()只是逻辑视图并不是具体的视图,DispatcherSevlet通过ViewResolver视图解析?将逻辑视图转换成真正的视图View
6.DispatcherServle通过model 解析出 ModelAndView()中的参数进行解析最终返回一个具体的view 并返回给客户端
  
    DispatcherServlet、HandlerMapping、HandlerAdapter和ViewResolver等对象协同工作
    
    SpringMVC常?用注解都有哪些?
		@requestMapping : 用于请求URL 路径
		@requestBody : 接收http请求的json格式,将json格式转换成java对象
		@responseBody : 将controller?方法返回的对象转换成json 响应给客户端
            
springMvc的优化?
1.controller尽量使用单例,可以减少创建对象和回收对象的开销,
2.处理request的方法形参上加@RequestParam注解,可以避免springmvc使用asm框架读取class文件获取方法参数名的过程

Spring MVC整合

1.首先,要在web.xml里面配置SpringMVC的核心控制器,DispatcherServlet,对指定的后缀请求进行拦截。

2.Controller层要加 @Controller注解,表明该类是MVC的控制层。

3.创建Service接口,给接口加上注解 @Component或者 @Service 表明这是Service业务处理层

4.在Controller层声明Service变量(属性),给变量(属性) 加上 @Autowired注解,通过自动绑定机制将Service注入到Controller。 
      (注:@Autowired默认是ByType,如果想根据属性名注入,那么就再加上注解 @Resource(name="属性名"))

5.在Controller层的方法上加上注解 @RequestMapping("requestAddress") 表明该方法的请求地址

6.Dao层要加上注解 @Repository 表明这是数据库持久层

7.同样将dao实例注入到service层中。

8.配置视图解析器 "InternalResourceViewResolver",对处理后的跳转进行统一配置。

springMVC和struts2的不同

	1.核心控制器不同  
              springMVC的核心控制器是servlet,struts2是Filter  (核心控制器的主要作用是处理所有的请求)

	2.控制器实例,springMVC会比struts2快一些(理论上)。
		springMVC是基于方法设计,而struts2是基于对象。
		struts2每次发送请求都会实例一个action,每个action都会被注入属性,而springMVC更像servlet,
                只有一个实例,每次执行对应的方法即可。(注意,由于是单例实例,所以应当避免全局变量的修改,这样会产生线程安全问题)

	3.管理方式:大部分公司的核心架构中,会用到spring,而springMVC又是spring中的一个模块,
                  所以spring对于springMVC的控制器管理更加简单方便,而且提供了全注解方式进行管理,各种功能的注解都比较全面,
                  使用简单,而struts2需要采用xml很多的配置参数来管理(虽然也可以采用注解,但几乎没有公司这么做)。

	4.参数传递:Struts2中自身提供多种参数接受,其实都是通过(ValueStack)进行传递和赋值,而springMVC是通过方法的参数进行接收。

	5.学习难度:struts有很多的技术点,比如拦截器,值栈及OMG表达式,学习成本较高,springMVC比较简单,较少的时间就可以上手。

	6.interceptor的实现机制:struts有自己的interceptor机制,springMVC用的是独立的AOP方式。

	7.springMVC处理ajax请求,直接通过返回数据,方法中使用注解@ResponseBody,springMVC自动把对象转换为json数据,而struts2是通过插件的方式进行处理。

在springMVC流行起来之前,struts2在MVC框架中占核心地位,随着springMVC的出现,springMVC慢慢的取代struts2,但是很多企业都是原来搭建的框架,使用struts2较多。

Struts2

①工作原理
	在Struts2 框架中的处理大概分为以下几个步骤:
		1)客户端初始化一个指向 Servlet 容器(例如 Tomcat )的请求
		2)这个请求经过一系列的过滤器 Filter(这些过滤器中有一个叫做ActionContextCleanUp 的可选过滤器,
                        这个过滤器对于 Struts2 和其他框架的集成很有帮助,例如:SiteMesh Plugin)
		3)接着 FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请求是否需要调用某个Action
    	        4)如果 ActionMapper决定需要调用某个Action,FilterDispatcher 把请求的处理交给 ActionProxy
                5)ActionProxy 通过 Configuration Manager询问框架的配置文件,找到需要调用的 Action 类
          	6)ActionProxy 创建一个 ActionInvocation 的实例。
    	        7)ActionInvocation 实例使用命名模式来调用,在调用 Action 的过程前后,涉及到相关拦截器( Intercepter )的调用。
          	8)一旦 Action 执行完毕, ActionInvocation 负责根据 struts.xml 中的配置找到对应的返回结果。
                        返回结果通常是(但不总是,也可能是另外的一个 Action 链)一个需要被表示的 JSP 或者 FreeMarker 的模版。
                        在表示的过程中可以使用 Struts2 框架中继承的标签。在这个过程中需要涉及到 ActionMapper 。
    
②工作流程:
	1)客户端在浏览器中输入一个 url 地址。
	2)这个 url 请求通过 http 协议发送给 tomcat 。
	3)tomcat 根据 url 找到对应项目里面的 web.xml 文件。
	4)在 web.xml 里面会发现有 struts2 的配置。
	5)然后会找到 struts2 对应的 struts.xml 配置文件。
	6)根据 url 解析 struts.xml 配置文件就会找到对应的 class 。
        7)调用完 class 返回一个字 String ,根据 struts.xml 返回到对应的 jsp 。

技术分享图片

模板技术 ,一般用于页面静态化

freemarker:扩展名:*.ftl

velocity :扩展名 *.vm

SSH整合

1、在ssh框架中是怎么整合spring?
	首先在web.xml中通过ContextLoaderListener来融入spring,并加载spring的相关配置文件
 
 2、在ssh框架中是怎么整合struts2?
	配置sturts2的前端总控制器filterDispatcher来过滤相关的 请求并且加载struts.xml
    
    action继承ActionSupport,然后通过引入struts-spring-plugin.jar包并且根据配置文件中service的id生成get,set方法来注入service层。
    
3、在ssh框架中是怎么整合hibernate?
	通过spring中的配置文件加载hibernate.cfg.xml文件从而融入hibernate
	dao层继承于HibernateDaoSupport,并且在dao的配置文件中注入sessionFactory

1.spring

 基础:4+1 , beans、core、context、expression , commons-logging (struts已经导入)
	AOP:aop联盟、spring aop 、aspect规范、spring aspect
	db:jdbc、tx
测试:test
 web开发:spring web
驱动:mysql
连接池:c3p0
     
     整合包
?	spring整合hibernate: spring orm
?	struts 整合spring:struts2-spring-plugin-2.3.15.3.jar
删除重复jar包
	字节码增强的jar包

2.hibernate:spring orm
%h%\hibernate3.jar         核心
%h%\lib\required          必须

? 技术分享图片

%h%\lib\jpa              jpa规范 (java persistent api 持久api),hibernate注解开发 @Entity @Id 等
 
l 整合log4j
?    导入 log4j...jar (struts已经导入)
?    整合(过渡):slf4j-log4j12-1.7.5.jar

技术分享图片

 二级缓存
?    核心:ehcache-1.5.0.jar
?    依赖:
?        backport-util-concurrent-2.1.jar
?        commons-logging (存在)

3.struts
?		struts-2.3.15.3\apps\struts2-blank\WEB-INF\lib

技术分享图片

spring整合有hibernate.cfg.xml

Javabean
    public class User {
	private Integer id;
	private String username;
	private String password;
	private Integer age;
映射文件
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
	<class name="com.itheima.domain.User" table="t_user">
		<id name="id">
			<generator class="native"></generator>
		</id>
		<property name="username"></property>
		<property name="password"></property>
		<property name="age"></property>
	</class>
</hibernate-mapping>
      	dao层
spring提供 HibernateTemplate 用于操作PO对象,类似Hibernate Session对象。
public class UserDaoImpl implements UserDao {
	//需要spring注入模板
	private HibernateTemplate hibernateTemplate;
	public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
		this.hibernateTemplate = hibernateTemplate;
	}
	@Override
	public void save(User user) {
		this.hibernateTemplate.save(user);
	}
}
        	service层
public class UserServiceImpl implements UserService {
	private UserDao userDao;
	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}
	@Override
	public void register(User user) {
		userDao.save(user);
	}
}
	hibernate.cfg.xml
<session-factory>
		<!-- 1基本4项 -->
		<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="hibernate.connection.url">jdbc:mysql:///ee19_spring_day03</property>
		<property name="hibernate.connection.username">root</property>
		<property name="hibernate.connection.password">1234</property>
		<!-- 2 配置方言 -->
		<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
		<!-- 3 sql语句 -->
		<property name="hibernate.show_sql">true</property>
		<property name="hibernate.format_sql">true</property>
		<!-- 4 自动生成表(一般没用) -->
		<property name="hibernate.hbm2ddl.auto">update</property>
		<!-- 5本地线程绑定 -->
		<property name="hibernate.current_session_context_class">thread</property>	
		<!-- 导入映射文件 -->
		<mapping resource="com/itheima/domain/User.hbm.xml"/>	
	</session-factory>
            
applicationContext.xml
 <!-- 1 加载hibenrate.cfg.xml 获得SessionFactory 
		* configLocation确定配置文件位置
	-->
	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
		<property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
	</bean>	
	<!-- 2创建模板 
		* 底层使用session,session 有sessionFactory获得
	-->
	<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
		<property name="sessionFactory" ref="sessionFactory"></property>
	</bean>
<!-- 3 dao -->
	<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
		<property name="hibernateTemplate" ref="hibernateTemplate"></property>
	</bean>
	<!-- 4 service -->
	<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
		<property name="userDao" ref="userDao"></property>
	</bean>
<!-- 5 事务管理 -->
	<!-- 5.1 事务管理器 :HibernateTransactionManager -->
	<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager" >
		<property name="sessionFactory" ref="sessionFactory"></property>
	</bean>
	<!-- 5.2 事务详情 ,给ABC进行具体事务设置 -->
	<tx:advice id="txAdvice" transaction-manager="txManager">
		<tx:attributes>
			<tx:method name="register"/>
		</tx:attributes>
	</tx:advice>
	<!-- 5.3 AOP编程,ABCD 筛选 ABC  -->
	<aop:config>
		<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service..*.*(..))"/>
	</aop:config>      

spring整合hibernate:没有hibernate.cfg.xml 【】

?	删除hibernate.cfg.xml文件,但需要保存文件内容,将其配置spring中
?	修改dao层,继承HibernateDaoSupport
<!-- 1.1加载properties文件 -->
	<!-- 1.2 配置数据源 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql:///ee19_spring_day03"></property>
		<property name="user" value="root"></property>
		<property name="password" value="1234"></property>
	</bean>
	<!-- 1.3配置 LocalSessionFactoryBean,获得SessionFactory 
		* configLocation确定配置文件位置
			<property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
		1)dataSource 数据源
		2)hibernateProperties hibernate其他配置项
		3) 导入映射文件
			mappingLocations ,确定映射文件位置,需要“classpath:” ,支持通配符 【】
				<property name="mappingLocations" value="classpath:com/itheima/domain/User.hbm.xml"></property>
				<property name="mappingLocations" value="classpath:com/itheima/domain/*.hbm.xml"></property>
			mappingResources ,加载执行映射文件,从src下开始 。不支持通配符*
				<property name="mappingResources" value="com/itheima/domain/User.hbm.xml"></property>
			mappingDirectoryLocations ,加载指定目录下的,所有配置文件
				<property name="mappingDirectoryLocations" value="classpath:com/itheima/domain/"></property>
			mappingJarLocations , 从jar包中获得映射文件
	-->
	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource"></property>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
				<prop key="hibernate.show_sql">true</prop>
				<prop key="hibernate.format_sql">true</prop>
				<prop key="hibernate.hbm2ddl.auto">update</prop>
				<prop key="hibernate.current_session_context_class">thread</prop>
			</props>
		</property>
		<property name="mappingLocations" value="classpath:com/itheima/domain/*.hbm.xml"></property>
	</bean>

/ 底层需要SessionFactory,自动创建HibernateTemplate模板
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {
	@Override
	public void save(User user) {
		this.getHibernateTemplate().save(user);
	}
}
	spring 删除模板,给dao注入SessionFactory
	<!-- 3 dao -->
	<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
		<property name="sessionFactory" ref="sessionFactory"></property>
	</bean>

Struts整合spring。spring创建action

编写action类,并将其配置给spring ,spring可以注入service
2.编写struts.xml 
3.表单jsp页面
4.web.xml 配置 
	1.确定配置文件contextConfigLocation
	2.配置监听器 ContextLoaderListener
	3.配置前端控制器 StrutsPrepareAndExecuteFitler
18.4.1	action类
?	通用
public class UserAction extends ActionSupport implements ModelDriven<User> {
	//1 封装数据
	private User user = new User();
	@Override
	public User getModel() {
		return user;
	}
	
	//2 service
	private UserService userService;
	public void setUserService(UserService userService) {
		this.userService = userService;
	}

?	功能
/**
	 * 注册
	 * @return
	 */
	public String register(){
		userService.register(user);
		return "success";
	}

18.4.2	spring配置
<!-- 6 配置action -->
	<bean id="userAction" class="com.itheima.web.action.UserAction" scope="prototype">
		<property name="userService" ref="userService"></property>
	</bean>


18.4.3	struts配置
<struts>
	<!-- 开发模式 -->
    <constant name="struts.devMode" value="true" />

    <package name="default" namespace="/" extends="struts-default">
    	<!-- 底层自动从spring容器中通过名称获得内容, getBean("userAction") -->
    	<action name="userAction_*" class="userAction" method="{1}">
    		<result name="success">/messag.jsp</result>
    	</action>
    </package>
</struts>

18.4.4	jsp表单
<form action="${pageContext.request.contextPath}/userAction_register" method="post">
		用户名:<input type="text" name="username"/> <br/>
		密码:<input type="password" name="password"/> <br/>
		年龄:<input type="text" name="age"/> <br/>
		<input type="submit" />
	</form>

18.4.5	配置web.xml
<!-- 1 确定spring xml位置 -->
  <context-param>
  	<param-name>contextConfigLocation</param-name>
  	<param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  <!-- 2 spring监听器 -->
  <listener>
  	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <!-- 3 struts 前端控制器 -->
  <filter>
  	<filter-name>struts2</filter-name>
  	<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
  </filter>
  <filter-mapping>
  	<filter-name>struts2</filter-name>
  	<url-pattern>/*</url-pattern>
  </filter-mapping>

struts整合spring:struts创建action 【】

?	删除spring action配置
?	struts <action class="全限定类名">
    
?	要求:Action类中,必须提供service名称与 spring配置文件一致。(如果名称一样,将自动注入)
<package name="default" namespace="/" extends="struts-default">
    	<!-- 底层自动从spring容器中通过名称获得内容, getBean("userAction") -->
    	<action name="userAction_*" class="com.itheima.web.action.UserAction" method="{1}">
    		<result name="success">/messag.jsp</result>
    	</action>
    </package>

分析:
1. struts 配置文件
	default.properties  ,常量配置文件
	struts-default.xml ,默认核心配置文件
	struts-plugins.xml ,插件配置文件
	struts.xml,自定义核心配置文件
	常量的使用,后面配置项,将覆盖前面的。
2.default.properties  ,此配置文件中确定 按照【名称】自动注入
	/org/apache/struts2/default.properties
	 
3. struts-plugins.xml ,struts整合spring 
		<constant name="struts.objectFactory" value="spring" />
	struts的action将有spring创建

总结,之后action有spring创建,并按照名称自动注入

Hibernate的运行原理

	首先通过configuration去加载hibernate.cfg.xml这个配置文件,根据配置文件的信息去创建sessionFactory,sessionFactory是线程安全的,
是一个session工厂,用来创建session,session是线程不安全的,相当于jdbc的connection,最后通过session去进行数据库的各种操作,
在进行操作的时候通过transaction进行事务的控制。

Hibernate五大核心(类/接口)简述

	1 .Configuration接口的作用是对Hibernate进行配置,以及对它进行启动。(加载hibernate.cfg.xml)并创建一个SessionFactory对象。

	2 .SessionFactory接口
		SessionFactory接口负责初始化Hibernate。它充当数据存储源的代理,并负责创建Session对象。SessionFactory是线程安全的。

	3 .Session接口
		Session(会话)接口是Hibernate应用使用的主要接口。Session接口负责执行被持久化对象的CRUD操作(增删改查)。
                  Session对象是非线程安全的。Session 相当于jdbc的connection

	4 .Query与Criteria接口 总之Query和Criteria接口负责执行各种数据库查询。

	5 .Transaction接口 Transaction(事务)负责操作相关的事务。

Hibernate与JDBC的区别

1、hibernate和jdbc主要区别就是,hibernate先检索缓存中的映射对象( 即hibernate操作的是对象),而jdbc则是直接操作数据库.

2、Hibernate是JDBC的轻量级的对象封装,它是一个独立的对象持久层框架。Hibernate可以用在任何JDBC可以使用的场合

3、Hibernate是一个和JDBC密切关联的框架,所以Hibernate的兼容性和JDBC驱动,和数据库都有一定的关系,
      但是和使用它的Java程序,和App Server没有任何关系,也不存在兼容性问题。

4、如果正确的使用JDBC技术,它的执行效率一定比hibernate要好,因为hibernate是基于jdbc的技术.

5、JDBC使用的是SQL语句,Hibernate使用的是HQL语句,但是HQL语句最终还会隐式转换成SQL语句执行。

开启事务 session.beginTransaction();

执行相关的操作,如果成功则session.getTransaction().commit();

执行操作失败则 session.getTransaction.rollback();

Hibernate的三种状态以及状态的转换

Transient(临时)
   new 一个初始化对象后,并没有在数据库里保存数据,处于临时状态;

Persistent(持久化)
   当执行save()方法,调用session.close()方法之前,内存中的对象与数据库有对应关系处于持久化状态;

Detached(托管/游离)
   当执行session.close()之后,处于托管状态;

状态的转换
   处于托管状态下,调用update()方法后,转换为持久化状态;
   在持久化状态下,执行delete()方法后,转换为临时状态;
   在未初始化对象之前,调用get(),load(),find(),iterate()之后,直接进入持久化状态。

hibernate缓存概述

	hibernate分为一级缓存即session缓存也叫事务级别的缓存以及二级缓存sessionFactory即应用级别的缓存,还有查询缓存即三级缓存.

一级缓存的生命周期和session的生命周期保持一致,hibernate默认就启用了一级缓存,不能将其关闭,
可以通过session.clear()和session.evict(object)来管理一级缓存。其中get,load,iterate都会使用一级缓存,一级缓存缓存的是对象。

二级缓存的生命周期和sessionFactory的生命周期保持一致,可以跨session,被多个session共享,
hibernate3默认开启二级缓存,也可以手动开启并指定缓存插件如ehcache,oscache等。二级缓存也只能缓存对象。

三级缓存也叫查询缓存,查询缓存是针对普通属性结果集的缓存,对实体对象的结果集只缓存id。
对query.list()起作用,query.iterate不起作用,也就是query.iterate不使用查询缓存

Mybatis

1. #{}和${}的区别是什么?
	#{}是预编译处理,${}是字符串替换。
	Mybatis 在处理#{}时,会将sql 中的#{}替换为?号,调用PreparedStatement 的set方法来赋值;
	Mybatis 在处理${}时,就是把${}替换成变量的值。
	使用#{}可以有效的防止SQL 注入,提高系统安全性。
    
2. 通常一个Xml 映射文件,都会写一个Dao 接口与之对应,请问,这个Dao 接口的工作原理是什么?Dao 接口里的方法,参数不同时,方法能重载吗?
	Dao 接口,就是人们常说的Mapper 接口,接口的全限名,就是映射文件中的namespace的值,接口的方法名,
            就是映射文件中MappedStatement 的id 值,接口方法内的参数,就是传递给sql 的参数。
            Mapper 接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key 值,可唯一定位一个MappedStatement,
            举例:
	      com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到namespace 为com.mybatis3.mappers.StudentDao 
                  下面id = findStudentById 的MappedStatement。

    在Mybatis 中,每一个<select>、<insert>、<update>、<delete>标签,都会被解析为一个MappedStatement 对象。
	Dao 接口里的方法,是不能重载的,因为是全限名+方法名的保存和寻找策略。
	Dao 接口的工作原理是JDK 动态代理,Mybatis 运行时会使用JDK 动态代理为Dao接口生成代理proxy 对象,
            代理对象proxy 会拦截接口方法, 转而执行MappedStatement 所代表的sql,然后将sql 执行结果返回。

 3. Mybatis 是如何进行分页的?分页插件的原理是什么?
            Mybatis 使用RowBounds 对象进行分页,它是针对ResultSet 结果集执行的内存分页,而非物理分页,
            可以在sql 内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。
            分页插件的基本原理是使用Mybatis 提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,
            然后重写sql,根据dialect 方言,添加对应的物理分页语句和物理分页参数。
    
4. Mybatis 是如何将sql 执行结果封装为目标对象并返回的?都有哪些映射形式?
	第一种是使用<resultMap>标签,逐一定义列名和对象属性名之间的映射关系。
    	第二种是使用sql 列的别名功能,将列别名书写为对象属性名,比如T_NAME AS NAME,对象属性名一般是name,
小写,但是列名不区分大小写,Mybatis 会忽略列名大小写,智能找到与之对应对象属性名,你甚至可以写成T_NAME AS NaMe,Mybatis一样可以正常工作。
有了列名与属性名的映射关系后,Mybatis 通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。
    
5. Xml 映射文件中,除了常见的select|insert|update|delete 标签之外,还有哪些标签?
		注:这道题出自京东面试官。
      还有很多其他的标签, 加上动态sql 的9 个标签,
trim|where|set|foreach|if|choose|when|otherwise|bind 等,其中为sql 片段标签,通过标签引入sql 片段,为不支持自增的主键生成策略标签。
    
6. 简述Mybatis 的插件运行原理,以及如何编写一个插件
      Mybatis 仅可以编写针对ParameterHandler 、ResultSetHandler 、StatementHandler、Executor 这4 种接口的插件,Mybatis 使用JDK 的动态代理,
为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4 种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler 的invoke()方法,当
然,只会拦截那些你指定需要拦截的方法。实现Mybatis 的Interceptor 接口并复写intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,
记住,还需要在配置文件中配置你编写的插件。
    
7. 一级、二级缓存
      1) 一级缓存: 基于PerpetualCache 的HashMap 本地缓存, 其存储作用域为Session,当Session flush 或close 之后,该Session 中的所有Cache 就将清空。
      2)二级缓存与一级缓存其机制相同,默认也是采用PerpetualCache,HashMap 存储, 不同在于其存储作用域为Mapper(Namespace), 并且可自定义存储源, 如
            Ehcache。要开启二级缓存,你需要在你的SQL 映射文件中添加一行:<cache/>
      3 ) 对于缓存数据更新机制, 当某一个作用域( 一级缓存Session/ 二级缓存Namespaces)的进行了C/U/D 操作后,默认该作用域下所有select 中的缓存将被clear。
    
8. Mybatis 是否支持延迟加载?如果支持,它的实现原理是什么?
	Mybatis 仅支持association 关联对象和collection 关联集合对象的延迟加载,association 指的就是一对一,collection 指的就是一对多查询。
            在Mybatis 配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。它的原理是,使用CGLIB 创建目标对象的代理对象,
            当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,
            那么就会单独发送事先保存好的查询关联B 对象的sql,把B 查询上来,然后调用a.setB(b),于是a 的对象b 属性就有值了,
            接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。
    
9. Mybatis 映射文件中,如果A 标签通过include 引用了B 标签的内容,请问,B 标签能否定义在A 标签的后面,还是说必须定义在A 标签的前面?
	虽然Mybatis 解析Xml 映射文件是按照顺序解析的,但是,被引用的B 标签依然可以定义在任何地方,Mybatis 都可以正确识别。
      原理是,Mybatis 解析A 标签,发现A 标签引用了B 标签,但是B 标签尚未解析到,尚不存在,此时,Mybatis 会将A 标签标记为未解析状态,
      然后继续解析余下的标签,包含B 标签,待所有标签解析完毕,Mybatis 会重新解析那些被标记为未解析的标签,此时再解析A 标签时,
      B 标签已经存在,A 标签也就可以正常解析完成了。
    
10. 简述Mybatis 的Xml 映射文件和Mybatis 内部数据结构之间的映射关系?
	Mybatis 将所有Xml 配置信息都封装到All-In-One 重量级对象Configuration 内部。在Xml 映射文件中,<parameterMap>标签会被解析为ParameterMap 对象,
      其每个子元素会被解析为ParameterMapping 对象。<resultMap>标签会被解析为ResultMap 对象,其每个子元素会被解析为ResultMapping 对象。每一个<select>、
<insert>、<update>、<delete>标签均会被解析为MappedStatement 对象,标签内的sql 会被解析为BoundSql 对象。

Mybatis 缓存

	Mybatis 中有一级缓存和二级缓存,默认情况下一级缓存是开启的,而且是不能关闭的。
        一级缓存是指SqlSession 级别的缓存,当在同一个SqlSession 中进行相同的SQL 语句查询时,
            第二次以后的查询不会从数据库查询,而是直接从缓存中获取,一级缓存最多缓存1024 条SQL。
        二级缓存是指可以跨SqlSession 的缓存。是mapper 级别的缓存,对于mapper 级别的缓存不同的sqlsession 是可以共享的。

技术分享图片

1.Mybatis 的一级缓存原理(sqlsession 级别)
	第一次发出一个查询sql,sql 查询结果写入sqlsession 的一级缓存中,缓存使用的数据结构是一个map。
	key:MapperID+offset+limit+Sql+所有的入参
	value:用户信息
	同一个sqlsession 再次发出相同的sql,就从缓存中取出数据。如果两次中间出现commit 操作(修改、添加、删除),
      本sqlsession 中的一级缓存区域全部清空,下次再去缓存中查询不到所以要从数据库查询,从数据库查询到再写入缓存。
    
2.二级缓存原理(mapper 基本)
	二级缓存的范围是mapper 级别(mapper 同一个命名空间),mapper 以命名空间为单位创建缓存数据结构,结构是map。
      mybatis 的二级缓存是通过CacheExecutor 实现的。CacheExecutor其实是Executor 的代理对象。所有的查询操作,
      在CacheExecutor 中都会先匹配缓存中是否存在,不存在则查询数据库。

key:MapperID+offset+limit+Sql+所有的入参
具体使用需要配置:
	1. Mybatis 全局配置中启用二级缓存配置
	2. 在对应的Mapper.xml 中配置cache 节点 <setting name="cacheEnabled"value="true"/>
	3. 在对应的select 查询节点中添加useCache=true
    	清空缓存  flushCache="true"
    
<settings> 
    <!-- 开启二级缓存的支持 -->
    <setting name="cacheEnabled" value="true"/> 
</settings>

ibatis(mybatis)和hibernate有什么不同?

相同点:
	都是Java中ORM框架,屏蔽jdbc,api的底层访问细节,我们不用与jdbc api打交道就可以完成对数据库的持久化操作。
      jdbc api编程流程固定,还将sql语句与Java代码混杂在了一起,经常需要拼凑sql语句,细节很繁琐。

	ibatis(mybatis)的好处:屏蔽jdbc api的底层访问细节,将sql语句与Java代码进行分离,提供了将结果集自动封装成为实体对象和对象的集合的功能。
    queryforList返回对象集合,用queryforObject返回单个对象;
    提供了自动将实体对象的属性传递给sql语句的参数。

	hibernate的好处:
    	hibernate是一个全自动的ORM映射工具,它可以自动生存sql语句,并执行并返回Java结果。

不同点:
	1.hibernate要比ibatis(mybatis)功能强大很多,因为hibernate自动生存sql语句。
	2.ibatis(mybatis)需要我们自己在xml配置文件中写sql语句,hibernate我们无法直接控制该语句,
            我们就无法去写特定的高效的sql,对于一些不太复杂的sql查询,hibernate可以很好地帮我们完成,但是,对于复杂的查询,
            hibernate就很难适应了,这时候用ibatis就是不错的选择,因为ibatis还是由我们自己写sql语句。
	3.ibatis要比hibernate简单得多,ibatis是面向sql的,不用考虑对象间一些复杂的映射关系。
  ————————————————————————————————————————————————————————————————————————  
    
	Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,建立对象与数据库表的映射。是一个全自动的、完全面向对象的持久层框架。
	Mybatis是一个开源对象关系映射框架,原名:ibatis,2010年由谷歌接管以后更名。是一个半自动化的持久层框架。

 1 相同点
	Hibernate与MyBatis都可以是通过SessionFactoryBuider由XML配置文件生成SessionFactory,然后由SessionFactory 生成Session,最后由Session来开启执行事务和SQL语句。

	其中SessionFactoryBuider,SessionFactory,Session的生命周期都是差不多的。Hibernate和MyBatis都支持JDBC和JTA事务处理。

2 两者区别
    2.1 开发方面
        在项目开发过程当中,就速度而言:
            hibernate开发中,sql语句已经被封装,直接可以使用,加快系统开发;
            Mybatis 属于半自动化,sql需要手工完成,稍微繁琐;
        但是,凡事都不是绝对的,如果对于庞大复杂的系统项目来说,发杂语句较多,选择hibernate 就不是一个好方案。
    
    2.2 sql优化方面
        Hibernate 自动生成sql,有些语句较为繁琐,会多消耗一些性能;
        Mybatis 手动编写sql,可以避免不需要的查询,提高系统性能;

    2.3 对象管理比对
        Hibernate 是完整的对象-关系映射的框架,开发工程中,无需过多关注底层实现,只要去管理对象即可;
        Mybatis 需要自行管理 映射关系;

    2.4 缓存方面    	
相同点:
	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:强大、方便、高效、复杂、间接、全自动化

SSM整合


Spring boot

Spring Boot 原理
	Spring Boot 是由Pivotal 团队提供的全新框架,其设计目的是用来简化新Spring 应用的初始搭建以及开发过程。
该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。
通过这种方式,Spring Boot 致力于在蓬勃发展的快速应用开发领域(rapid applicationdevelopment)成为领导者。其特点如下:
    1、创建独立的Spring应用程序
    2、嵌入的Tomcat,无需部署WAR文件
    3、简化Maven配置
    4、自动配置Spring
    5、提供生产就绪型功能,如指标,健康检查和外部配置
    6、绝对没有代码生成和对XML没有要求配置

常用注解
@SpringBootApplication 扫描到Configuration类并把它加入到程序的上下文,启动类
@EnableAutoConfiguration 自动配置
@ComponetScan 组件扫描,可自动发现和装配一些Bean
@Autowierd 自动导入
@PathVariable 获取参数

数据库

技术分享图片

mysql数据库

常用的存储引擎

    1 MyISAM存储引擎
		? MyISAM引擎是MySQL数据库最常用的;
		? 它管理的表具有以下特性:
		? 使用三个文件表示每个表:
            a) 格式文件 — 存储表的结构(mytable.frm)
            b) 数据文件 — 存储表的数据(mytable.MYD)
            c) 索引文件 — 存储表的索引(mytable.MYI)
		? 可转换为压缩、叧读表来节省空间

    2 InnoDB存储引擎
        ? InnoDB存储引擎是MySQL数据库的缺省引擎;
        ? 它管理的表具体有以下特征:
            a) 每个InnoDB表在数据库目录中以.frm格式文件表示
            b) InnoDB表空间tablespace被用于存储表的内容
            c) 提供一组用来记录事务性活劢的日志文件
            d) 用COMMIT(提交)、SAVEPOINT及ROLLBACK(回滚)支持事务处理
            e) 提供全部ACID兼容
            f) 在MySQL服务器崩溃后提供自动恢复
            g) 多版本(MVCC)和行级锁定
            h) 支持外键及引用的完整性,包括级联更新和删除

    3 MEMORY存储引擎
        ? 使用MEMORY存储引擎的表,因为数据存储在内存中,且行的长度固定,所以使得MEMORY存储引擎非常快;
        ? MEMORY存储引擎管理的表具有下列特征:
            a) 在数据库目录内,每个表均以.frm格式文件表示;
            b) 表数据及索引被存储在内存中;
            c) 表级锁机制;
            d) 字段属性不能包含TEXT或BLOB字段;
        ? MEMORY存储引擎以前被称为HEAP引擎;
  
    选择合适的存储引擎
		MyISAM表最适合于大量的数据读而少量数据更新的混合操作。MyISAM表的另一种适用情形是使用压缩的只读表。
		? 如果查询中包含较多的数据更新操作,应使用InnoDB。其行级锁机制和多版本的支持为数据读取和更新的混合提供了良好的并发机制。
	? 使用MEMORY存储引擎存储非永久需要的数据,或者是能够从基于磁盘的表中重新生成的数据。

事务Transaction

1) 开启事务:start transaction
2) 结束事务:end transaction
3) 提交事务:commit transaction
4) 回滚事务:rollback transaction
    
    隔离性有四个隔离级别 isolation  脏读 不可重复读 幻象读
1) read uncommitted 读未提交
2) read committed 读已提交
3) repeatable read 可重复读  MySQL数据库管理系统默认隔离级别
4) serializable 串行化
    
    查看当前会话级隔离级别
        select @@tx_isolation;
        select @@session.tx_isolation;		
	查看当前全局隔离级别:
        @@global.tx_isolation;
		select @@global.tx_isolation;

索引

索引(Index)是帮助DBMS高效获取数据的数据结构。
分类:普通索引/唯一索引/主键索引/全文索引
	普通索引:允许重复的值出现
	唯一索引:除了不能有重复的记录外,其它和普通索引一样(用户名、用户身份证、email,tel)
	主键索引:是随着设定主键而创建的,也就是把某个列设为主键的时候,数据库就会給改列创建索引。这就是主键索引.唯一且没有null值
	全文索引:用来对表中的文本域(char,varchar,text)进行索引, 全文索引针对MyIsam
explain select * from articles where match(title,body) against(‘database’);【会使用全文索引】

Oracle数据库

ORACLE数据库具有以下特点:
    1 支持多用户、大事务量的事务处理
    2 数据安全性和完整性控制
    3 支持分布式数据处理
    4 可移植性
    
 数据文件dbf
	数据文件是数据库的物理存储单位。数据库的数据是存储在表空间中的,真正是在某一个或者多个数据文件中。
而一个表空间可以由一个或多个数据文件组成,一个数据文件只能属于一个表空间。一旦数据文件被加入到某个表空间后,就不能删除这个文件,
如果要删除某个数据文件,只能删除其所属于的表空间才行。
    
 表空间
	表空间是Oracle 对物理数据库上相关数据文件( ORA 或者 DBF 文件)的逻辑映射。
一个数据库在逻辑上被划分成一到若干个表空间,每个表空间包含了在逻辑上相关联的一组结构。每个数据库至少有一个表空间 称之为 system 表空间。
每个表空间由同一磁盘上的一个或多个文件组成,这些文件叫数据文件。一个数据文件只能属于一个表空间。
    VARCHAR2(30),DATE,NUMBER,CHAR(4),NUMBER(10,2)
    
    数据类型:
		1.字符型
			1 CHAR : 固定长度的字符类型,最多存储 2000 个字节
			2 VARCHAR2 : 可变长度的字符类型,最多存储 4000 个字节
			  LONG : 大文本类型。大文本类型。最大可以存储最大可以存储22个个GG
		2.数值型数值型
			NUMBER : 数值类型数值类型
			例如:
                  NUMBER (5) 最大可以存的数为 99999
				NUMBER(5,2) 最大可以存的数为 999.99
		3.日期型
			1 DATE :日期时间型,精确到秒
			2 TIMESTAMP :精确到秒的小数点后 9 位
		4.二进制型(大数据类型)二进制型(大数据类型)
			 CLOB : 存储字符 最大可以存 4 个 G
			 BLOB :存储图像、声音、 视频等二进制数据 最多可以存 4 个 G
                 
   序列是 ORACLE 提供的用于产生一系列唯一数字的 数据库对象 。
      select 序列名称 .nextval from dual  提取下一个值
      select 序列名称 .currval from dual  提取当前值         

索引

1 普通索引
    create index 索引名称 on 表名 列名;
2 唯一索引    
    create unique index 索引名称 on 表名 列名;
3 复合索引    
    create index 索引名称 on 表名 列名 列名...;
4 反向键索引   
    create index 索引名称 on 表名 列名 reverse;
    应用场景:当某个字段的值为连续增长的值,如果构建标准索引,会形成歪脖子树。这样会增加查询的层数,性能会下降。
    建立反向键索引,可以使索引的值变得不规则,从而使索引树能够均匀分布。
5 位图索引
     语法:create bitmap index 索引名称 on 表名 列名;   
     使用场景:位图索引适合创建在低基数列上位图索引不直接存储ROWID ,而是存储字节位到 ROWID 的映射	优点:减少响应时间,节省空间占用
  
    

约束

	在Oracle中,数据完整性可以使用约束、触发器、应用程序(过程、函数)三种方法来实现,
        在这三种方法中,因为约束易于维护,并且具有最好的性能,所以作为维护数据完整性的首选。
  
约束的分类
    not null(非空)    
    unique(唯一)
    primary key(主键)
    foreign key(外键)
    check
        
存储过程:是大型的SQL语句集,用于在大型数据库系统中完成特定的功能。
    存储在数据库中,编译后永久有效,用户通过指定存储过程的名称并指定参数(如果存储过程具有参数)来执行。
        
触发器:
	触发器是用户得以在关系表上的一类有事件驱动的数据库对象,也是一种保证数据完整性的方法;
	触发器一旦定义,无需用户调用,任何对表的修改操作均由数据库服务器自动激活相应的触发器。
	触发器的主要作用是实现主键和外键不能保证的复杂的参照关系性和数据的一致性,从而保护表中数据;       
	触发器与存储过程的唯一区别是触发器不能执行EXECUTE语句调用,而是在用户执行Transact-SQL语句时自动触发执行。        
        

Redis

	redis是一个key-value的nosql数据库,先存到内存中,会根据一定的策略持久化到磁盘,即使断电时也不会丢失数据,支持的数据类型比较多。
主要用来做缓存数据库的数据和web集群时当作中央缓存存放session
	使用场景:
        缓存:把经常需要查询的,很少修改的数据,放到读取速度很快的空间(内存),以便下次访问时减少访问时间,减轻压力。
		计数器:redis中的计数器是原子性的内存操作。可以解决库存溢出问题,进销系统库存溢出。
		session缓存服务器:web集群时作为session缓存服务器缓存队列等。

String 字符串
hash 键值对
list 链表
set 集合
zset 有序集合
    
Redis 持久化?
      RDB 持久化 (默认) : 在指定的时间间隔内将内存中的数据集快照写入磁盘。 dump.rdb
		在指定的时间间隔内生成数据集的时间点快照,适用于进行备份,可以最大化Redis性能
      AOF 持久化 : 记录服务?执行的所有写操作命令,并在服务?启动时,通过重新执行这些命令来还原数据集,
            使用AOF持久化会让Redis变的非常耐久,对于相同数量的数据集而言,AOF文件通常是要大于RDB文件,AOF在运行效率上往往会慢于RDB。  								           appendonly.aof
    触发RDB快照
        1 在指定的时间间隔内,执行指定次数的写操作
        2 执行save(阻塞, 只管保存快照,其他的等待) 或者是bgsave (异步)命令
        3 执行flushall 命令,清空数据库所有数据,意义不大。
        4 执行shutdown 命令,保证服务器正常关闭且不丢失任何数据,意义...也不大。
    对于RDB来说,提供了三种机制:save、bgsave、自动化。    
        自动触发是由我们的配置文件来完成的。在redis.conf配置文件中。
        
两者的区别:
	RDB持久化是指在指定的时间间隔之内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子程序,现将数据集写入临时文件.写入成功后,在替换之前的文件,用二进制压缩存储
	AOF持久化以日志的形式记录服务?所处理的每一个写/删除操作,查询操作不会记录,以文本形式记录,可以打开文本查看详细的操作记录。      

笔记

java动态代理
目标类
切面类
proxy.newProxyInstance(
	类加载器,
	代理类需要实现的所有接口(数组),
	new InvocationHandler(){
		@Override
		public Object invoke(代理对象,方法,方法的实际参数)throws throwable{
			method.invoke(userService,args);
		}
	}

);
CGLIB字节码增强
public class MyBeanFactory {;
public static UserServiceImpl createService(){;
目标类 final UserServiceImpl userservice = new UserServiceImpl();
代理类	final MyAspect aspect = new MyAspect();
核心类:Enhance enhance = new Enhance();
获取代理类的子类:enhance.setSuperClass(userservice.getClass());
设置回调函数:enhance.callback(new MethodInterceptor(){
@Override
public Object intercept(Object proxy,Method method,Object[] args,方法的代理 methodProxy){
			前方法
			目标方法
			Object obj = method.invoke(userservice,args);
			执行代理类的父类
			methodProxy.invokeSuper(proxy,args);
			后方法
				return obj;
			}			
		});
创建代理:
	UserServiceImpl proxyservice = (UserServiceImpl)enhance.create();
	return proxyservice;

1、String和StringBuffer的区别?

	简单地说,就是一个变量和常量的关系。
        StringBuffer 对象的内容可以修改;
        而String 对象一旦产生后就不可以被修改,重新赋值其实是两个对象。
        StringBuffer 的内部实现方式和String 不同, StringBuffer 在进行字符串处理时,不生成新的对象,在内存使用上要优于String 类。
所以在实际使用时,如果经常需要对一个字符串进行修改,例如插入、删除等操作,使用StringBuffer 要更加适合一些。
        
Sting StringBuilder StringBuffer 区别:
   1.String 是内容不可变的字符串,String底层使用了一个不可变的字符数组(final char value[]);
   2.StirngBuilder StringBuffer,是内容可以改变的字符串,底层(char[] value)

   最经典的就是拼接字符串。
   1.String进行拼接String c ="a"+"b";
   2.StringBuilder或者StringBuffer;
   StringBuilder sb = new StringBuilder();
   sb.apend("a").apend("b");
   拼接字符串不能使用String进行拼接,要使用StirngBuilder StringBuffer。

   StringBuilder是线程不安全的,效率较高。默认长度为16
   StringBuffer 是线程安全的,效率较低。初始长度为16

2、接口和抽象类的区别?

Java 提供和支持创建抽象类和接口。
    它们的实现有共同点,不同点在于:
		接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。
		类可以实现很多个接口,但是只能继承一个抽象类
		类可以不实现抽象类和接口声明的所有方法,当然,在这种情况下,类也必须得声明成是抽象的。
		抽象类可以在不提供接口方法实现的情况下实现接口。
Java 接口中声明的变量默认都是final 的。抽象类可以包含非final 的变量。
Java 接口中的成员函数默认是public 的。抽象类的成员函数可以是private , protected 或者是public 。
		接口是绝对抽象的,不可以被实例化。抽象类也不可以被实例化,但是,如果它包含main 方法的话是可以
被调用的。
    
抽象类(abstract class)和接?(interface)有什么异同?
    成员区别: 抽象类成员可以是变量或常量,拥有构造方法,可以有抽象方法和非抽象?法;
			 接口?抽象更抽象 接?的成员变量量只能是常量,没有构造方法,都是抽象?法.
	关系区别: 抽象类子类使用extends关键字来继承,只能单继承或者多重继承;
			接口使用implements来实现,可以多实现
	设计理念: 抽象类的方法可以由public,protected和defalut修饰符; 
			?而接口默认修饰符只有public;

3、什么是cookie?session和cookie有什么区别?

cookie 是Web服务器发送给浏览器的一块信息。
    浏览器会在本地文件中给每一个Web服务器存储cookie。以后浏览器在给特定的Web服务器发请求的时候,同时会发送所有为该服务器存储的cookie。
    session 和cookie 的区别:
		无论客户端浏览器做怎么样的设置,session 都应该能正常工作。
    	客户端可以选择禁用cookie ,但是session 仍然是能够工作的,因为客户端无法禁用服务端的session 。
	在存储的数据量方面session 和cookies 也是不一样的。
    	session 能够存储任意的Java 对象, cookie 只能存储String 类型的对象。
	session和cookie都是会话(session)跟踪技术。
        cookie通过在客户端记录信息确定用户身份,session通过在服务器端记录信息确定用户身份。
        但是session的实现依赖于cookie,sessionID(session的唯一标识符需要存放在客户端)。
        
    cookie和session的区别:
		1.cookie数据存放在客户的浏览器上,session数据存放在服务器上。
		2.cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应该使用session。
		3.session会在一定的时间内保存在服务器上。当访问增多,会比较占用服务器性能,考虑到服务器性能方面,应该使用cookie。
		4.单个cookie保存的数据不能超过4k,很多浏览器限制一个站点最多保存20个cookie。
		5.所以建议:
			将登录信息等重要信息存放在session其他信息如果需要保留,可以放在cookie中。
比如购物车最好使用cookie,但是cookie是可以在客户端禁用的,这时候我们要使用cookie+数据库的方式实现,当从cookie中不能取出数据时,就从数据库获取。

4、sendRedirect()和forward()方法有什么区别?

sendRedirect() 方法会创建一个新的请求,而forward() 方法只是把请求转发到一个新的目标上。
    重定向(redirect) 以后,之前请求作用域范围以内的对象就失效了, 因为会产生一个新的请求,而转发(forwarding)
以后,之前请求作用域范围以内的对象还是能访问的。一般认为sendRedirect() 比forward() 要慢
 
    1.forward是服务器端的转向,而redirect是客户端的跳转。
	2.使用forward浏览器的地址不会发生改变,而redirect会发生改变。
	3.forward是一次请求中完成的,而redirect是重新发起请求。
	4.forward是在服务器端完成,而不用客户端重新发起请求,效率较高。

5、根据你的理解,解释一下MVC。

1)模型Model:应用对象。
	模型是应用程序的主体部分。模型代表了业务数据和业务逻辑; 当数据发生改变时,它要负责通知视图部分;一个模型能为多个视图提供数据。
由于同一个模型可以被多个视图重用,所以提高了应用的可重用性。
(2)视图View:数据的展现。
	视图是用户看到并与之交互的界面。视图向用户显示相关的数据,并能接收用户的输入数据,但是它并不进行任何实际的业务处理。
视图可以向模型查询业务状态,但不能改变模型。视图还能接受模型发出的数据更新事件,从而对用户界面进行同步更新。
(3)控制器Controller :逻辑处理、控制实体数据在视图上展示、调用模型处理业务请求。
	当 Web 用户单击 Web 页面中的提交按钮来发送 HTML 表单时,控制器接收请求并调用相应的模型组件去
处理请求,然后调用相应的视图来显示模型返回的数据。

6、Hibernate 实体对象生命周期的三种状态

(1)Transient( 瞬态) :
    一个实体通过new操作符创建后,没有和Hibernate 的Session 建立关系,也没有手动赋值过该实体的持久化标识(持久化标识可以认为映射表的主键)。
此时该实体中的任何属性的更新都不会反映到数据库表中。
(2)persistent( 持久态) :
    当一个实体和Hibernate 的Session 创建了关系,并获取了持久化标识,而且在Hibernate 的Session 生命周期内存在。
此时针对该实体任何属性的更改都会直接影响到数据库表中一条记录对应字段的更新,也即与对应数据库表保持同步。
(3)Detached( 游历态) :
    当一个实体和Hibernate 的Session 创建了关系,并获取了持久化标识,而此时Hibernate 的Session 的命周期结束,实体的持久化标识没有被改动过。
针对该实体的任何属性的修改都不会及时反映到数据库表中。

7、java有哪些集合,特点是什么?

List (有序可重复)
	arrayList : 基于动态数组的数据结构
        (有序,可重复,关注的是索引,查询速度快,默认长度10 ,扩容的长度为当前长度的1.5倍)
	linkedList : 基于链表的数据结构(增删操作较多)
        
Set (无序,不可重复)
	HashSet(不能存储重复的元素,存储顺序和取出的顺序不一定一致Set集合没有索引,默认长度16 ,当长度的负载因子达到0.75 会?自动扩容,扩容的长度为当前长度乘以*2

Map (无序,不可重复 ,查询快, 非同步的线程是不安全的 可以存null值)
	HashMap : 底层数组加链表 存储的格式是键值对 它根据键的HashCode值存储数据 
            (JDK1.8 以后当链表长度大于8时,会转成红黑数,默认长度为16 ,当负载因子达到0.75,就会自动扩容,扩容的长度为当前长度*2 ,
构造一个空的 HashMap 在把原来的HashMap 存?新的HashMap当中, 所以他存储的位置可能会有变化 
             
能否让HashMap同步?
HashMap可以通过下面的语句进行同步:
Map m = Collections.synchronizeMap(hashMap);
Hashtable (不不允许存null值,同步的,所以线程是安全的)
             
 HashSet和HashMap的区别
	*HashMap* 					*HashSet*
HashMap实现了Map接口;			HashSet实现了Set接?
HashMap储存键值对				 HashSet仅仅存储对象
使?put()?法将元素放?map中   	  使?add()?法将元素放?set中
           

7、final、finally、finalize的区别?

final : 用于声明属性,方法和类, 分别表示属性不可被更改,?法不可覆盖, 被修饰的类不可被继承
finally : 异常处理语句结构的一部分,表示总是执行. (一般用来释放资源)
finalize : Object 类的一个方法, 在垃圾收集?执行的时候回调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等.

8、object中的方法

clone() :创建并返回此对象的?一个副本(克隆)
equals():判断是否相等
finalize():垃圾回收器方法
getClass():返回类对象
hashCode():返回哈希码值
notify():唤醒在此对象等待的单个线程
notifyAll():唤醒在此对象等待的所有线程
toString():返回的对象用字符串来表示
wait():是线程等待

线程的可用状态及生命周期

1.就绪(Runnable):线程准备运行,不一定马上开始运行;
2.运行中(Running):进程正在执行线程的代码;
3.等待中(Waiting):线程处于阻塞的状态,等待外部的处理结束;
4.睡眠中(Sleep):线程被强制睡眠;
5.I/O阻塞(BlockedonI/O):等待I/O操作完成。
6.同步阻塞(BlockedonSynchronization):等待获取锁。
7.死亡(Dead):线程完成了了执?行行。

说说你对Java中反射的理理解

	反射就是在运行状态当中,对于任意的一个类,能够获取这个类的所有属性和方法,对于任何一个对象,
都能调用它的任意一个方法,这种动态获取的信息及动态调用对象方法的功能称为反射;

反射机制的优点就是可以实现动态的创建对象和编译,体现出很大的灵活性,缺点是对性能有影响.
获取字节码有三种?方法: 
	1. 根据类名: 类名.class; 2.根据对象: 对象.class; 3.根据全限定名 : Class.forName(全限定类名)

Java中的设计模式?

创建型模式(5种):工厂方法模式,抽象工厂模式,单例例模式,建造者模式,原型模式。
结构型模式(7种):适配器模式,装饰?模式,代理模式,外观模式,桥接模式,组合模式,享元模式。
行为型模式(11种):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器?模式。
    
    单例设计模式的一般定义: 一个类中只允许有一个实例;
		实现思路 : 让类的构造方法私有化, 同时提供一个静态方法去实例化这个类;
			懒汉式: 当你是使用这个对象的时候它才会创建这个对象,时间换空间(线程不不安全的)
			饿汉式: 当这个类初始化的时候就会创建好这个对象,空间换时间 (线程是安全的,推荐使用)
                
	简单?厂设计模式的一般定义: 简单工厂又称为静态工厂,由工厂对象决定创建哪一个产品对象.
		实现思路: 写一个,让他制造出我们想要的对象.
            
	适配?设计模式的一般定义: 某类继承这个适配?,从而实现我们需要实现的方法;
		实现思路:通过写一个适配?类,里面写了所有的抽象方法,但这些方法是空的,并且适配?类要定义成抽象的,
                        如果适配?类可以自己实现就没有意义了,适配?的作用,继承适配?,简化操作.

	模板设计模式的一般定义:定义一个算法骨架将具体实现交给子类去实现,
		实现思路:在类中定义一个抽象方法,具体实现交给子类去实现;

	装饰者设计模式的一般定义:就是给一个对象增加一些新功能,而且是动态的;
		实现思路: 要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例;
    

说下原?生jdbc操作数据库流程?

第一步:Class.forName()加载数据库连接驱动;
第二步:DriverManager.getConnection()获取数据连接对象;
第三步:根据SQL获取sql会话对象,有2种?方式 Statement、PreparedStatement;
第四步:执行SQL处理结果集,执行SQL前如果有参数值就设置参数值setXXX();
第五步:关闭结果集、关闭会话、关闭连接

GET和POST的区别?

① GET请求的数据会附在URL之后(就是把数据放置在HTTP协议头中),以?分割URL和传输数据,参数之间以&相连.
② GET方式提交的数据最多只能是1024字节,理论上POST没有限制,可传较大量的数据。
③ POST的安全性要比GET的安全性高。
    通过GET提交数据,用户名和密码将明文出现在URL上,
    (1)登录页面有可能被浏览?缓存
    (2)其他人查看浏览?的历史纪录,那么别人就可以拿到你的账号和密码了.
    
    
	GET和POST请求都是http的提交方式,用户通过不同的http的提交方式完成对资源(url)的操作。
            具体点来讲get一般用于获取/查询 资源信息,而post一般用于更新资源信息。
	http定义了与服务器交互的不同方法,最基本的方法有四种,分别是get,post,put,delete

        URL全称是资源描述符,我们可以这样认为:
    	一个URL地址,它用于描述一个网络上的资源,而http中的get,post,put,delete就对应着这个资源的查,改,增,删 4个操作。
            具体点来讲get一般用于获取/查询 资源信息,而post一般用于更新资源信息。

	1.get请求提交的数据会在地址栏显示出来,而post请求不会在地址栏显示出来。
		get提交,请求的数据会附在URL之后(就是把数据放置在http协议中),以?分割URL和传输数据,多个参数用&连接;
    	post提交是把提交的数据放置在http包的包体中。因此,get提交的数据会在地址栏中显示出来,而post提交,地址栏不会改变。

	2.传输数据的大小
		get请求由于浏览器对地址长度的限制而导致传输的数据有限制。
   	    而post请求不会因为地址长度限制而导致传输数据限制。

	3.安全性,post的安全性要比get的安全性要高。
	由于get方法的数据会在地址中呈现,所以可以通过历史记录找到密码等关键信息。
    

session共享怎么做的(分布式如何实现session共享)?

利用Redis做session共享 ;
	方案是重写服务?中的HttpSession和HttpservletRequest, 首先实现HttpSession接口,
重写session所有的方法,将session以hash值得方式存在Redis中,一个session的key就是sessionID, 
setAtrribute重写之后就是更新Redis中的数据,getAttribute重写之后就是Redis中的数据,等需要将 HttpSession 的接口一一实现。
        
解决方案一:基于Nginx的ip_hash 负载均衡
	其实就是对请求过来的ip地址对你的多少台可用的服务器进行取模,然后就会把你的请求通过Nginx的反向代理给分发到对应的服务器上。
(这里会把可用的服务器放到一个数组中,如果取模得到的结果是几,就把请求分到服务器数组中的下标为几 的服务器上)

        1、优点:实现简单,对应用无侵入性,无额外开销。
? ? ? ? 2、缺点:一旦某个web服务器重启或宕机,相对应的session的数据将会丢失,且其对nginx负载均衡的能力进行了弱化。

解决方案二:基于Tomcat的session复制
	这个解决方案其实就是当用户请求的时候,把产生的sessionID给复制到系统所有的服务器中,
这样就能保证当用户请求的时候从服务器A可能调用到服务器B上的模块的时候,也能保证服务B也有该用户的sessionID,这样就不会再次让用户进行再次登录操作了。也就解决问题了。
	代码实现:修个Tomcat配置文件
        1、修改server.xml中的Cluster节点;
        2、修改应用web.xml,增加节点:<distributable/>
            
    1、优点:对应用无侵入性,重启或宕机不会造成session的丢失。
	2、缺点:需要依赖支持session复制的web服务器,在数据量很大的情况下不仅占用网络资源,而且会导致延迟。只适用于web服务器比较少且session数据量少的情况。
             
解决方案三:使用Redis做缓存session的统一缓存
	这种方案呢,其实就是把每次用户的请求的时候生成的sessionID给放到Redis的服务器上。
然后在基于Redis的特性进行设置一个失效时间的机制,这样就能保证用户在我们设置的Redis中的session失效时间内,都不需要进行再次登录。

	代码实现:修改应用的配置文件
        1、增加redis client和spring session的依赖;
        2、修改web.xml,增加filter:springSessionRFilter;
		3、修改spring配置文件,在容器中注入spring session和Redis相关的bean;
        
解决方案四:
    其实还可以把session放到cookie中去,因为每次用户请求的时候,都会把自己的cookie放到请求中,
所以这样就能保证每次用户请求的时候都能保证用户在分布式环境下,也不会在进行二次登陆。

tomcat的server.xml

<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"channelSendOptions="8">

????????<Manager className="org.apache.catalina.ha.session.DeltaManager"? ? ? ? ? ? ? ? ? ? ? ? ? ? ????????????????expireSessionsOnShutdown="false"????notifyListenersOnReplication="true"????/>

? ????????<Channel className="org.apache.catalina.tribes.group.GroupChannel">

?????????????<Membership className="org.apache.catalina.tribes.membership.McastService"?

????????????????????bind="127.0.0.1"????address="228.0.0.4"? <!--保留ip,用于广播-->port="45564"

????????????????????frequency="500"

????????????????????dropTime="3000"/>

? ? ? ? ? ? <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"? ?address="auto"port="4001"

?????????????????????<!--如果是在同一台机器上的两个tomcat做负载,则此端口则不能重复-->

????????????????????autoBind="100"

????????????????????selectorTimeout="5000"

? ? ? ? ? ????????? maxThreads="6"/>

????????????<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">

????????????????<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>

????????????</Sender>

????????????<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>

????????????<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>

????????</Channel>

? ? ? ? <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/>

? ? ? ? <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>

? ? ? ? <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"? tempDir="/tmp/war-temp/"?

????????????????????deployDir="/tmp/war-deploy/"? ?watchDir="/tmp/war-listen/"? ?watchEnabled="false"/>

? ? ? ? ?<ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>

? ? ? ? ?<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>

</Cluster>

什么是jsp,什么是Servlet?jsp和Servlet有什么区别?

	jsp本质就是一个servlet,每个jsp页面都是一个servlet 的实例
	Servlet是由java提供用于开发web服务?应用程序的一个组件,用来生成动态内容
jsp和Servlet有什么区别 :
	1.jsp是html页面中内嵌的java代码,侧重页面显示 
    2.Servlet是html代码和java代码分离,侧重逻辑控制,MVC设计思想中JSP位于视图层,servlet位于控制层
	Servlet(Sever Applet),全称Java servlet,是用Java编写的服务器端程序。
    servlet是指任何实现了这个servlet接口的类。
		其主要功能在于交互式地浏览和修改数据,生成动态web内容。servlet运行于支持Java的应用服务器中。
HttpServlet重写doGet和doPost方法或者你也可以重写service方法完成对get和post请求的响应。
jsp和Servlet的相同点和不同点?
	jsp是servlet技术的扩展,所有的jsp文件都会被翻译为一个继承HttpServlet的类,也就是说jsp最终也是一个servlet。这个servlet对外提供服务。
	servlet和jsp最主要的不同点在于,jsp侧重于视图,servlet主要用于控制逻辑。
    servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML里分离开来,(servlet如果要实现HTML的功能,必须使用Writer输出对应的html)
而jsp的情况是Java和HTML可以组合成一个扩展名为.jsp的文件。

Servlet生命周期?

   servlet有良好的生存期的定义,包括加载和实例化、初始化、处理请求以及服务结束,这个生存周期由javax.servlet.Servlet接口的init,service和destroy方法表达。
       
      加载servlet的class-->实例化servlet-->调用servlet的init完成初始化-->
  	  响应请求(servlet的service方法)其中(doget,dopost方法)
      -->servlet容器关闭时(servlet的destroy方法)

JSP内置对象?

9个内置对象:
request        用户端请求,此请求会包含来自get/post请求的参数
response      网页传回用户端的回应
pageContext 网页的属性在这里进行管理
session         与请求有关的会话期
application    servlet 正在执行的内容
out                用来传送回应的输出
config            servlet 架构部件
page              jsp 网页本身
exception      针对错误网页,未捕捉的例外
    九大隐式对象	
输入/输出对象:  request   response   out
作用域通信对象: session  application  pageContext 
Servlet 对象:   page   config 
错误对象:      exception 

jsp传递值 request,session,application,cookie

jsp有哪些域对象?

(1)pageContext,在当前jsp页面有效,跳到其它页面失效
(2)request,指一次请求范围内有效,从http请求到服务?处理结束,返回响应的整个过程。在这个过程中使用forward(请求转发)方式跳转多个jsp,在这些页面里你都可以使用这个变量
(3)session,指当前会话有效范围,浏览?从打开到关闭过程中,转发、重定向均可以使用
(4)application context域-指只能在同一个web中使用,服务?未关闭或者重启,数据就有效

谈谈你对ajax的认识?

	Ajax 是一种创建交互式网页应用的网页开发技术,
	通过异步模式,提升了用户体验,优化了浏览?和服务?之间传输,减少不必要的数据往返,减少了宽带的占用.
    最大的特点是可以实现局部刷新,在不更新整个页面的前提下维护数据.

常?用的Linux命令?

创建目录和移除目录:mkdir rmdir
创建文件: touch 文件名称
打包并压缩:tar -zcvf
解压压缩包: tar -xvf
查找字符串:grep
显示当前所在目录:pwd
创建空文件:touch
编辑?:vim vi
删除:rm -rf
修改: mv 目录名称 新目录名称
动态打印日志信息:tail –f 日志文件
列出文件列表:ls 【参数 -a -l】

Mysql性能优化?

1.当只要一行数据的时候实例 limit 1
2.选择正确的数据库引擎, MyISAM 适用于一些大量查询的应用, InnoDB的写操作比较优秀
3.?用not exists代替not in
4.充分使?用索引, B-TREE 仍然是最高效的索引之中的一个
5.用 NOSQL 的方式使用 MYSQL

数据库存储过程的优点:

	1.存储过程只在创建时进行编译,以后每次执行存储过程都不需要重新编译,而一般sql语句每执行一次就编译一次,因此使用存储过程可以大大提高数据库执行速度。
	2.通常,复杂的业务逻辑需要很多条sql语句。这些语句要分别从客户机发送到服务器,当客户机和服务器之间的操作很多时,
将产生大量的网络传输,如果将这些操作放在一个存储过程中,那么客户机和服务器之间的网络传输就会大大减少,降低了网络负载。
	3.存储过程创建一次便可以重复使用,从而可以减少数据库开发人员的工作量。
	4.安全性高,存储过程可以屏蔽对底层数据库对象的直接访问,使用execute权限调用存储过程,无需拥有访问底层数据库对象的显式权限。

jdbc中preparedStatement比statement的好处

大多数我们都使用PreparedStatement代替
	1.PreparedStatement是预编译的,比Statement速度快
	2.代码的可读性和可维护性要好。
		虽然使用PreparedStatement来代替statement会使代码多出几行,但这样的代码无论从可读性还是可维护性上来说,都比直接用statement的代码高很多档次。
	3.安全性   PreparedStatement 可以防止sql注入攻击,而statement却不能
(如果使用预编译语言你传入的任何内容都不会和原来的语句发生任何匹配的关系,只要全使用预编译语言,就不用对传入的数据做任何过滤,
而如果使用普通的statement,有可能要对drop等做费尽心思的判断和过滤)

在千万级的数据库查询中,如何提高效率?

1.数据库设计方面
	a.对查询进行优化,尽量避免全表扫描
	b.应尽量避免在where子句中对字段使用null判断
	c.索引并不是越多越好,索引固然可以提高select的效率,但同时也降低了insert及update操作
	d.尽量使用数字型字段,是因为引擎在处理查询和连接时会逐个比较字符串中每个字符,而对于数字一次就可以了
	e.避免频繁创建和删除临时表,以减少系统表资源的消耗
2.语句句?方?面
	f.应尽量避免在where子句中使用!= 或<.> 操作符,或者是or来连接条件,
		否则将引擎放弃使用索引而使用全表扫描
	g.任何地方都不要使用 select * from T , 用具体的字段来代替(*), 不要返回用不到的字段
	h.用not exists代替not in
3.java方面
	i.合理利用内存,有的数据要缓存.
            
优化10 大策略
	策略1.尽量全值匹配
		当建立了索引列后,能在wherel 条件中使用索引的尽量所用。
	策略2.最佳左前缀法则
		如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列。
	策略3.不在索引列上做任何操作
		不在索引列上做任何操作(计算、函数、(自动or 手动)类型转换),会导致索引失效而转向全表扫描
	策略4.范围条件放最后
		中间有范围查询会导致后面的索引列全部失效
	策略5.覆盖索引尽量用
		尽量使用覆盖索引(指一个查询语句的执行只用从索引中就能够取得,不必从数据表中读取,),减少select *
	策略6.不等于要慎用
		mysql 在使用不等于(!= 或者<>)的时候无法使用索引会导致全表扫描,如果一定要需要使用不等于,请用覆盖索引
	策略7.Null/Not 有影响
		注意null/not null 对索引的可能影响
			1、自定定义为NOT NULL
				在字段为not null 的情况下,使用is null 或is not null 会导致索引失效
			解决方式:覆盖索引
			2、自定义为NULL 或者不定义
            	Is not null 的情况会导致索引失效
			解决方式:覆盖索引
	策略8.Like 查询要当心
		like 以通配符开头(‘%abc...‘)mysql 索引失效会变成全表扫描的操作
			解决方式:覆盖索引
	策略9.字符类型加引号
		字符串不加单引号索引失效
			解决方式:请加引号
	策略10.OR 改UNION 效率高
			解决方式:如果一定要用OR,那么使用覆盖索引            
            

内连接与外连接的区别?

内连接 : 也被称为自然连接,只有两个表相同的数据才会出现在结果集中.
外链接不仅包含符合连接条件的行, 
	1.左外链接(左边的表不加限制) 
    2.右外链接(右边的表不加限制) 
    3.全外链接(左右两表都不加限制)

HTTP与HTTPS有什什么区别?

HTTP : 是一个客户端和服务端请求和答应的标准
HTTPS : 是以安全为目的的HTTP通道,简单讲师HTTP的安全版  
区别:
	1: HTTPS协议需要到CA申请证书,一般免费的较少,因而要收取一定的费用
	2:HTTP是超文本传输协议,信息是明文传输,HTTPS则是具有安全性的SSL加密传输协议
	3:HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,HTTP : 80 HTTPS : 443
	4.HTTP连接很简单,是无状态的, HTTPS协议是由SSL+HTTP协议构建的可进行加密传输,身份验证的网络协议,比HTTP协议安全 
        
HTTP常?见的状态码有哪些
200 OK //客户端请求成功
301 Moved Permanently(永久移除),请求的URL已移走。Response中应该包含一个Location URL, 说明资源现	在所处的位置
302 found 重定向
400 Bad Request //客户端请求语法错误,不能被服务器所理解
401 Unauthorized //请求未经授权,这个状态码必须和WWW-Authenticate报头域一起使用
403 Forbidden //服务?接受到请求,但是拒绝提供服务
404 Not Found //请求资源不存在, 输入了错误URL
500 Internal Server Error //服务?内部错误
503 Server Unavailable // 服务?当前不能处理客户端请求,一段时间后可能恢复正常        

TCP和UDP区别?

TCP用于可靠传输的情况, 应用于文件传输, 重要状态更新等场景
UDP用于对高速传输和实时性要求较高的通信领域。广播
    
TCP协议
TCP协议全称: 传输控制协议, 顾名思义, 就是要对数据的传输进行一定的控制.
TCP(Transmission Control Protocol,传输控制协议)是面向连接的协议,也就是说,在收发数据前,必须和对方建立可靠的连接。    
三次握手
	第一次:
		客户端 - - > 服务器 此时服务器知道了客户端要建立连接了。 请求的SYN报文
	第二次:
		客户端 < - - 服务器 此时客户端知道服务器收到连接请求了。
            确认应答(ACK)和同步序列号(SYN)
	第三次:
		客户端 - - > 服务器 此时服务器知道客户端收到了自己的回应。 确认应答(ACK)

到这里, 就可以认为客户端与服务器已经建立了连接.
四次挥手
	第一次: 当主机A完成数据传输后,将控制位FIN置1,提出停止TCP连接的请求 ;
	第二次: 主机B收到FIN后对其作出响应,确认这一方向上的TCP连接将关闭,将ACK置1;
	第三次: 由B端再提出反方向的关闭请求,将FIN置1 ;
	第四次: 主机A对主机B的请求进行确认,将ACK置1,双方向的关闭结束.。           
             
为什么建立连接是三次握手,关闭连接确是四次挥手呢?
	建立连接的时候,服务器在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。
	而关闭连接时,服务器收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,而自己也未必全部数据都发送给对方了,
所以己方可以立即关闭,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送,从而导致多了一次。
 
	TCP协议规定,主动关闭连接的一方要处于TIME_ WAIT状态,等待2*MSL(maximum segment lifetime)的时间后才能回到CLOSED状态.            
源端口号/目的端口号: 表示数据从哪个进程来, 到哪个进程去.
32位序号:
4位首部长度: 表示该tcp报头有多少个4字节(32个bit)
6位保留: 顾名思义, 先保留着, 以防万一
6位标志位
    URG: 标识紧急指针是否有效
    ACK: 标识确认序号是否有效
    PSH: 用来提示接收端应用程序立刻将数据从tcp缓冲区读走
    RST: 要求重新建立连接. 我们把含有RST标识的报文称为复位报文段
    SYN: 请求建立连接. 我们把含有SYN标识的报文称为同步报文段
    FIN: 通知对端, 本端即将关闭. 我们把含有FIN标识的报文称为结束报文段
16位窗口大小:
16位检验和: 由发送端填充, 检验形式有CRC校验等. 如果接收端校验不通过, 则认为数据有问题. 此处的校验和不光包含TCP首部, 也包含TCP数据部分.
16位紧急指针: 用来标识哪部分数据是紧急数据.
选项和数据暂时忽略

技术分享图片
技术分享图片

Mybatis的编程步骤是什么样的?

1:创建SqlSessionFactory
2:通过SqlSessionFactory创建SqlSession
3:通过SqlSession执?行行数据库操作
4:调用session.commit() 提交事务
5.调用session.close() 关闭会话

1、 创建对象的方式有几种?

1、 new

2、 反射

3、 反序列化

4、 Clone

内存泄漏和内存溢出

	内存泄露 (memory leak),是指应用程序在申请内存后,无法释放已经申请的内存空间.
            一次内存泄露危害可以忽略,但如果任其发展最终会导致内存溢出(out of memory).如读取文件后流要进行及时的关闭以及对数据库连接的释放。
	内存泄露是指你的应用使用资源之后没有及时释放,导致应用内存中持有了不需要的资源,这是一种状态描述。
            而且通常都是由于内存泄露导致堆栈内存不断增大,从而引发内存溢出。
        
	内存溢出(out of memory)是指应用程序在申请内存时,没有足够的内存空间供其使用。如我们在项目中对于大批量数据的导入,采用分段批量提交的方式。
               指你的应用的内存已经不能满足正常使用了,堆栈已经达到系统设置的最大值,进而导致崩溃,这事一种结果描述。

解析xml文件的几种技术

1.dom4j 2.sax	3.jaxb	4.jdom 5.dom

        1.dom4j        
      dom4j是一个Java的XML API,类似于jdom,用来读写XML文件的。dom4j是一个非常优秀的Java XML API,
            具有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的软件。
 
	2.sax
        SAX(simple API for XML)是一种XML解析的替代方法。相比于DOM,SAX是一种速度更快,更有效的方法。
            它逐行扫描文档,一边扫描一边解析。而且相比于DOM,SAX可以在解析文档的任意时刻停止解析,但任何事物都有其相反的一面,对于SAX来说就是操作复杂。
 	
         3.jaxb
         JAXB(Java Architecture for XML Binding) 是一个业界的标准,是一项可以根据XML Schema产生Java类的技术。
            该过程中,JAXB也提供了将XML实例文档反向生成Java对象树的方法,并能将Java对象树的内容重新写到XML实例文档。
            从另一方面来讲,JAXB提供了快速而简便的方法将XML模式绑定到Java表示,从而使得Java开发者在Java应用程序中能方便地结合XML数据和处理函数。

2、dom4j 与 sax 之间的对比:【注:必须掌握!】
           dom4j不适合大文件的解析,因为它是一下子将文件加载到内存中,所以有可能出现内存溢出,
	   sax是基于事件来对xml进行解析的,所以他可以解析大文件的xml
	   也正是因为如此,所以dom4j可以对xml进行灵活的增删改查和导航,而sax没有这么强的灵活性
	   所以sax经常是用来解析大型xml文件,而要对xml文件进行一些灵活(crud)操作就用dom4j

final,finally,finalize 三者区别

Final是一个修饰符:
	当final修饰一个变量的时候,变量变成一个常量,它不能被二次赋值
	当final修饰的变量为静态变量(即由static修饰)时,必须在声明这个变量的时候给它赋值
	当final修饰方法时,该方法不能被重写
	当final修饰类时,该类不能被继承
    
	Final不能修饰抽象类,因为抽象类中会有需要子类实现的抽象方法,(抽象类中可以有抽象方法,也可以有普通方法,当一个抽象类中没有抽象方法时,这个抽象类也就没有了它存在的必要)
	Final不能修饰接口,因为接口中有需要其实现类来实现的方法
	
Finally:
	Finally只能与try/catch语句结合使用,finally语句块中的语句一定会执行,并且会在return,continue,break关键字之前执行
   
finalize:
	Finalize是一个方法,属于java.lang.Object类,finalize()方法是GC(garbage collector垃圾回收)运行机制的一部分,finalize()方法是在GC清理它所从属的对象时被调用的

防止表单重复提交

针对于重复提交的整体解决方案:
1.用redirect来解决重复提交的问题
2.点击一次之后,按钮失效
3.通过loading
4.自定义重复提交过滤器
5.解决struts2重复提交
可以结合s:token标签来解决重复提交问题

利用token的原理:
1.在前端的jsp页面中加入s:token标签,在访问该页面时就会生成
  隐藏域,该隐藏域中包含一个随机生成的字符串,并把该字符串
  存入session中

2.在struts2的配置文件中加入token拦截器后,当正常访问action
的时候,会从session中取出该字符串,然后和页面隐藏域中提交
字符串做对比,如果一致则正常执行并删除session中存储的字符串。
    
1.通过JavaScript屏蔽提交按钮(不推荐)
2.给数据库增加唯一键约束(简单粗暴)
3.利用Session防止表单重复提交(推荐)
4.使用AOP自定义切入实现

jsp标签

1.JSP  include动作 
jsp:include  动作
以“<jsp: 动作名 ” 开始,以“</jsp:动作名>  ” 结束
比如:<jsp:include page=" Filename" />

2.JSP指令:<%@ include%><%@   %>
    以“<%@ ” 开始,以“%> ” 结束。比如:
    <%@ include file = " Filename" %>
3.JSP输出表达式:<%= %><%=Java表达式 %>
    输出变量的值,后边不能加<%= ; %>
4.JSP Scriptlet【脚本】:<% ;%>  <% Java 代码 %>
     例子:
    <% Calendar now = Calendar.getInstance(); %>
5.JSP声明:<%! %> <%! 函数或者方法 %>
   例子:
   <%!
String getHello(String name) {
 return "Hi," + name + "!";
}
   %>
6.迭代标签:<c:foreach>
   Jstl中的核心标签(core)
7.JSP注释:
<!-- 这是注释,但客户端可以查看到 -->
<%-- 这也是注释,但客户端不能查看到 --%>
8.el表达式:${}
9.jsp:include动作是在运行时动态包含。
  @include指令是在编译时包含。
  它们两个都只能包含本项目的相关文件,不能包含其他项目的。	 
   如果要包含其他项目的文件可以使用c:import

Oracle分页

select * from (select * from (select s.*,rownum rn from student s ) where rn<=5) where rn>0

select * from teacher where order by id limit 5,4;

Java核心整理

原文:https://www.cnblogs.com/wowinsincere/p/13191723.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!