大乱炖-java基础

不记文档,白忙一场


每日课程

1> java相关 -> 设计模式
    23种名称+划分类型都过一遍,细节每日过5种
2> 

0、java相关

0、基础知识大牛

1> 基础中基础(数篇):https://blog.csdn.net/u014634338/article/details/81434327
2> ClassLoader不在面试准备之列,最好有时间写一个小Demo
3> java之注解源码(自定义注解)不在面试准备之列,最好有时间写一个小Demo

1、设计模式

详见"学习资料 -> 后端 -> java"
1> 学习地址一
    地址:菜鸟教程 -> 工厂模式
    网址:https://www.runoob.com/design-pattern/factory-pattern.html
        
2> 学习地址二
    地址:优秀博客
    网址:https://www.cnblogs.com/onetwo/p/9933417.html

2、java8的新特性

Lambda 表达式

格式:
    (params) -> expression
    (params) -> statement
    (params) -> { statements }
-------------------------------------------------------------------------------------------
场景:
    将函数当成参数传递给某个方法
-------------------------------------------------------------------------------------------
例子(匿名内部类)1:
    1> 排序:new ArrayList<>().sort(Comparator c)
    2> 迭代:new ArrayList<>().forEach(n -> System.out.println(n));
    3> 排序:Collections.sort(List list, Comparator c)
    4> 线程:new Thread( () -> System.out.println("In Java8") ).start();
-------------------------------------------------------------------------------------------
例子(配合函数式接口Predicate)2:
    filter(List names, Predicate condition) 
        if(condition.test(name)) 
    调用:
        filter(Arrays.asList("1","2"), (String str)->str.startsWith("J"));
-------------------------------------------------------------------------------------------
例子(更多):
    https://www.cnblogs.com/coprince/p/8692972.html

函数式接口(Lambda 表达式配合函数式接口,将函数当成参数传递给某个方法 )

概述:
    函数式接口,用Lambda表达式赋值,实现了将函数当成参数传递给某方法
-------------------------------------------------------------------------------------------
例子:
    1> Function T 作为输入,返回的 R 作为输出
        Function function = (x) -> {return x "Function";};
        System.out.println(function.apply("hello world"));  // hello world Function
        注:既有输入,也有输出,apply方法
    2> Predicate T 作为输入 ,返回 boolean 值的输出
        Predicate predicate = (x) ->{return x % 2 == 0 ;};
        System.out.println(predicate.test(1));              // false
        注:test方法
    3> Consumer T 作为输入 ,没有输出
        Consumer consumer = (x) -> {System.out.println(x);};
        consumer.accept("hello world ");                    // hello world
        注:消费者,只有输入,accept【接收】方法
    4> Supplier 没有输入 , R 作为输出
        Supplier supplier = () -> {return "Supplier";};
        System.out.println(supplier.get()); 
        注:提供者,只有输出,get【提供的第几个】方法
    注:你可以把其当成正常的接口使用,由用户使用 Lambda 传入

《流》

1> 概述
    1> 应用在Stream流上的操作,可以分成两种:Intermediate(中间操作)和Terminal(终止操作)。
    2> 中间操作的返回结果都是Stream,故可以多个中间操作"叠加";
    3> 终止操作用于返回我们最终需要的数据,只能有一个终止操作
    4> 使用流可以实现并行操作,不需要程序猿生成线程,只专心逻辑代码就好
    
2> 生成流的方法
    1> List/Set(Collection接口的 ):stream()或parallelStream()方法
    2> Arrays:Arrays.stream(array, from, to)
    3> 静态的Stream.of()、Stream.empty()方法
    4> 静态的Stream.generate()方法生成无限流,接受一个不包含引元的函数
    注:集合有1个,数组有1个,Stream静态方法有2个
    
3> 基础接口BaseStream包含如下方法:
    1> boolean isParallel(); //判断是否是并行化流
    2> S sequential(); //将流串行化
    3> S parallel(); //将流并行化
    4> S unordered();  //解除有序流的顺序限制,发挥并行处理的性能优势
​
4> 流的Intermediate方法(中间操作)   
    0> sorted(Comparator) 排序(将流元素按Comparator排序)
    1> filter(Predicate) 过滤(将结果为false的元素过滤掉)
    2> distinct() 去重
    3> limit(n) 保留前n个元素
    4> skip(n) 跳过前n个元素
    5> map(fun)  ===> 重要
        注:转换元素的值,可以用方法引元或者lambda表达式(python也有此功能)
​
5> 流的Terminal方法(终止操作)
    max(Comparator)
    min(Comparator)
    count()
    findFirst() 返回第一个元素
    findAny() 返回任意元素
    anyMatch(Predicate) 任意元素匹配时返回true
    allMatch(Predicate) 所有元素匹配时返回true
    noneMatch(Predicate) 没有元素匹配时返回true
    reduce(fun)   ===> 重要
    注:reduce -> 从流中计算某个值,接受一个二元函数作为"累积器",从前两个元素开始持续应用它,累积
        器的中间结果作为第一个参数,流元素作为第二个参数(python也有此功能)
        
6> 基本类型流 vs 对象流
    1> Stream、IntStream、LongStream、DoubleStream都是BaseStream的子接口
    2> IntStream、LongStream、DoubleStream直接存储基本类型,可以避免自动装/拆箱,效率会更高一些
    
7> 更多:
    https://blog.csdn.net/sanri1993/article/details/101176712
    https://www.cnblogs.com/coprince/p/8692972.html

3、深浅拷贝

相同点:
    都是实现Cloneable接口,重写clone方法
1> 浅拷贝
    只拷贝目标对象,目标对象中如果还有引用,则原来对象和拷贝生成对象都指向一个内存。
2> 深拷贝
    目标对象和目标对象中引用都进行拷贝。
注:深拷贝实现
    1、两个类都实现了cloneable接口,重写clone方法时,super.clone()返回对象,点内部引用再点
        clone()方法,将内部引用也拷贝。
    2、还有一种没有试验的方法,实现Serializable接口(这个接口的作用是实现序列化)
注:https://blog.csdn.net/riemann_/article/details/87217229

4、集合

HashMap - 源码

1> 底层数据结构
    1> jdk1.7采用数组+链表
    2> jdk1.8采用数组+链表+红黑树
2> 为什么要加链表(和红黑树),而不是直接用数组存储
    1> 因为有哈希碰撞的问题,数组中每个元素都是链表
    2> 数组查找快,插入删除效率低;链表查找需要遍历慢,插入删除直接重新指向下一节点效率高
        数组解决查询问题,链表解决插入删除问题
    3> 红黑树解决单向链表过长查询效率问题
3> 关键变量 
    1> table
        1> 类型是Entry[],是哈希表中的数组
    2> Entry
        1> 表示链表节点,jdk1.7名称为Entry,1.8改名为Node,红黑树节点为TreeNode
        2> 是静态内部类,成员变量有:key,value,hash值(整数),next(Entry对象)
    3> capacity
        1> 数组容量,初始值(定义了常量),值1<<4(1左移4位),也就是16
        2> 扩容一次,容量变为原来2倍
    4> load_factor
        1> 负载因子,初始值(定义了常量),值0.75
    5> threshold
        1> 扩容阈值,等于capacity*load_factor,数组元素个数大于等于该值,开始扩容
4> put操作
    1> 根据key计算哈希值
        1> jdk1.7中,一堆的异或和>>>运算
        2> jdk1.8中,(key==null)? 0 : (h = key.hashCode()) ^ (h>>>16)
        注:key.hashCode()先将值赋值给h变量,再将新的h值无符号右移16位,最后将两边做异或运算。
            即key的hashCode值,高16位和低16位进行异或运算。
            1010 0011 1100 1111 -> 无符号右移之后 -> 0000 0000 1010 0011
            前后做异或运算
            1010 0011 1100 1111
            0000 0000 1010 0011
            -------------------
            1010 0011 0110 1100
        注:jdk1.8运算方法,是作者的一个经验,这样做使hash值分布比较均匀,碰撞概率比较低
    ------------------------------------------------------------
        注释掉第2步:判断table变量(即HashMap的数组)是否为null,如果为null,
        则初始化HashMap的数组,大小为16,扩容阈值为12,元素类型为Node
    ------------------------------------------------------------
    3> 计算数组下标
        (n-1) & hash
        注:数组容量为n,第一步计算的哈希值为hash
        注:n-1转为二进制,不论和什么值做&运算,肯定小于n-1,保证下标就在HashMap数组容量内
    4> 判断下表所在位置是否有元素
        1> 若没有,则新建一个链表节点,放在该下标位置
        2> 若已有元素,则进行进一步判断
            1> 如果已有元素的hash值等于当前hash值且key相等或者key的equals相等,则
                还是保持原来的元素(后面代码将数组元素的value值重新赋值为当前value)
            2> 如果已有元素的类型是TreeNode,则
                将当前元素执行挂载红黑树下操作
            3> 其余情况(是链表)
                1> 循环链表所有节点
                2> 在循环中判断节点的下一节点是否为null
                3> 如果是null,就时链表的尾巴了,新建一节点,让最后一个节点的next引用指向新节点
                4> 判断如果元素个数大于等于转红黑树的一个阈值8,则执行链表转红黑树操作
                注:其余情况中,就return了,不会执行下面代码进行++size > threshold判断
                    是否容量超过扩容阈值。即挂在链表下的不算HashMap数组容量。
5> get操作
    1> 根据key计算哈希值
        1> jdk1.7中,一堆的异或和>>>运算
        2> jdk1.8中,(key==null)? 0 : (h = key.hashCode()) ^ (h>>>16)
    2> 计算数组下标
        (n-1) & hash
        注:jdk1.7和1.8计算数组下表方法一致
    3> 判断下表所在位置是否有元素,如果有则
        1> 如果hash值和当前hash值一样,且key相等或key的equal相等,则返回第一个节点对象
        2> 如果第一个节点的next属性不为null,则
            1> 第一个节点类型为TreeNode,则从红黑树取
            2> 否则,循环链表节点,比对"hash值和当前hash值一样,且key相等或key的equal相等",
                并返回值

Map - 面试

1> HashMap的数据结构
2> HashMap的hash运算是如何实现的?为什么这样实现
    1> 提高性能:异或和位移运算,对CPU更友好,减少系统开销
    2> 减少hash碰撞:通过异或和位移运算,让hash值变得更复杂,进而影响hash的分布
3> HashMap默认容量是多少?如何扩容
    1> 默认16,也可以在构造时传入capacity初始值和load_factor初始值。最大1<<30,即2的30次方
    2> 就容量左移1位。即<<1,即扩大为原来的2倍
4> HashMap的put方法的过程
    见"源码"
5> HashMap链表节点过深时,为什么选择红黑树
    优化查找数据性能:O(n) --> O(logN)
6> 什么是hash碰撞,发生碰撞怎么办
    1> 键值的hash值一样,进而计算数组下表一样,导致hash碰撞
    2> HashMap中使用的是拉链法
7> JDK1.8对HashMap做了哪些改进
    1> 链表长度大于等于8,那么链表将转换为红黑树
    2> 发生hash碰撞时,1.7在链表头部插入,1.8在尾部插入
    3> 链表节点由Entry转换为Node类(换了一个类名)
    4> hash的实现,1.8是(key==null)? 0 : (h = key.hashCode()) ^ (h>>>16);
        1.7中是一堆的异或和>>>运算
8> HashMap和HashTable的区别
    1> HashMap是线程不安全的;HashTable是线程安全的。
        因为HashTable的put和get方法直接加了synchronized。HashTable效率小于HashMap
    3> HashMap允许一个key值为null,多个value值为null;HashTable不允许。
        因为HashMap的hash方法,如果key为null,返回0,计算数组下标用(n-1)&hash肯定是0,放在0位置
    4> HashMap默认大小16,HashTable是11,扩容前者是两倍,后者是两倍+1
    5> HashMap的hash值:(key==null)? 0 : (h = key.hashCode()) ^ (h>>>16);
        HashTable的hash值:直接用key.hashCode()
9> HashMap和ConcurrentHashMap的区别
    1> ConcurrentHashMap在java.util.concurrent包下,线程安全;HashMap线程不安全。
        (1.7用分段锁,1.8用CAS+Synchronized -- 不需要记)
    2> CurrentHashMap的键值对都不允许有空值;
10> 我们能否让HashMap实现同步(线程安全)?
    调用工具类Collections的synchronizeMap方法:Map map = Collections.synchronizedMap(hashMap 

Map - 待解决

1> 来源:https://www.cnblogs.com/zengcongcong/p/11295349.html
2> HashMap在java8之后的特性
    初始数据量
    数据量扩展到多大,由数组展开为链表
3> 执行put/get方法,下表是根据(n-1)&hash计算的,也就是数组不是顺序放置节点,怎么保证数组全部
    用完再扩容。
    数组存在的空白的地方,怎么处理
4> 1<<4
    1> 位移运算符
        <<      :     左移运算符,num << 1,相当于num乘以2(num表示的二进制数左移一位,低位补0)
        >>      :     右移运算符,num >> 1,相当于num除以2(num表示的二进制数右移一位)
        >>>     :     无符号右移,忽略符号位,空位都以0补齐
    2> 来源:https://www.cnblogs.com/hongten/p/hongten_java_yiweiyunsuangfu.html
5> ^
    1> 异或,两个值相异,则结果为真。
    2> 二进制运算中,两值相等,则结果为1,不等,则结果为0。比如1^0=1
    3> 只有二进制运算有异或运算,所以十进制两个数,需要换算为二进制再进行计算
    4> 面试题
        int[] numarry = new int[]{1,2,2};
        int aim = numarry[0];
        for(int i = 1; i < 3; i++)
        {
            aim = aim ^ numarry[i];
        }
        System.out.println("最后:"+aim);
        解决思路:异或运算的结合律 -> a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c
        注:结合律这样的运算规律是数学家几代人,得出来的,记住即可。1^2^2 = 1^(2^2)=1^0=1
        来源:https://blog.csdn.net/guanminwei/article/details/104022161
6> &
    运算规则:两个数都转为二进制,然后从高位开始比较,如果两个数都为1则为1,否则为0。

集合整体框架 - Collection

来源:https://www.cnblogs.com/bingyimeiling/p/10255037.html
1> 概述:
    1> Collection派生出了三个子接口:List、Set、Queue
    2> 其中List代表了有序可重复集合,可直接根据元素的索引来访问;
        Set代表无序不可重复集合,只能根据元素本身来访问;
        Queue是队列集合;
        Map代表的是存储key-value对的集合,可根据元素的key来访问value。
2> List接口
    1> 概述:
        List接口常用的实现类有:
            ArrayList类,实现了List接口;
            LinkedList类,同时实现了List接口和Deque接口;
            Vector类,实现了List接口;
            Stack类,继承自Vector类;
    2> ArrayList类:
        1> ArrayList是一个动态数组,初始容量(10),果我们明确所插入元素的多少,最好指定一个初始
            容量值,避免过多的进行扩容操作而浪费时间、效率。
        2> 在每次向容器中增加元素的同时都会进行容量检查,当快溢出时,就会进行扩容操作。
    3> LinkedList类:
        1> 除了可以根据索引访问集合元素外,LinkedList还实现了Deque接口,可以当作双端队列来使用,
            也就是说,既可以当作"栈"使用,又可以当作队列使用
        2> LinkedList的实现机制与ArrayList的实现机制完全不同,ArrayLiat内部以数组的形式保存集合
            的元素,所以随机访问集合元素有较好的性能;LinkedList内部以链表的形式保存集合中的元
            素,所以随机访问集合中的元素性能较差,但在插入删除元素时有较好的性能
    4> Vector类:
        与ArrayList相似,但是Vector是同步的。所以说Vector是线程安全的动态数组。它的操作与
        ArrayList几乎一样
    5> Stack类:
        Stack继承自Vector,实现一个后进先出的堆栈,基本的push和pop 方法
3> Set接口
    1> 概述
        1> Set集合不允许存储相同的元素,所以如果把两个相同元素添加到同一个Set集合,则添加操作失
            败,新元素不会被加入,add()方法返回false
        2> Set接口常用的实现类有:
            HashSet类,实现了Set接口;
            LinkedHashSet类,继承自HashSet类,同时实现了Set接口;
            TreeSet类,实现了SortedSet接口,SortedSet接口继承自Set接口;
            EnumSet抽象类,实现了Set接口;、
    2> HashSet类
        1> HashSet不是线程同步的,如果多线程操作HashSet集合,则应通过代码来保证其同步。(HashSet
            是按照hash算法来存储元素的,因此具有很好的存取和查找性能。)
        2> HashSet存储原理:
            1> 位置:当向HashSet集合存储一个元素时,调用该对象的hashCode()方法得到其hashCode
                值,然后根据hashCode值决定该对象的存储位置
            2> 位置是否已有值:两个对象通过equals()方法比较返回true,两个对象的hashCode()方法返
                回值相等。HashSet会以链式结构将两个对象保存在同一位置,这将导致性能下降

5、泛型

来源:https://www.jianshu.com/p/986f732ed2f1
来源:https://www.cnblogs.com/lixuwu/p/10829368.html
1> 概述
    1> 泛型的本质是参数化类型,也就是说使用泛型类或者泛型方法,所操作的数据类型被指定为一个参数
    注:什么是参数化类型
        Collection或LinkedList
        Collection中的类型也作为了参数。
        
2> 为什么要引入泛型
    1> 在jdk1.5引入泛型之前,使用 Object 来实现通用,缺点
        1> 强制转换:每次使用时都需要强制转换成想要的类型
        2> 安全:在编译时编译器并不知道类型转换是否正常,运行时才知道,不安全
    2> 所以泛型好处
        1> 消除强制类型转换:泛型的一个附带好处是,使用时直接得到目标类型
        2> 类型安全:编译时期就可以检查出因 Java 类型不正确导致的 ClassCastException 异常
        
3> 类型的上界和下界(限定泛型类型变量)
    extends 关键字声明了类型的上界(理解为:类型都需要继承于xxx)
    super 关键字声明了类型的下界(理解为:类型都需要超越于xxx)
​
4> 通配符类型
    1> 无限制通配符 < ?>: Object 有些相似,用于表示无限制或者不确定范围的场景。
    2> 有限制通配形式:< ? super E> 和 < ? extends E> 
    3> 为了获得最大限度的灵活性,要在表示 生产者或者消费者 的输入参数上使用通配符,使用的规则就是:
        生产者有上限、消费者有下限
    4> < ? super E> 用于灵活写入;< ? extends E> 用于灵活读取
    注:泛型的参数类型还可以是通配符类型,例如 Class(参见"json字符串转对象")
    
5> 泛型的类型擦除
    1> 当编译器对带有泛型的java代码进行编译时,会将类型替换为它的上级类型,如果是没有限定的泛型参数
        类型,就会被替换为Object。所以生成的是普通的不带泛型的字节码。
    2> Java中泛型在运行期是不可见的,Java 虚拟机运行时对泛型基本一无所知。
    
6> 泛型的使用场景
    1> 当类中要操作的引用数据类型不确定的时候,过去使用 Object 来完成扩展,JDK 1.5后推荐使用泛型来
        完成扩展,同时保证安全性。
        
7> 数组中不能使用泛型
    建议使用 List 来代替 Array,因为 List 可以提供编译期的类型安全保证,而 Array 却不能。
    
8> Java 中 List 和原始类型 List 之间的区别?
    1> 在编译时编译器不会对原始类型进行类型安全检查,却会对带参数的类型进行检查
    2> 通过使用Object作为类型,可以告知编译器该方法可以接受任何类型的对象,比如String 或 Integer
    3> 你可以把任何带参数的类型传递给原始类型 List,但却不能把 List< String> 传递给接受 
        List< Object> 的方法,因为泛型的不可变性,会产生编译错误。
9> 如何获取泛型的类型
    1> 这里的Type指java.lang.reflect.Type
    2> Type superclass = genericType.getClass().getGenericSuperclass();
    
10> 一些常用的泛型类型变量
    E:元素(Element),多用于java集合框架
    K:关键字(Key)
    N:数字(Number)
    T:类型(Type)
    V:值(Value)
    
n> 使用经验(jackson定义工具类)
    import com.fasterxml.jackson.databind.ObjectMapper;
    public class JsonUtil {
        //对象转json字符串
        public String obj2Json(T clazz) {
            ObjectMapper mapper = new ObjectMapper();
            String jsonStr = null;
            try {
                jsonStr = mapper.writeValueAsString(clazz);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            return jsonStr;
        }
        //json字符串转对象
        public T json2Obj(String jsonStr,Class clzz) {//通配符类型,例如 Class
            ObjectMapper mapper = new ObjectMapper();
            T jsonObj = null;
            try {
                //{"code":"1","data":"1#123456","listenPort":"8881"}
                jsonObj = mapper.readValue(jsonStr, clzz);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return jsonObj;
        }
    }

6、JVM

概述

来源:https://www.bilibili.com/video/BV1kJ411S7Ku?p=1
来源(本地方法栈):https://www.cnblogs.com/yanl55555/p/12624519.html
购买:https://www.javaxxz.com/forum-167-1.html
    
JVM整体架构
JVM类加载器
JVM内存结构
JVM执行引擎

JVM内存结构(JVM内存模型)

注:元空间详解https://blog.csdn.net/yuan_qh/article/details/100176771
注:https://blog.csdn.net/m0_37510446/article/details/104121744
**方法区只是一规范,在不同的虚拟机中的实现是不一样的,例如永久代和元空间。
1> JVM组成结构
    由3个主要的子系统组成
    1> 类加载子系统(ClassLoader)
    2> 运行时数据区(内存结构)(Runtime Data Area)
    3> 执行引擎(Execution Engine)
2> 运行时数据区组成结构
    1> java栈
    2> 本地方法栈
    3> 程序计数器
    4> 方法区
    5> 堆
    注:方法区和堆是线程共享的,java栈、本地方法栈、程序计数器每个线程都会开辟自己独立的空间。
3> java栈组成结构
    1> 线程中每个方法都会在java栈中分配一个空间,是内存单元,叫栈帧
    2> java栈和数据结构中栈特性一模一样,先进后出
    3> 栈帧中存放方法的数据(包括:见点"4")
4> 栈帧组成结构
    1> 局部变量表
    2> 操作数栈
    3> 动态链接
    4> 方法出口
5> 程序计数器
    就是一个指针,指向用来存储指向下一条指令的地址
6> 本地方法栈
    native方法,会录入本地方法栈中,而不是java栈中。也是开辟一个栈帧
7> 动态链接
    null
8> 执行引擎中包含了垃圾回收器
​
9> 怎么从理论 -> 验证
    1> javap -c Math.class > Class.txt(字节码反编译JVM指令)
    2> jvisualvm
    
10> 垃圾回收
    1> java栈、本地方法栈、程序计数器都是"线程独有"的,线程结束,自动释放空间,不需要GC管。
    2> JVM调优,指的就是堆空间GC优化
11> 元空间呢    
    jdk1.6,有永久代,常量池在方法区
    jdk1.7,有永久代,常量池在"堆";
    jdk1.8,无永久代,常量池在"元空间"(独立于JVM,直接内存)
    注:方法区是一种逻辑概念,元空间、永久代都是jdk对这个概念的代码实现
12> 堆空间分为:年轻代(1/3)和老年代(2/3);
    年轻代分为:Eden区(8/10)、Survivor区(2/10);
    Survivor区分为:Survivor 0区和Survivor 1区
    注:minor GC 轻GC
    注:Eden就是伊甸园的意思
13> GC机制
    1> 老年代都满了,就会触发full GC
    2> STW(stop the work):
14> java是解释型语言
15> 编译器类型
    1> JIT编译器:just in time,即时编译器
    2> 字节码解释器
    3> mixed mode
16> JVM运行机制
    1> javac将java文件转为class字节码文件
    2> java运行将调用JVM的"类装载子系统"加载到方法区
17> 如果是超大对象,直接让Eden区和Surivior区都装不下。则会直接放在老年代中。
18> 国外试验数据结构的网站?

JVM类加载器

1> 类加载过程
    1> 加载 -> 链接 -> 初始化
    2> 链接包括:校验 -> 准备 -> 解析
        1> 加载:在硬盘上查找并通过IO读入字节码文件
        2> 校验:校验字节码文件的正确性
        3> 准备:给类的静态变量分配内存,并赋予默认值
        4> 解析:类装载器装入类所引用的其他所有类
        5> 初始化:对类的静态变量初始化为指定的值,执行静态代码块
2> 类加载器种类
    1> 启动类加载器:负责加载JRE的核心类库,比如jre目录下的rt.jar,charsets.jar等
        注:启动类加载器是c++写的,调用类名.getClassLoader.getClass.getName得到null
    2> 扩展类加载器:负责加载jre目录下的ext目录中的jar包
    3> 系统类加载器:负责加载ClassPath路径下的类包
    4> 用户自定义加载器:负责加载用户自定义路径下的类包
3> 类加载机制
    1> 全盘负责委托机制
    2> 双亲委派机制:
    3> 为什么要用"双亲委派机制"?
        1> 沙箱安全机制:
        2> 避免类的重复加载:
4> 查看类加载过程
    1> 类启动加参数:-verbose:class
    2> 如果是IDEA:Run -> Edit Configurations -> VM -> -verbose:class
5> 在线画图工具
    1> draw.io
    2> processon.com
6> JVM加载jar包是否会将包里所有类都加载进内存?
    1> 不会
    2> java命令是增加参数-verbose:class -> 如:java -verbose:class MathTest
    3> JVM对class文件是按需加载(运行期间动态加载),所以如果类中有对象引用,
        外部类加载 -> 连接 -> 初始化,执行到外部引用,才会开始加载内部对象类

JVM调优 - 命令

注:jdk目录下的bin目录下自带的工具
1> jinfo
    注:查看JVM相关参数
    1> 查看某线程:jinfo -flag 进程号
    2> 查看所有java信息:jinfo -sysprops 进程
2> jstat
    1> 查看堆内存使用量,加载类的数量
    2> jstat -class 进程号
    3> 垃圾回收统计:jstat -gc 进程号
    4> 堆内存统计:jstat -gccapacity 进程号
    5> 新生代垃圾回收统计:jstat -gcnew 进程号
    6> 新生代内存统计:jstat -gcnewcapacity 进程号
    7> 老年代垃圾回收统计:jstat -gcold 进程号
    8> 老年代内存统计:jstat -gcoldcapacity 进程号
    9> 元空间统计:jstat -gcmetacapacity 进程号
3> jmap
    1> 用来查看内存信息
    2> 实例个数以及占用内存大小:jmap -histo 进程号 > ./log.txt (可用jhat动态分析)
    3> 堆总体信息:jmap -heap 进程号
    4> 导出dump文件:jmp ... -> 装入jvisualvm图形界面分析,文件->装载->选地址
    注:留下案发现场 -> 启动加参数 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./(路
        径)。然后拿到dump文件,装载进jvisualvm图形界面进行分析。
4> jstack
    1> 排查死锁:jstack 进程号(中间会有线程状态等信息,最后会打印死锁信息)
    2> 远程连接jvisualvm:需要手动在项目启动时开JMX端口
        1> 启动jar程序开启配置:java -Dcomxxxxx -jar foo.jar
        2> tomcat的JMX端口配置:JAVA_OPTS=xxxx
        注:jvisualvm远程连接服务器需要在远程服务器上配置host(连接ip主机名),并且要关闭防火墙 
    3> 查找占用cpu最高的堆栈信息
        1> top命令,找到pid进程号
        2> 按H,找到tid线程号
        3> tid转十六进制
        4> 执行statck tid

JVM垃圾收集

1> 如果是超大对象,直接让Eden区和Surivior区都装不下。则会直接放在老年代中。
2> java启动加参数:
    1> 打印GC日志:-XX:+PrintGCDetails
    2> 进入老年代阈值:-XX:MaxTenuringThreshold
3> 如何判断对象可以被回收?
    1> 引用计数法:引用计数器的数量为0时(Math m1 = new Math();Math m2=m1; --> 引用计数器为2)
        如果:m1 = null; m2 = null;则垃圾回收器可以对对象进行呢回收。 
        1> 效率高,初期jdk版本使用,目前主流都不用
        2> 不能解决对象之间相互循环引用的问题
    2> 可达性算法:
        1> 通过"GC Roots"根节点,向下搜索。当一个对象没有和任何"GC Roots根节点"相连的话,就说明
            对象不可用。
        2> GC Roots根节点:类加载器、static成员、本地变量表、常量引用、本地方法栈的变量、Thread
4> finalize使用
    1> 是Object类的一个方法,保证类在垃圾回收之前完成对特定资源的回收。
    2> 可达性算法之后,还要经过再标记过程
        1> 第一次标记:查看对象是否重写了finalize方法(是Object的方法),没有就回收
        2> 有重写finalize方法,则放入一个队列中,之后会有专门线程触发该方法,但不保证方法执行完
            成。如果执行finalize方法之后,对象重新有了引用,则移出队列。没有,则被回收。
5> 垃圾回收算法
    1> 清除-标记算法
    2> 复制算法
    3> 清除-整理算法
    注:年轻代可以用复制算法;老年代可以用清除-标记算法、清除-整理算法
6> 垃圾回收器
    1> serial回收器:最开始串行
    2> parNew回收器:最开始并行
    3> parallelGC回收器:JDK1.8默认
        1> 类似parNew回收器
        2> 更大吞吐量,更高效利用CPU
    4> CMS收集器:
        1> 第一款真正意义的"并发收集器",之前的是"并行收集器"
        2> 步骤:初始标记 -> 并发标记 -> 重新标记等,目的是让STW的时间尽可能短
    5> G1回收器:JDK1.9默认(JDK1.7已出现)
        1> G1将java堆划分为多个大小相等的独立区域(Region),虽然保留新生代和老年代的概念,但不再
            是物理隔阂,它们都是不连续的区域。
        2> 新增Humongous区,分配大对象,直接进Humongous区。
        3.1> 可预测的停顿,是G1相对CMS最大的优势。内部建立了可预测的停顿时间模型,可指定垃圾回收
            在一个时间段(毫秒)内完成。
        3.2> G1收集器在后台维护了一个优先列表,每次根据允许的收集时间,有限选择回收价值最大的
            Region
        4> G1有"YoungGC"和"MixedGC"(MixedGC收集所有新生代和部分老年代);
            如果没有了region可用了,还是会产生"FullGC"
    6> ZGC回收器:JDK11默认
        回收1T以上的堆空间
    
    总结:
        1> 如果老年代->CMS,则新生代->ParNew
        2> 如果老年代->Parallel Old,则新生代->Parallel Scavenge
        3> 如果老年代->Serial Old,则新生代->Serial
        
7> 如何选择垃圾回收器
    1> 官方推荐:
        1> 新生代:Serial、ParNew、Parallel Scavenge
        2> 老年代:Serial Old、Parallel Old、CMS
        注:配合使用,怎么配合,官方有图
        3> 如果选用G1收集器可以同时用在新生代和老年代
    2> 原则:
        1> 优先调整堆的大小,让服务器自己来选择
        2> 如果内存小于100M,使用串行收集器   
        3> 如果是单核,没有停顿时间,使用串行
        4> 如果允许停顿时间超过1s,使用并行
        5> 如果响应时间最重要,使用并发

JVM调优 - 实战

1> JVM调优的两个指标
    1> 停顿时间:STW时间。 -XX:MaxCGPauseMills
    2> 吞吐量:垃圾收集时间和总时间的占比:1/(1+n),吞吐量:1-1/(1+n)。 -XX:CGTimeRatio=n
    注:-XX:+PrintGCDetails输出的PSYoungGen就是Parallel Scavenge收集器
2> JVM调优步骤
    1> 打印GC日志:-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:./test.txt
        参数(将GC日志输出到文件)
    2> 分析日志
    3> 调优JVM参数
3> 参数(使用不同GC收集器)
    1> Parallel Scavenge收集器 + Parallel Old收集器:
    2> ParNew收集器 + CMS收集器:
    3> Serial收集器 + Serial Old收集器:
    4> G1收集器:-XX:+UseG1GC
4> 分析工具
    1> gceasy(国外网gceasy.io)
    2> gcviewer(客户端)

7、Mysql相关

索引底层数据结构与算法

1> 磁盘存取原理
    1> 寻道(速度慢、耗时)
    2> 旋转(速度较快)
2> 索引使用的数据结构及原因
    1> 为什么不用"二叉树"
        1> 什么是二叉树(Binary Tree)
            二叉树是每个节点最多有两个子节点的树。
        2> 数据量大的时候,深度会很大,可能会出现不平横的情况
    2> 为什么不用"红黑树"
        1> 什么是红黑树(Red-Black Trees)
            1> 红黑树属于平衡二叉树
        2> 做了平衡,但是数据量大的时候,深度会很大(高度不可控)
        3> 每个节点下最多两个子节点,所以第二行最多2个,第三行最多4个,依次类推。大数据量,很大的
    3> 为什么不用"Hash"
        1> 定位查找,只需要把字段做hash运算,一次运算就可以知道结果
        2> 但是没有办法做范围查询
        3> MySQL索引类型包括:B+Tree和Hash
    4> 为什么不用"B树"
        1> 度(Degree):节点的数据存储个数
        2> 可以有效控制深度(度越大,深度越小)
        3> 问题:那么度(Degree)是不是设置的越大越好?
        4> 度大小的设置,磁盘和内存之间一次交互数据量,是以"页"为单位,"页"的整数倍,1"页"大
            小"4K"。MySQL会根据计算机硬件条件,设置"度"为一次交换数据量的大小。
        注:预读:磁盘一般会顺序向后读取一定长度的数据(页的整数倍)放入内存
    5> 使用"B+树"
        1> 非叶子节点不存储data,只存储key,可以增大度
            MySQL底层已经固定确定了度(Degree)的大小,所以单个数据大小,就决定了一个节点可以
            存储的数据多少
        2> 叶子节点之间有指针指向(方便范围查询)
        3> 数据会有冗余,因为非叶子节点不存储data,
        4> B Trees节点的大小设为一页,每次新建节点直接申请一个页的空间,这样就保证一个节点
            物理上也存储在一个页里,就实现了一个节点的读入只需要一次I/O
3> MyISAM索引实现(非聚集)  
    1> 索引文件和数据文件是分离的
    2> 一张表有3个文件(.fmt、.myd、.myi即format数据结构、mydata数据,myindex索引)
    3> 先到.myi文件中找到所查找数据的地址
    4> 再到.myd文件中找到对应数据
    5> primary key(主键索引)和secondary key(非主键索引)的存储方式一样
    注:二级索引,即非主键索引
4> InnoDB索引实现(聚集)
    1> 数据文件本身就是索引文件
    2> 聚集索引(主键索引) - 叶节点包含了完整的数据记录
    3> 二级索引,叶节点只包含了该字段和主键两个值
    4> 第三条解释了,为什么InnoDB表必须要有主键
    5> 二级索引,叶子节点存储主键值,是为了一致性和节省空间。(否则维护两个索引文件)
    注:二级索引,即非主键索引
    注:二级索引,先找到主键,再到主键索引中匹配主键,拿所有数据
        (因为主键索引存所有数据,非主键索引只存主键)
5> 联合索引
    每个节点中的单个数据,都是多个字段组成的,查找的时候,先比较第一个字段,如果不是,不再比较第二
    第三个字段,直接往下比较。

Explain查询

1> 各字段含义
    1> id列:
        数字越大,越先执行。如果id值相等,则从上往下执行,越靠上优先级越高。
    2> select_type列:
        1> simple:简单查询。查询不包含子查询和union
        2> primary:复杂查询中最外层的select
        3> subquery:包含在select中的子查询
        4> derived:包含在from中的子查询。(MySQL会将查询结果存放在一个临时表中,也称为派生表)
        5> union:在union中的第二个和随后的select
        6> union result:从union临时表检索结果的select
    3> table列:
        表名
    4> type列(优化时常用):
        1> 标识关联类型或访问类型(即MySQL执行引擎决定怎么查找,查找哪些范围)
        2> 效率优先级:system > const > eq_ref >ref > range > index > ALL
            1> system:表里总共就1条数据
            2> const:由条件查询到结果集就1条数据
            3> eq_ref:equal reference(主键/唯一关联)primary key或unique key索引的所有部分
                被连接使用,最多只会返回一条符合条件的记录。
                除const之外最好的连接类型。
                如:表A主键左连接表B字段,A一条对应B多条
            4> ref:相比eq_ref,关联字段(或where条件字段)不是唯一索引,而是普通索引。 
                ref因为普通索引字段,可能查出来多条字段;
                eq_ref因为主键索引,只会查出一条字段。
            5> rang:有between、in、>等
            6> index:扫描全表索引,通常比ALL要快(表字段都建了索引)
            7> ALL:全表扫描。(表中有字段没有索引,通常需要增加索引进行优化)
            注:一般来说,查询级别达到range就可以,最好达到ref
    5> ※possible_keys:
        执行引擎 分析可能会用到的索引,最终用到的索引是key列,两列可能一样可能不一样
    6> ※key列(优化时常用):
        最终用到的索引是key列
    7> ※key_len列:
        用到了几个索引(字节加起来),比如一个表联合索引的字段都是int,则int类型是4个字节。
        key_len就是8
        1> 字符串:
            1> char(n):n字节长度
            2> varchar(n):如果是utf-8,则长度3n+2。
                (2个字节,是备注字符长度的,int值)
                (如果允许NULL,则3n+2+1。多出一个字节,是char,备注是否允许为null)
    8> ※ref列:
        索引关联的字段。比如用到了联合索引中的第一个索引,右关联到一张表的主键,ref列的值就是"一张
        表的主键"。如果只是联合索引表自己使用,ref列的值会是联合索引值。
    9> rows列:
        MySQL执行引擎估计会扫描的行数。
    10> Extra列(优化时常用):
        1> 附加信息,比较复杂
        2> Using index:
            覆盖索引,查询的列被索引覆盖。比如表联合索引有3个索引字段,查找的两个字段就在联合索引
            中,就会直接从索引中拿值。如果是查询select *则还有其余不是索引的字段。就不会是
            Using index
            注:where条件中字段是索引的前导列(联合索引最左边的列),是性能高的表现
        3> Using where
            查询的列未被索引覆盖,where字段不是索引的前导列
        4> Using where;Using index:
            where条件中字段不是索引的前导列,无法利用索引查找(最左前缀),但是查找数据在索引中。
            因为通过配置,索引可以加载在内存中,可以不用到硬盘中拿数据。
        5> NULL:
            where条件是索引的前导列,但是查询字段都没有被索引覆盖。(需要回表来实现)
        6> Using index condition:
            where条件中是一个前导列的范围,查询列不完全被索引覆盖
            如:where file_id > 10
        7> Using temporary:
            一些语句,比如select distinct,MySQL执行引擎会建一张临时表,效率较低。
            如果distinct字段有索引,被索引覆盖,就不会建临时表
        8> Using filesort:
            order by语句的字段,没有索引的情况。
            如果有索引(索引是排好序的数据结构B+Tree底层已经排序了),则Using index
2> explain extended
    1> 语法:select语句前加explain extended,第二行写show warnings;
    2> 结果:MySQL引擎分析之后,查询优化,得到的最终执行SQL(复杂sql是没办法优化的)
3> 最左前缀原则
    注:和select * from db.table_1 order by col1,col2,col3 desc一模一样。
        就相当于把上述sql查询结果提前放在了B+Tree结构中。
        所以where col2=,col3=或者where col1=,col3=都不会使用索引。
        因为抛开col1,col2的顺序就是乱的。
    注:只要包含第一个索引,且其余索引连续,比如一共3个索引,where key1=,key2,则内部也是会进行优
        化的。也会使用索引。
        但是如果没有包含第一个索引,或者使用了第一个索引但是中间有隔开的,都不能使用索引。
4> 最佳实践
    1> 全值匹配
        联合索引3个字段,则where col1=,col2=,col3=
        顺序随便写,MySQL执行引擎会进行优化,顺序不影响引用的使用。
    2> 最左前缀原则
        详见第三点
    3> 不在索引上做任何操作(会导致索引失效)
    4> MySQL执行引擎不能使用索引中范围条件右边的列
        比如第二个字段用了范围操作,第三个索引就用不上了。它是把第二个索引符合的都搂出来,再循环第
        三个字段,已经用不到索引了。
    5> 尽量使用覆盖索引
        反例:select * from table_1 where col1=,col2=,col3=,则是Using index condition
            因为如果索引在内存中,虽然col1,col2,col3是联合索引,顺序也对,但是找到之后,B+Tree
            中存储的是索引字段+主键,还需要到主键索引表中,拿数据。
        正例:Using index
    6> 索引字段使用!=或者<>
        联系B+Tree底层结构,没办法走索引。
        也会导致全表查询
    7> 索引字段使用is null或者is not null
        联系B+Tree底层结构,没办法走索引。
        也会导致全表查询
    8> like加%通配符
        1> like '%xx',不走索引
            优化方法:把select * 改为select col1,col2,col3,则Using where;Using index:
        2> like 'xx%',会走索引
        注:联系B+Tree底层结构
    9> 字符串不加''单引号,索引失效
    10> 少用or,索引会失效
        少用or或in,用它时,非主键字段的引用会失效,主键索引有时失效,有时失效,和数据量有关。

实战

1> 表有3个索引,where后面3个字段都有,顺序不按联合索引的字段顺序,会使用索引吗
    答:会,mysql执行引擎会做优化。而且只用两个索引,只要有第一个索引,即where 1,2或者where 2,1
2> where col1=,col2>,col3=,则col2会使用索引吗,col3呢
    答:col2会,col3不会。
3> where c1=,c2=,c4>,c3=,哪些字段会走索引?
    答:都会走。即第一点和第二点综合起来。MySQL执行引擎会优化顺序,c3到c4前,而且范围会走索引。
4> where c1>,c2=,c3=,哪些字段会走索引?
    答:都不走。很奇怪,记住结论吧。(第一个索引就用范围)
5> 是否where字段是索引,就一定会走索引
    答:不是。mysql会优化,如果觉得走索引还没有全表查询快,就不走索引
===========================================================================================
注:where中索引用以过滤,order by中索引用以排序(都是利用B+Tree已排序好的数据结构)
注:如果没有使用索引,Extra列会有Using filesort值
注:mysql有两种排序,自带的filesort排序,还有是index索引排序
​
6> order by
    题:where c1=,c2,c4 order by c3,则哪些字段会走索引?
    答:c3会,c4不会。索引都帮忙排好序,mysql肯定不会重新搞一个filesort文件去排序。
        但是c4不会,因为"精确定位已经断掉了",只能是先根据c1,c2精确定位,再排序,再循环找出c4
7> order by
    题:where c1=,c2= order by c4,则哪些字段会走索引?
    答:c4不会,因为最左前缀原理
8> order by
    题:where c1=,c4= order by c2,c3,则哪些字段会走索引?
    答:会
9> order by
    题:where c1=,c4= order by c3,c2,则哪些字段会走索引?
    答:c2和c3不会,因为最左前缀原理
10> order by
    题:where c1=,c2= order by c2,c3,则哪些字段会走索引?
    答:会
11> order by
    题:where c1=,c2=,c5= order by c2,c3,则哪些字段会走索引?
    答:会
12> order by
    题:where c1=,c2=,c5= order by c3,c2,则哪些字段会走索引?
    答:会。因为c2已经是固定值了,mysql经过了优化估算。
13> order by
    题:order by c1 asc,c2 desc,则哪些字段会走索引?
    答:不会。因为B+Tree是联合起来做了升序,一升一降已经无法满足了
===========================================================================================
注:group by底层是先对group by后字段进行order by
注:如果没有使用索引,Extra列会有Using temporary(当然也会有Using filesort)值
注:where的性能高于having,能在where中限定条件就不要去having中限定
​
13> group by
    题:where c1=, c4= group by c2,c3,则哪些字段会走索引?
    答:会。
14> group by
    题:where c1=, c4= group by c3,c2,则哪些字段会走索引?
    答:不会。因为底层会先排序。会产生Using filesort,Using temporary,极度恶劣
15> group by
    题:where c1> group by c1,则哪些字段会走索引?
    答:不会。第一个字段要避免范围查询。
16> group by
    题:where c1 in(,) group by c2,c3,则哪些字段会走索引?
    答:不会
===========================================================================================
17> in和exists区别
    1> in里面有数量限制
        之前在oracle测试过,超过500好像,版本忘了,一条数据都查不出来。
        用exists代替解决了
    2> 规则就是"小表驱动大表"
        1> select * from table where col in(),先执行in里面子查询,需要子查询是小表,外面大表
        2> select * from table where exists (select 1),先执行外面,需要小表,exists里是大表

1> 锁分类
    1> 性能分:乐观锁和悲观锁
    2> 对数据库操作:读锁和写锁
    3> 对数据操作粒度:表锁和行锁
2> 读锁(共享锁)、写锁(排他锁)
    1> 读锁,会阻塞写,不会阻塞读
        注:共享锁 - 用来数据迁移,锁起来,都不能写,都可以读。
            只能读,不能写(包括自己和其他session都不能写,自己报错,其他阻塞)
    2> 写锁,会把读和写都阻塞
        注:排他锁 - 用来增删改查前,锁起来;
            锁起来,只能自己读+写(其他session读、写都不能做)
3> 表锁(偏读 - 打开表,尽量批量读)   
    注:MyISAM引擎偏向于表锁
    1> 手动给表加锁(读锁/写锁)
        lock table xxx read
        lock table xxx write
    2> 查看加过锁的表
        show open tables
    3> 删除表锁
        unlock tables
    注:读锁常用在数据迁移,给表加上锁,可以读,但是自己和其他session都不能写。
    注:MyISAM在执行SELECT语句时,会给涉及到的表加读锁;执行增删改查前,会给涉及的表加写锁。
4> 行锁(偏写 - 逐行写,逐行关)
    注:InnoDB引擎偏向于行锁
    注:行锁支持事务
    1> 开启事务
        begin;
    2> 关闭事务
        commit;
    3> 回滚事务
        rollback;
    注:一个session开启事务,update一行数据,没有commit之前,会给该行数据加行锁。
        其他session不能update这行数据,阻塞。但是不影响update其他行数据。
5> 事务(事务特性+并发中事务出现问题)
    1> 事务特性:
        ACID,即原子性、一致性、隔离性、持久性
    2> 事务让并发变为可能(但是仍会出现问题)
        脏读、幻读、不可重读、更新丢失
        注:脏读,为什么一个session没有提交的数据,另一个session会读到。
            因为有"隔离级别"的不同。
        1> 脏读:一个事务读取了另一个事务还没有提交的数据
        2> 不可重复读:一个事务读取了另一个事务已经提交的修改数据
        3> 幻读:一个事务读取了另一个事务提交的新增数据
6> 事务隔离级别(mysql如何机制解决并发事务问题)
    注:事务隔离级别底层可以帮我们解决脏读、幻读、不可重复读的问题(都是读一致性问题)
    1> 不同的事务隔离级别
        隔离级别    脏读  不可重复读   幻读
        读未提交    可能  可能          可能
        读已提交    不可能 可能          可能
        可重复读    不可能 不可能         可能
        可串行化    不可能 不可能         不可能
    2> 命令
        1> 查看当前事务隔离级别:select @@tx_isolation from dual;(tx-transaction)
        2> 设置新的事务隔离级别:set tx_isolation='read-uncommitted'(比如设置读未提交)
    3> 可重复读(MySQL默认隔离级别)
        1> 现象:字段原来是450,两个事务同时开启,A事务update字段为400,B事务读字段还是450;
            但是B事务update col=col-50,结果却变为350。
            注:可以这么记 - 可重复读,强调的是"读"是没问题的,没有说可以用来"写"。
        2> 解释:可重复读隔离级别下使用了MVCC(multi-version concurrent control)机制,
            select操作不会更新版本号,是读快照(历史版本);insert/update/delete会更新版本号,
            是当前读(当前版本)
        3> 所以更新"库存"sql应写成update table set balance=balance-10。而不能先在代码中计算出
            减法之后,再直接update减后的数字。
        4> 现象:一共4条数据,事务A插入一条数据,事务B去select,结果还是4条。(因为读的是快照)
            但是事务B去update事务A新插入的数据,是可以成功的。(因为MVCC机制,和幻读没有解决)
    4> 可串行化(Serializable)
        已经没有并发了,一个事务完了,在开始另一个事务。这压根没有效率
    5> 解决幻读(非代码级别,用MySQL解决)
        间隙锁:通过范围去加锁,即使记录不存在,也会预先加上锁。
        共5要记录,事务A更新id=5:update table set col = where id>4 and id<=20
        事务B执行insert语句:insert into values (6,'',''),会等待阻塞,超时之后报错。
7> 死锁
    session 1:begin; 开启事务
    session 2:begin; 开启事务
    
    session 1:select * from table where id=1 for update; 第一行加锁
    session 2:select * from table where id=2 for update; 第二行加锁
    session 1:select * from table where id=2 for update; 第二行加锁(阻塞,等待s2放锁)
    session 2:select * from table where id=1 for update; 第一行加锁(阻塞,等待s1放锁)
    结果:第四行命令执行完,立马报错
        Deadlock found when trying to get lock; try restarting transaction
    解释:两个事务相互等待时,会发生死锁
    
    命令:杀掉死锁
        v$locked_object
        v$session

8、Spring相关

预备

1> 备注:
    1> SqlSessionFactoryBean
    2> 注入方式:https://www.cnblogs.com/icehand1214/p/9708174.html
    3> idea创建spring项目:https://www.cnblogs.com/chenyulin/p/creatSpringproject.html
2> 快捷键:
    1> IDEA生成图谱:
        ctrl + alt + u,详细图谱 ctrl + alt + shift + u 
        来源:https://www.cnblogs.com/deng-cc/p/6927447.html
    2> IDEA在debug时evaluate:
        alt + f8
    3> 查找接口的实现类:
        ctrl+h
    4> 查看类中所有方法
        ctrl + f12
        注:过滤结果搜索,不需要ctrl+f等,直接按键盘即可。
    5> 全局搜索
        edit -> find -> find in path
        ctrl + shift +f
    6> 条件断点
        

IoC容器设计理念

前提: 
    "IoC中如果没有依赖注入",那这个框架就只能帮我们构建一些简单的Bean,而之前所说的复杂Bean
    的构建问题将无法解决,Spring框架就不可能像现在这样成功。
1> 没有Spring的时候,怎么创建"依赖对象"
    1> 外部new出来,传入调用对象
        1> 构造方法传入
        2> 属性set方法传入
        3> 调用方法时,动态传入
    2> 内部自己new出来
        1> 属性赋初始值直接new
        2> 初始化方法中直接new
        3> 其他业务方法中直接new
    比如:外部new
    ===================
    Test t = null;
    public Test2 (Test t) {
        this.t = t;
    }
    ===================
    Test t = new Test();
    Test2 t2 = new Test2(t);
2> 为什么使用Spring  
    注:如果是一个简单的对象,用不用Spring没有太大区别。
    传统方法
        Goods water = new Water();
        Train train = new Train(water);
    Spring方法
        context.getBean("train");
    1> 来源:
        https://zhuanlan.zhihu.com/p/70147855
        基于POJO的轻量级和最小侵入性编程;
        通过依赖注入和面向接口实现松耦合;
        基于切面和惯例进行声明式编程;
        通过切面和模板减少样板式代码。
    2> 解释:
        1>================================================================================
        没有Spring的时候,每个对象负责管理着依赖的对象引用
        注:因为Water水和coal煤都可以实现Goods物品接口,都实现doSomthing方法
        public class Train implements Transport{
            private Goods goods;
            public Train() {
                goods = new Water();
            }
            public void catchGoods(){
                goods.doSomthing(); //水的话,是用来浇灌庄稼
            }
        }
        注:上面创建"依赖对象"方法是 - "内部自己new出来"。这样就造成这两个对象的紧耦合,这个火车
            可以运水来浇灌农田,但是如果让这个火车来运煤供暖,可能就不太符合了。
            因为Water水和coal煤都可以实现Goods物品接口,如果想运送煤炭,就需要修改代码。
        
        2>================================================================================
        所以,为了解决紧耦合,可以采用创建"依赖对象"另一种方法,"外部new出来,传入调用对象"
        public class Train implements Transport{
            private Goods goods;
            public Train(Goods goods) {
                this.goods = goods;
            }
            public void catchGoods(){
                goods.doSomthing();
            }
        }
        注:外部创建对象 Goods coal = new Coal();
            外部对象传入 Train train = new Train(coal);
            或者
            外部创建对象 Goods water = new Water();
            外部对象传入 Train train = new Train(water);
            实现了"松耦合"
        
        3>================================================================================
        但是,虽然实现了松耦合,还有问题。(利用"外部new出来,传入调用对象")
        如果一个对象具有"复杂的依赖关系",在调用对象的时候,就需要在代码中写繁琐的创建对象代码。
        比如A依赖B,B依赖C,C依赖D
        创建A对象:
        D d = new D();
        C c = new C(d);
        B b = new B(c);
        A a = new A(a);
        注:需要代码来维护"复杂的依赖关系"
        所以,可以使用Spring的"依赖注入",让Spring来管理复杂的依赖关系,调用时从上下文对象中拿出
        即可。
    3> 实体Bean的构建
        1> 基于Class构建(无参构造器)
        2> 构造方法构建(有参构造器)
        3> 静态工厂方法构建(工厂方法创建对象)
        4> FactoryBean构建(实现FactoryBean方法,不是有构造器/工厂类创建对象的情况)
        ===========================================================================
        1> 基于Class构建:
            
            注:底层基于class属性,反射进行构建(无参构造器)
        2> 构造方法构建:
            
                
                
            
        3> 静态工厂方法构建
            
                
            
        4> FactoryBean构建
            注:bean配置和普通bean一样
            class类实现FactoryBean接口
3> 依赖注入
    1> 概述
        试想IoC中如果没有依赖注入,那这个框架就只能帮我们构建一些简单的Bean,而之前所说的复杂Bean
        的构建问题将无法解决,Spring框架就不可能像现在这样成功。
    2> 依赖注入的方式
        1> set方法注入
        2> 构造方法注入
        3> 自动注入(byName,byType)
        ===========================================================================
        1> "通过set方法"注入
            
            
                
            
            注:底层基于class属性,反射进行构建(有参构造器)
            
        2> "通过构造器方法"注入
            
                
            
            
        3> 自动注入
            
            
            
            public class HelloSpring {
                private HelloIoc helloIoc;
            }
            
            
            
            public class HelloSpring {
                private HelloIoc helloIoc;
            }
            注:byName是根据class中属性名,和xml中id和name想匹配知道,并自动注入的。
            注:还有一种是default,需要标签加属性default-autowired
        注:
            1> set注入,需要property标签,类中需要set/get方法
            2> 构造器注入,需要constructor-arg标签,类中需带参要构造器
            3> 自动注入,xml和class都不需要配置。Spring自动判别
4> bean的基本特性
    1> 作用范围
        1> 类型有:prototype(getBean获取到的是多例)、singleton(getBean获取到的是单例)
        2> singleton的话,会缓存在IoC的容器中
        3> "bean标签中的属性":
        注:
            @Controller的Bean默认是单例的,所以避免加属性成员变量
    2> 生命周期
        1> Bean对象的创建、初始化、销毁就是Bean的生命周期。
        2> "bean标签中的属性":init-method、destroy-method指定初始化和销毁时调用的方法
    3> 加载机制
        1> 懒加载:使用的时候,才创建Bean
        2> 非懒加载:容器启动时即创建对象
        3> 懒加载容器启动更快,非懒加载可以在容器启动时更快发现程序中的错误。
        4> "bean标签中的属性":
        注:
            当scope="prototype" (多例)时,默认以懒加载的方式产生对象。
            当scope="singleton" (单例)时,默认以非懒加载的方式产生对象。

源码学习-IoC

注:学习目标
    1> Bean工厂如何生产Bean
    2> Bean依赖关系由谁来解决
    3> Bean工厂和应用上下文的区别
1> Bean工厂如何生产Bean   
    注:目标
        1> xml配置的bean转变为哪个Class对象
        2> 把bean标签变为Class对象,是谁来完成的
        3> 这些Class对象都保存在了哪儿
    1> xml配置的bean转变为哪个Class对象
        1> BeanDefinition(Bean定义)
            1> 概述:
                xml的Bean信息最后都将保存在BeanDefinition对象中,
                每个Bean创建一个BeanDefinition对象。
            2> xml和BeanDefinition属性对应关系
                XML-Bean        BeanDefinition
                class           beanClassName
                scope           scope
                lazy-init       lazyInit
                constructor-arg ConstructorArgument
                property        MutablePropertyValues
                factory-method  factoryMethodName
            3> id属性作为Bean的key注册到BeanDefinitionRegistry注册器中。
                name属性作为别名key注册到AliasRegistry注册中心。
                最后都是指向其对应的BeanDefinition。
        2> BeanDefinitionRegistry(Bean注册器)
            1> "注册进去"
                id放在了BeanDefinitionRegistry接口中,如下方法:
                    void registerBeanDefinition(
                        String beanName, BeanDefinition beanDefinition)
                第一个参数,就是id值
                第二个参数,就是Bean的承载对象
            2> "取出来"
                所以,一个id只有一个Bean,一个Bean可以有多个id还是注册类中,我们可以通过id获取
                到BeanDefinition对象:
                    BeanDefinition getBeanDefinition(String beanName)
                注:name属性值放在了接口AliasRegistry中,即注册接口继承的接口
        3> BeanDefinitionReader(Bean读取、创建、注册总操控)
            代码模拟Spring构建Bean
            public static void main(String[] args) {
                //注册中心
                BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
                //读取器
                XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);
                //资源加载器
                DefaultResourceLoader loader = new DefaultResourceLoader();
                Resource resource = loader.getResource("spring-cfg.xml");
                //装在构建Bean
                reader.loadBeanDefinitions(resource);
​
                //所有构建出来的Bean
                String[] allBeanDefinitions = registry.getBeanDefinitionNames();
                System.out.println(Arrays.toString(allBeanDefinitions));
                //注册器中通过拿name别名
                registry.getAliases("helloWorld");
                //注册器中通过id拿Bean
                registry.getBeanDefinition("helloWorld");
            }
            注:BeanDefinition对象只能通过id来获取
    2> 把bean标签变为Class对象,是谁来完成的
        概述:有了bean定义就相当于有了配方,现在缺工厂来进行加工生产出Bean
        
2> Bean依赖关系由谁来解决

Spring事务

1> Spring事务特点
    1> 在数据库事务基础上进行封装扩展
    2> 加入事务传播的概念
    3> 提供声明式事务,让业务代码和事务分离
2> Spring提供API实现事务控制
    1> TransactionDefinition(事务定义)
    2> PlatformTransactionManager(事务管理)
    3> TransactionStatus(事务运行时状态)
    4> 示例代码(Spring提供事务使用接口):
    注:spring提供了@transactionTemplate 方法级别的事务注解,使用方便
        但是 方法级别的事务,粒度不够细。
        因此 可以使用spring提供的TransactionTemplate 工具类来控制细粒度事务。
    public static void main(String[] args) {
        //创建TransactionTemplate
        final DataSource ds = new DriverManagerDataSource(url, username, pwd);
        TransactionTemplate template = new TransactionTemplate();
        template.setTransactionManager(new DataSourceTransactionManager(ds));
​
        //使用TransactionTemplate
        template.execute(new TransactionCallback() {
            public Object doInTransaction(TransactionStatus status) {
                Connection conn = DataSourceUtils.getConnection(ds);
                try {
                    PreparedStatement ps = conn.prepareStatement(
                        "insert into dushu values (?,?,?,?)");
                    ps.setInt(1,1);
                    ps.setString(2,"淳熙严州图经");
                    ps.setString(3,"宋·陈公亮");
                    ps.setString(4,"暂缺简介...");
                    ps.executeUpdate();
                    int i = 1/0;
                } catch (Exception e) {
                    status.setRollbackOnly();
                    System.out.println("回滚操作:"+e.getMessage());
                }
                return null;
            }
        });
    }   
    注:通过Connection conn = DataSourceUtils.getConnection(ds);后去链接是同一个
3> 声明式事务
    1> 概述:
        第三点通过调用API来实现对事务的控制,非常繁琐,与直接操作JDBC事务没有多大改善,所以Spring
        提出了声明式事务。让业务代码和事务分离,不需要程序猿关心事务
    2> 示例:
        配置文件中,配置bean - DataSourceTransactionManager,这个类实现了
        PlatformTransactionManager接口,所以有事务提交和回滚的方法。
        最重要:添加事务的注解驱动
            
        注:txManager就是DataSourceTransactionManager这个Bean
4> 事务传播机制
    注:这些定义,都是占在内层方法,即被调用方法的出发点来定义的。
        比如required,就是内层方法发现外层方法已有事务,则加入该事务,如果发现没有,就只给自己创
        建一个新事务:Creating new transaction with name(内层事务创建)
    1> 类别和特性
        类别          事务传播类型          说明
        支持当前事务    required(必须的)      先探测事务,若已有,则加入,若没有,为自己新建一个
                      supportes(支持)      先探测事务,若已有,则加入,若没有,以非事务执行
                      mandatory(强制)      先探测事务,若没有,否则报错
        不支持当前事务   requires_new(隔离)   无论如何,先新建事务,若已有,则挂起,完成后resume
                      not_supported(不支持)无论如何,非事务执行,若已有,则挂起,完成后resume
                      never(强制非事务)     无论如何,非事务执行,若已有,则挂起,就抛异常
        套事务         nested(嵌套事务)      若已有,则在嵌套事务内执行
    2> 测试示例
        不要看视频的,看如下博客:https://blog.csdn.net/qq_26323323/article/details/81908955
        自己做示例,外层required,内层not_supported
        1> Creating new transaction with name(外层事务创建)
        2> Executing prepared SQL statement [insert into dushu (执行外层操作)
        3> Suspending current transaction (内层挂起外层事务)
        4> Executing prepared SQL statement [insert into dushu (执行内层操作)
        5> Resuming suspended transaction after completion of inner transaction(还原事务)
        注:所以,如果内层报错(在update之后报错),内层插入数据库成功,外层失败回滚。
    3> 引出声明式事务的底层原理
        示例:如果外层required,内层required_new,外层报错。
            本来结果应该是,内层成功,外层回滚。
            但如果内外层方法,都放在一个类中,外层调用内层,结果却是:都失败
            引出:声明式事务底层原理是,通过aop动态代理,内部相当于this.add()不会动态代理 
   

SpringMVC

1> 组件初始化
    DispatcherServlet类中的initStrategies方法中
2> HandlerMapping
    1> 目前主流的三种mapping
        1> BeanNameUrlHandlerMapping
        2> SimpleUrlHandlerMapping  
        3> RequestMappingHandlerMapping
    2> 在IoC中实例化这些类之后,DispatherServlet就会通过getHandler方法查找对应Handler,但是返回
        的是一个Object类型,还需要适配器adaptor适配这个Object调用哪种handler。
3> HandlerAdaptor
    1> spring mvc通过适配器模式来适配调用指定的Handler
    2> Handler和HandlerAdaptor对应关系如下:
        Handler类型       对应适配器                       描述
        Controller      SimpleControllerHandlerAdaptor      标准控制器,返回ModelAndView
        HttpRequestHandler  HttpRequestHandlerAdaptor       业务自行处理,不需返回ModelAndV
        Servlet         SimpleServletHandlerAdaptor         基于标准的Servlet处理
        HandlerMethod   RequestMappingHandlerAdaptor        基于@RequestMapping对应方法
注:
    源码较简单,后面一定要看一下

SpringAOP

1> 概述
    aop是要实现的目的,SpringAOP是它的一种实现。
2> 应用场景
    1> 日志记录
    2> 权限验证
    3> 效率检查
    4> 事务管理
3> SpringAOP底层原理
    1> JDK动态代理
    2> CGLIB代理
4> SpringAOP和AspectJ关系(使用)  
    注:SpringAOP提供两种,基于xml和基于注解
    1> 加入jar包aspectjweaver
    2> 打开aspectj自动代理(xml配置或者写java配置类:@Configuration和@EnableAspectJAutoProxy)
    3> 定义Aspect(@Aspect切面,必须交给IoC容器管理)
    4> 定义PointCut(@PointCut切点,springel有好多种写法)
    5> 定义Advice(@Before/@After通知)
5> 源码分析
    1> 一个类,没有实现接口,则IoC容器中保存的是类名.class,
        UserServcieImpl userServcieImpl = context.getBean(UserServcieImpl.class);
        userServcieImpl.buyService(); 
        注:UserServcieImpl是类,发现userService类型CglibAopProxy
    2> 一个类,实现了接口,则IoC容器中保存的是接口名.class,
        UserServcie userServcie = context.getBean(UserServce.class);
        userServcie.buyService(); 
        注:UserServce是接口,发现userService类型JdkDynamicAopProxy@2662
    注:因为Jdk动态代理,底层已经继承了Proxy类,所以只能用过实现接口来完成代理。
    3> 关键类
        1> 单例对象转变为"代理类对象"
            AbstractBeanFactory.java:245
            Object sharedInstance = getSingleton(beanName);
        2> 多例对象转变为"代理类对象"
            AbstractBeanFactory.java:330
            prototypeInstance = createBean(beanName, mbd, args);

Spring5新特性

9、MyBatis相关

10、StringBuilder

https://www.cnblogs.com/shamao/p/10942203.html
    
​
1> 可变与不可变
    String是不可变字符串对象,StringBuilder和StringBuffer是可变字符串对象(其内部的字符数组长度
    可变)。
​
2> 是否多线程安全
    String中的对象是不可变的,也就可以理解为常量,显然线程安全。StringBuffer与StringBuilder中的
    方法和功能完全是等价的,只是StringBuffer中的方法大都采用了synchronized关键字进行修饰,因此是
    线程安全的,而StringBuilder没有这个修饰,可以被认为是非线程安全的。
​
3> 执行效率
    StringBuilder > StringBuffer > String
    当然这个是相对的,不一定在所有情况下都是这样。比如 String str = "hello" + "world"; 的效率就
    比 StringBuilder builder = new StringBuilder().append("hello").append("world"); 要高。
​
4> 因此,这三个类是各有利弊,应当根据不同的情况来进行选择使用:
    当字符串相加操作或者改动较少的情况下,建议使用String这种形式;
    当字符串相加操作较多的情况下,建议使用StringBuilder,如果采用了多线程,则使用StringBuffer。
    
注:
    1> String类其实是通过char数组来保存字符串的。所有对String类的操作都不是在原有的字符串上进行
        的,而是重新生成了一个新的字符串对象
    2> 每当我们创建字符串常量时,JVM会首先检查字符串常量池。如果该字符串已经存在常量池中,那么就直
        接返回常量池中的实例引用。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池
        中,同时返回对该实例的引用

11、数据结构

1> 大纲:
    https://blog.csdn.net/yeyazhishang/article/details/82353846#2%E6%A0%88
    1、数组
        数组的大小固定后就无法扩容了
    2、栈
        先进后出(入栈、出栈)
    3、队列
        先进先出(入队、出队)
    4、链表
        数据域 + 指针域
    5、树
        在日常的应用中,我们讨论和用的更多的是树的其中一种结构,就是二叉树
    6、散列表
        散列表,也叫哈希表
    7、堆
        可以被看做一棵树的数组对象
        因为堆有序的特点,一般用来做数组中的排序,称为堆排序。
    8、图
        图是一种比较复杂的数据结构,在存储数据上有着比较复杂和高效的算法
1> 红黑树特性
    https://zhuanlan.zhihu.com/p/31805309
    1> 二叉查找树的3个特性
    2> 红黑树除了具有二叉查找树的特性,还具有的3个特性(节点是红色/黑色、根节点是黑色不算)
    3> 增加节点导致打破平衡,调整的2种方法

12、注解

java实现自定义注解
来源:https://blog.csdn.net/zt15732625878/article/details/100061528

13、内存机制

注:详见"JVM",下面内容不需看
https://www.jianshu.com/p/8624e27ae45d
https://www.cnblogs.com/shamao/p/10938028.html

14、ClassLoader类装载器

注:详见"JVM",下面内容不需看
来源:https://segmentfault.com/a/1190000008491597
大牛:https://blog.csdn.net/hupoling/article/details/90938342
面试:写一个自定义的类加载器 + 哪些情况需要自定义类加载器
-------------------------------------------------------------------------------------------
1> 为什么要自定义ClassLoader
    因为系统的ClassLoader只会加载指定目录下的class文件,如果你想加载自己的class文件,那么就可以自定
    义一个ClassLoader
2> 如何自定义ClassLoader
    

15、native本地方法

1> 因为你会c语言,肯定会问你native方法
    java语言和c语言交互的方法
2> JNI是Java Native Interface的缩写,中文为JAVA本地调用
3> DLL的全称是Dynamic Link Library,中文叫做“动态链接文件”
4> JNIEXPORT和JNICALL宏定义:
    https://blog.csdn.net/shulianghan/article/details/104072587
    window平台:
        方法返回值前加:JNIEXPORT ,方法名前加:JNICALL 
4> java调用c接口:
    https://blog.csdn.net/WeiHao0240/article/details/99568579
    注:window和linux具体实现不太一样
注:  
    "注意":写一个Java类是为了生成.h和.c的格式
        其中,java的类名,包名,方法名,都必须和项目中java类保持一致
    1> 如何把c文件编译成dll
        gcc -shared -o demo.dll demo.c
    2> java调用c(单独java文件)
        https://blog.csdn.net/u011181989/article/details/92242435
        生成so文件不需要,直接用第一点,生成dll文件
        还需要注意一点,找jni文件,https://blog.csdn.net/WeiHao0240/article/details/99568579
        还需要注意一点,java -d 和 javah 的命令都写错了,文件名都应该是HelloMyJni.java
        还需要注意一点,javah生成的.h文件,include 应该改为"jni.h"
        还需要注意一点,接上面一点,把java中jni.h和jni_md.h都和java文件放一起。所以"",
            <>是系统文件的意思
        还需要注意一点,如果只是简单调用,生成的dll文件和java文件放一起就可以了。
            若果是系统中随处可调用,就需要放在
            window:java安装目录下的bin目录
        1> 报错:
            如果文件第一行是package xx,指定了包名则:
            编译:需要指定路径加-d参数,值为当前即点 --> javac -d . HelloMyJni.java
            运行:需要包名.类名 --> java com.guozi.test.HelloMyJni
            头文件:需要包名.类名 --> javah -jni com.guozi.test.HelloMyJni
    3> java调用c(项目)
        前提:
            比如我的项目中,工具类HelloMyJni.java放在目录com.guozi.test下。
        外部写单个java类测试:
            1> java类必须加package com.guozi.test
                System.loadLibrary("dll名称");
            2> 编译:javac -d . HelloMyJni.java
            3> 生成头文件:javah -jni com.guozi.test.HelloMyJni
                头文件:com_guozi_test_HelloMyJni.h
                方法名:Java_com_guozi_test_HelloMyJni_helloWorld
                可知:如果模拟项目应用java调c,java类必须加package,因生成文件名和方法名不一样
                注:将.h文件中include 改为include "jni.h"
            4> 写c文件:
                1> #include "jni.h"
                2> #include "com_guozi_test_HelloMyJni.h"
                3> JNIEXPORT void JNICALL
                    Java_com_guozi_test_HelloMyJni_helloWorld(JNIEnv * env, jobject obj) 
                4> 生成dll文件:gcc -shared -o HelloWorld.dll HelloWorld.c
                5> dll文件位置:放在java目录bin下(其实随便path扫到的目录下即可)
                    好像会先找java.class.path,如果找不到,再找系统的路径。
        项目中应用:
            定义工具类,写入HelloMyJni内容,向外提供调用方法。完成!
            注:HelloMyJni的所在目录必须是和"前提"中说的一样 --> com.guozi.test

16、java/c/python区别

1> C语言是一门面向过程设计语言;
2> Java是一门面向对象编程语言,而Java语言是从C语言衍生而来,它吸收了C++语言的各种优点,并且摒弃了
    C++里难以理解的多继承、指针等概念。
3> python和java的区别:
    Java是一种静态类型语言,Python是一种动态类型语言
    Java中的所有变量需要先声明(类型)才能使用,Python中的变量不需要声明类型
    (Java编译以后才能运行,Python直接就可以运行)
注:
    1> 面向对象编程和面向过程编程的区别
        https://www.cnblogs.com/qianxiaoruofeng/p/11561188.html
    2> 静态语言和动态语言的区别
        https://www.cnblogs.com/raind/p/8551791.html
        像Java,c,c++这种就属于静态语言。静态语言(强类型语言)
        像JavaScript,Python这种就属于动态语言。动态语言(弱类型语言)
        注:java是动态语言还是静态语言,见第二个"注"
    3> 解释型语言和编译性语言的区别
        见:https://www.zhihu.com/question/19608553/answer/27896401
        "温悦"用户回答
注:
    1> c语言经过编译之后变成的是是"机器码",二进制语言,机器可识别的
    
    2> java是解释型语言还是编译性语言
        不好严格区分
        一般认为是"解释型语言"
        JIT技术将运行频率很高的字节码直接编译为机器指令执行以提高性能
        java的编译结果是被jvm“解释执行”的,
        https://blog.csdn.net/weixin_34114823/article/details/88675337
        
    3> java是动态语言还是静态语言
        不好严格区分
        一般认为是"静态语言"
        但是(interface,reflection和dynamic class loading)运行时才能确定的特性
        
注:
    1> python效率低的原因
        https://blog.csdn.net/lmseo5hy/article/details/81875518
        执行时:Python解释器将源代码转换为字节码,然后再由Python解释器来执行这些字节码。
            其每次运行都要进行转换成字节码,然后再有虚拟机把字节码转换成机器语言,最后才能在硬件
            上运行。
            并不是和java一样,先显示编译为字节码,存在磁盘或内存,可以复用。
    2> AOT和JIT
        JIT:后端编译/即时(JIT)编译
        AOT:静态提前编译(Ahead Of Time,AOT编译)
        目前来看,流行框架太依赖反射和各种奇门优化,这东西应用于后台开发还不太现实
        AOT牺牲了跨平台性,直接由源码搞到了机器码https://www.zhihu.com/question/54842396

17、创建对象方式

1> 概述
    我们还可以使用反射机制(Class类的newInstance方法、使用Constructor类的newInstance方法)、使用
    Clone方法、使用反序列化等方式创建对象
2> 地址:
    https://blog.csdn.net/justloveyou_/article/details/72466416

18、看过哪些源码

1> HashMap的部分源码:put和get方法
2> Spring IoC的部分源码
3> Spring AOP的部分源码
4> Spring MVC的部分源码
5> Ribbon & Feign

19、线程池+多线程

20、zookeeper相关

21、类加载顺序

来源1:https://blog.csdn.net/yy339452689/article/details/104014732?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight
    
来源2:https://blog.csdn.net/u014745069/article/details/82655339
    
父类的静态字段——>父类静态代码块——>子类静态字段——>子类静态代码块——>
父类成员变量(非静态字段)——>父类非静态代码块——>父类构造器——>子类成员变量——>子类非静态代码块——>子类构造器

22、Dubbo源码

23、CGLIB和Java动态代理

1> Java动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,
    Java类继承机制不允许多重继承);
    Java动态代理使用Java原生的反射API进行操作,在生成类上比较高效;
    Java反射机制
2> CGLIB能够代理普通类;
    CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效
    它允许我们在运行时对字节码进行修改和动态生成
注:
    1> ASM详解:https://www.cnblogs.com/zt007/p/6377789.html
    2> Proxy详解:https://www.cnblogs.com/incognitor/p/9759987.html
        1> java静态代理:
            设计模式中的代理模式
            静态代理的缺点是冗余,因为一个代理类只能代理一个接口
        2> java动态代理:
            主要方法:Proxy.newProxyInstance
            接口方法调用实质是InvocationHandler.invoke()方法
    注:两种对比
        https://www.cnblogs.com/CarpenterLee/p/8241042.html

24、编码格式相关

搞清楚了ASCII、Unicode和UTF-8的关系
​
ASCII编码是1个字节,而Unicode编码通常是2个字节(如果要用到非常偏僻的字符,就需要4个字节)
注* 存储和传输   上就十分不划算
本着节约的精神,又出现了把Unicode编码转化为“可变长编码”的UTF-8编码
常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节
​
在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,转换为UTF-8编
码。

25、通信协议相关

26、多线程

1> 线程排查死锁的方法
    1> jsp找到对应线程号
    2> jstack 线程号
2> 线程的所有状态
    查看图片:H:\desktop\面试相关\面试\2019年面试\7、thread
    来源:https://blog.csdn.net/dupengshixu/article/details/83689719
    1> yield
        Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程。
        即:主动放弃自己线程得到的cpu的"cpu时间片时间片"
        yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转
        到可运行状态,但有可能没有效果。
    2> wait和notify
        等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁后,进入到了该对
            象的等待池,等待池中的线程不会去竞争该对象的锁。
        锁池:只有获取了对象的锁,线程才能执行对象的 synchronized 代码,对象的锁每次只有一个线程
            可以获得,其他线程只能在锁池中等待(唤醒之后,b、c争夺锁)
        区别:
            notify() 方法随机唤醒对象的等待池中的一个线程,"进入锁池";
            notifyAll() 唤醒对象的等待池中的所有线程,进入锁池。
        来源:https://blog.csdn.net/meism5/article/details/90238268
    3> join
        thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。
        比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
        注:当前线程,即"main线程",main线程调用了a线程的join方法,直到线程a执行完毕后,才会继续
            执行线程main。即b/c线程的start方法。
        
3> 线程池的所有状态
    RUNNING :能接受新提交的任务,并且也能处理阻塞队列中的任务;
    SHUTDOWN:关闭状态,不再接受新提交的任务,但却可以继续处理阻塞队列中已保存的任务。在线程池处于
        RUNNING 状态时,调用 shutdown()方法会使线程池进入到该状态。(finalize() 方法在执行过程
        中也会调用shutdown()方法进入该状态);
    STOP:不能接受新任务,也不处理队列中的任务,会中断正在处理任务的线程。在线程池处于 RUNNING 或
        SHUTDOWN 状态时,调用 shutdownNow() 方法会使线程池进入到该状态;
    TIDYING:如果所有的任务都已终止了,workerCount (有效线程数) 为0,线程池进入该状态后会调用
        terminated() 方法进入TERMINATED 状态。
    TERMINATED:在terminated() 方法执行完后进入该状态,默认terminated()方法中什么也没有做。
​
4> 线程实现方式
    1> 实现Runnable接口
    2> 继承Thread类
    3> 实现Callable接口,实现call方法(java1.5之后提供的方式)
        第1/2种方式,需重写/实现run方法,该方法没有返回值
        Callable接口有泛型
        call方法有返回值,值类型是Callable接口标识的泛型
        <源码有点复杂,FuturTask是实现接口实现类,源码复杂,暂时不看>
​
5> java中有几种线程池  
    来源:https://baijiahao.baidu.com/s?id=1652900816694167505&wfr=spider&for=pc
    1> 阻塞队列
        ArrayBlockingQueue:有界队列(基于数组的)
        LinkedBlockingQueue:有/无界队列(基于链表的,传参就是有界,不传就是无界)
        SynchronousQueue:同步移交队列(需要一个线程调用put方法插入值,
            另一个线程调用take方法删除值)
    2> handler:拒绝处理策略
        ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常
            注:抛异常
        ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常
            注:扔掉新的
        ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务
            注:扔掉旧的(重复此过程)
        ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
    3> 常用的线程池
        1> newCachedThreadPool()
            核心线程数为0,最大线程数几乎无限,阻塞队列使用的是 SynchronousQueue:同步移交队列
            注意事项:使用时要注意控制线程的数量,防止因线程数太多导致OOM。
        2> newFixedThreadPool(int nThreads)
            创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数
            量达到线程池初始的最大数,则将提交的任务存入到池队列中。
​
            FixedThreadPool是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所
            耗的开销的优点。但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线
            程,还会占用一定的系统资源。
            
            线程数量固定,传入的即是核心线程数,也是最大值;阻塞队列用的是无界的。
            注意:使用时要注意控制无界队列的大小,防止因队列中等待的线程数太多导致OOM。
        3> newSingleThreadExecutor()
            核心线程数和最大线程数都是1,阻塞队列是无限的(单一线程,一次执行一个线程)
            
    4> ScheduledThreadPoolExecutor
        来源:https://www.cnblogs.com/despacito/p/11171383.html
        从构造函数可以看出ScheduledThreadPoolExecutor还是调用的父类ThreadPoolExecutor的构造方
        式,唯一区别是工作队列固定为DelayedWorkQueue。
    
6> 线程池源码(核心)
    1> 主线程调用ThreadPoolExecutor类的submit方法,submit方法内部直接调用了execute方法,
        execute方法是顶层接口Executor定义,在ThreadPoolExecutor类中实现
    2> execute方法中,调用addWork方法,会new一个Work对象,后面调用work的start方法。
    3> work是ThreadPoolExecutor类的内部类,实现了Runnable接口,实现了run方法,方法内直接调用了
        runWork方法
    4> run方法中,通过getTask方法拿到任务,getTask方法是从workQueue队列中poll方法拿到任务。
    5> workQueue队列是在创建ThreadPoolExecutor类对象的构造器中定义的
    6> 从队列中拿到任务之后,会调用任务对象的run方法
​
7> 核心线程和非核心线程的区别
    来源:https://blog.csdn.net/qq_30596077/article/details/88658264
    1> 核心线程 :
        固定线程数 可闲置 不会被销毁 ThreadPoolExecutor的allowCoreThreadTimeOut属性设置
        为true时,keepAliveTime同样会作用于核心线程
    2> 非核心线程数:
        非核心线程闲置时的超时时长,超过这个时长,非核心线程就会被回收
    注:
        1> 核心线程池不会销毁,可以重复使用的,所以优先判断核心线程是否满了。
        2> 如果核心线程满了,就往队列中放,因为还是正常排队使用核心线程工作。
        3> 如果队列都满了,不得已再看非核心线程是否满了。(因为这家伙会销毁,耗费性能)
8> 并发原子类AtomicInteger
    https://baijiahao.baidu.com/s?id=1647621616629561468&wfr=spider&for=pc
9> CAS机制
    https://baijiahao.baidu.com/s?id=1647620168550407271&wfr=spider&for=pc

0、Spring

0、概念

1.Spring是什么?
    概述:Spring是一个'轻量级'的'IoC和AOP'的'容器框架'。
    目的:用于'简化'企业应用程序的开发,它使得开发者只需要关心业务需求
    主要模块有:
        1> Spring Core:提供IOC服务;(核心类库)
        2> Spring AOP:提供AOP服务;
        3> Spring MVC:提供面向Web应用的Model-View-Controller'实现'。
        4> Spring ORM:提供对现有的ORM框架的支持;
    来源:https://blog.csdn.net/a745233700/article/details/80959716

1、优点

2.Spring 的优点?
    1> spring属于'低侵入式'设计,代码的污染极低;
    2> spring的'DI机制'将对象之间的依赖关系交由框架处理,减低组件的'耦合性';
    3> Spring提供了AOP技术,支持将一些'通用任务进行集中式管理',从而提供更好的复用。
        如安全、事务、日志、权限等
    4> spring对于'主流的应用框架'提供了集成支持。(略)

2、AOP理解

3.Spring的AOP理解
    注* 一个概念,一个原理。优点不谈。
    1> 概述:
        a. OOP面向对象,允许开发者定义纵向的关系,但并不适用于定义横向的关系,导致了大量代码的重复
        b. AOP,一般称为面向切面,作为面向对象的一种补充。
        c.切面:
            用于将那些'与业务无关,但却对多个对象产生影响'的公共行为和逻辑,抽取并封装为一个可重用            的模块,这个模块被命名为'切面'(Aspect),
    2> 优点:(略)
        减少重复代码,
        降低耦合度,(模块间的)
        提高可维护性。
    3> 原理:
        AOP实现的关键在于 '代理模式',
        AOP代理主要分为静态代理和动态代理。
        静态代理的代表为AspectJ;动态代理则以Spring AOP为代表
    4> 静态代理和动态代理理解(略)
        a. 静态代理:
            就是AOP框架会在'编译阶段'生成AOP代理类。
            他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象
        b. 动态代理
            AOP框架不会去修改字节码,而是'每次运行'时在内存中临时为方法生成一个AOP对象
            这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法
    5> Spring AOP中的动态代理主要有两种方式(略)
        a JDK动态代理
        b CGLIB动态代理
        注* 目标对象有实现接口,spring会自动选择“JDK代理”
            // 目标对象没有实现接口, spring会用“cglib代理”
        来源:https://www.cnblogs.com/guzhou-ing/p/6445159.html
    6> 静态代理和动态代理的区别(略)
        上接4>
        相对来说AspectJ的静态代理方式具有更好的性能
        但是AspectJ需要特定的'编译器'进行处理

3、IoC理解

4.Spring的IoC理解
    注* 一个概念,一个原理。优点不谈。
    1> 概述:
        IOC就是控制反转,是指创建对象的控制权的转移。以前创建对象的主动权和时机是由自己把控的,而现
        在这种权力转移到Spring容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系。
    2> 优点:
        对象与对象之间松散耦合
        也利于功能的复用
    3> 原理:
        使用java的'反射机制',根据配置文件在运行时'动态的'去创建对象以及管理对象,并调用对象的方法           的。
    4> 注入方式:
    基于XML的注入:
        子来源:https://blog.csdn.net/a909301740/article/details/78379720
        a.构造方法注入
            
            
                
            
            
            
        b.setter注入
            
            
                
            
            
            
            注* 如果通过set方法注入属性,那么spring会通过默认的空参构造方法来实例化对象
        c.静态工厂注入
        d.实例工厂注入
    基于注解的注入:
        子来源:https://www.cnblogs.com/wangbin2188/p/9014400.html
        描述依赖关系主要有两种:
        @Resource
            默认以byName的方式去匹配与属性名相同的bean的id,
            如果没有找到就会以byType的方式查找。
            如果byType查找到多个的话,使用@Qualifier注解指定某个具体名称的bean
        @Autowired
            默认是以byType的方式去匹配类型相同的bean
        注* 实现步骤
            1 导入AOP的Jar包。因为注解的后台实现用到了AOP编程。
            2 需要更换配置文件头,即添加相应的约束。
            3 需要在Spring配置文件中配置组件扫描器,用于在指定的基本包中扫描注解。

4、自动装配

9.Spring的自动装配   
    0> 来源:
        https://blog.csdn.net/tanga842428/article/details/54694484
    1> 概述:
        Spring注入中byType和byName的总结
        区分清楚什么是byType,什么是byName
    2> 示例:
        
          
        
    3> 详解:
        比如说如上这段代码,byName就是通过Bean的id或者name,byType就是按Bean的Class的类型。
        若autowire="byType"意思是通过 class="cn.com.bochy.dao.impl.UserDaoImpl"来查找            UserDaoImpl下所有的对象。
        代码autowire="byName"意思是通过id="userDao"来查找Bean中的userDao对象
    4> @Autowired和@Resource之间的区别
        a.@Autowired默认是按照'类型'装配注入的
        b.@Resource默认是按照'名称'来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注           入。

5、用到的设计模式

10.Spring 框架中都用到了哪些设计模式?
    1> 工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
    2> 单例模式:Bean默认为单例模式。
    3> 代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
    4> (略)
        模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
    5> (略)
        观察者模式:定义对象间一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象
        都会得到通知被制动更新,如Spring中listener的实现--ApplicationListener。

6、事务

11.Spring事务的实现方式和实现原理
    1> 概述:
        Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,
        spring是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过binlog或者redolog实现的。
    2> Spring事务的种类
        Spring支持'编程式'事务管理和'声明式'事务管理两种方式
    3> 声明式事务的优缺点
        原理:
            声明式事务管理建立在'AOP'之上的。其本质是通过AOP功能,对'方法前后'进行'拦截',将事务            处理的功能'编织'到拦截的方法中.也就是在目标方法开始之前加入一个事务,在执行完目标方法            之后根据执行情况提交或者回滚事务。
        优点:
            不需要在业务逻辑代码中'掺杂'事务管理的代码
            这正是spring倡导的'非侵入式'的开发方式
        缺点:
            '最细粒度'只能作用到'方法级别',无法做到像编程式事务那样可以作用到'代码块级别'。

7、其他

5.BeanFactory和ApplicationContext有什么区别(略)
    0> 概述:
        BeanFactory和ApplicationContext是Spring的两大核心接口,'都可以当做Spring的容器'。
        其中ApplicationContext是BeanFactory的子接口。
    1> 区别:
        a.BeanFactroy采用的是'延迟加载'形式来注入Bean的,即只有在使用到某个Bean时(调用                     getBean()),才对该Bean进行加载实例化。
            注* 配置问题不易发现。
        b.ApplicationContext,它是在容器'启动'时,一次性创建了所有的Bean
            注* 占用内存空间。当应用程序配置Bean较多时,程序启动较慢。
6.请解释Spring Bean的生命周期?(略)
    1> 实例化Bean
        对于BeanFactory容器
        对于ApplicationContext容器
    2> 设置对象属性(依赖注入)
    3> 处理Aware接口
    4> BeanPostProcessor
    5> InitializingBean 与 init-method
    6> BeanPostProcessor
    7> DisposableBean
        当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的
        destroy()方法;
    8> destroy-method
        最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法
7.解释Spring支持的几种bean的作用域。(略)
    1> singleton
        默认,'每个容器'中只有一个bean的实例
    2> prototype
        为'每一个bean请求'提供一个实例
    3> request
    4> session
    5> global-session
8.Spring如何处理线程并发问题?
    ThreadLocal
    早在JDK 1.2的版本中就提供java.lang.ThreadLocal,
    ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。
    使用这个工具类可以很简洁地编写出优美的多线程程序。
​
​
​
12.spring的事务传播行为(略)
    spring事务的传播行为说的是,当多个事务同时存在的时候,spring如何处理这些事务的行为。
13.Spring中的隔离级别(略)
14.Spring框架中有哪些不同类型的事件(略)
15.解释一下Spring AOP里面的几个名词(待完善)
    1> 切面(Aspect)
    2> 连接点(Join point)
    3> 通知(Advice)
    4> 切入点(Pointcut)
    5> 引入(Introduction)
    6> 目标对象(Target Object)
    7> 织入(Weaving)
16.面向切面编程
    1> 来源:
        https://blog.csdn.net/seashouwang/article/details/80232811
        springboot-aop面向切面编程
    2> 来源:
        https://www.cnblogs.com/guzhou-ing/p/6445159.html -- 代码
        https://blog.csdn.net/MrLeeYongSheng/article/details/78643671   -- 需要所有jar包
        https://www.cnblogs.com/hzg110/p/6760653.html -- 需要jar包
        spring基于注解和XML实现AOP编程
注* 面向切面编程,创建一个最简单的Spring项目
    来源:https://www.cnblogs.com/hzg110/p/6760653.html
    注意:application.xml报错,是因为jar包不够,引入来源中的jar包之后,报错消失。
问题1:ClassPathXmlApplicationContext加载多个XML文件
    来源:https://blog.csdn.net/weinichendian/article/details/72842929

1、SpringMVC

0、流程

2.SpringMVC的流程?
    1> 用户发送请求至'前端控制器'DispatcherServlet;
    2> DispatcherServlet收到请求后,调用HandlerMapping'处理器映射器',请求获取Handle;
    3> 处理器映射器HandlerMapping根据请求url'找到'具体的处理器Handler,生成处理器对象及处理器拦截
        器(如果有则生成)一并返回给DispatcherServlet;
    4> DispatcherServlet 调用 HandlerAdapter'处理器适配器';
    5> HandlerAdapter 经过适配'调用' '具体处理器'(Handler,也叫后端控制器);
    6> Handler执行完成返回ModelAndView;
    7> HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;
    8> DispatcherServlet将ModelAndView传给ViewResolver'视图解析器'进行解析;
    9> ViewResolver解析后返回'具体View';
    10> DispatcherServlet对View进行'渲染'视图(即将模型数据填充至视图中)
    11> DispatcherServlet响应用户。

1、优点

3.Springmvc的优点(略)
    1> 可以支持各种视图技术,而不仅仅局限于JSP;
    2> 与Spring框架集成(如IoC容器、AOP等);
    3> 清晰的角色分配:前端控制器(dispatcherServlet) , 请求到处理器映射(handlerMapping), 处理      器适配器(HandlerAdapter), 视图解析器(ViewResolver)。
    4> 支持各种请求资源的映射策略。

2、和Struts2的区别

5.springMVC和struts2的区别有哪些?
    1> 入口
        springmvc的入口是一个servlet即前端控制器(DispatchServlet),
        而struts2入口是一个filter过虑器(StrutsPrepareAndExecuteFilter)。
​
    2> 开发原理
        springmvc是基于'方法'开发(一个url对应一个方法),请求参数传递到方法的形参。
        struts2是基于'类'开发,传递参数是通过类的属性。
​
    3> 请求/响应的数据存储
        a. Struts采用'值栈'存储请求和响应的数据,通过OGNL存取数据,
        b. springmvc通过'参数解析器'是将request请求内容解析,并给方法形参赋值,将数据和视图封装成
            'ModelAndView对象',最后又将ModelAndView中的模型数据通过reques域传输到页面。

3、函数返回值类型

17.SpringMvc中函数的返回值是什么?
    子来源:https://blog.csdn.net/qq_35192741/article/details/79085815
    1> ModelAndView
        ModelAndView mv = new ModelAndView();  
        mv.addObject("content", "springMVC HelloWorld:"+param);  
        mv.setViewName("springMVC/helloWorld");     
        return mv; 
    2> String
        //返回值为String类型的,视图名就是返回值  
        @RequestMapping(value="/returnString",method=RequestMethod.GET)  
        public String returnString(Model model) {  
            model.addAttribute("test", "return string!!!");  
            System.out.println("springMVC测试:helloWorld;");  
            return "return/returnString";  
        }  
    3> void
        //返回值是void类型的,由于没有返回值 
        //它默认的展示视图名和url中的一段是一样的即returnVoid.jsp
        @RequestMapping("/returnVoid")  
        public void returnVoid(Model model){  
            model.addAttribute("test", "return void!!!");  
        }  
    4> Map
        //返回值是void类型的,由于没有指定视图名 
        //它默认的展示视图名和url中的一段是一样的即returnVoid.jsp 
        @RequestMapping("/returnModel")  
        public Model returnModel(Model model){  
            model.addAttribute("test", "return Model!!!");  
            return model;  
        }  
    5> Model 
        //返回值是void类型的,由于没有指定视图名 
        //它默认的展示视图名和url中的一段是一样的即returnVoid.jsp 
        @RequestMapping("/returnMap")  
        public Map returnMap(Model model){  
            Map map = new HashMap();  
            map.put("test", "return map!!!");  
            return map;  
        } 

 

来源:https://blog.csdn.net/a745233700/article/details/80963758
0.对Spring MVC的理解 ?
    注* 一个概念,一个原理(即流程)。优点不谈。
1.什么是Spring MVC ?
    1> Spring MVC是'轻量级'的实现了'MVC设计模式'的'Web框架',
    2> (略)
        通过把Model,View,Controller分离,把复杂的web应用分成逻辑清晰的几部分,
    3> (略)
        简化开发,减少出错,方便组内开发人员之间的配合。
​
​
4.Spring MVC的主要组件?(略)   
    注意:Handler和View
    1> 前端控制器 DispatcherServlet(不需要程序员开发)
        接收请求、响应结果,相当于转发器,有了DispatcherServlet 就减少了其它组件之间的耦合度。
    2> 处理器映射器HandlerMapping(不需要程序员开发)
        根据请求的URL来查找Handler
    3> 处理器适配器HandlerAdapter
        在编写Handler的时候要按照HandlerAdapter要求的规则去编写,这样适配器HandlerAdapter才可以
        正确的去执行Handler。
    4> 处理器Handler(需要程序员开发)
    5> 视图解析器 ViewResolver(不需要程序员开发)
        进行视图的解析,根据视图逻辑名解析成真正的视图(view)
    6> 视图View(需要程序员开发jsp)
        View是一个接口,它的实现类支持不同的视图类型(jsp,freemarker,pdf等等)
​
6.SpringMVC怎么样设定重定向和转发的?
    1> 转发:在返回值前面加"forward:",譬如"forward:user.do?name=method4"
    2> 重定向:在返回值前面加"redirect:",譬如"redirect:http://www.baidu.com"
7.SpringMvc怎么和AJAX相互调用的?
    通过Jackson框架就可以把Java里面的对象直接转化成Js可以识别的Json对象。具体步骤如下 :
    1> 加入Jackson.jar
    2> (略)
        在配置文件中配置json的映射
    3> 在接受Ajax方法里面可以直接返回Object,List等,但方法前面要加上@ResponseBody注解。
8.如何解决POST请求中文乱码问题,GET的又如何处理呢?
    1> 解决post请求乱码问题:
        在web.xml中配置一个CharacterEncodingFilter过滤器,设置成utf-8;
        
            CharacterEncodingFilter
            org.springframework.web.filter.CharacterEncodingFilter
            
            
                encoding
                utf-8
            
        
        
            CharacterEncodingFilter
            /*
        
    2> get请求中文参数出现乱码解决方法有两个:
    方法1:
        修改tomcat配置文件添加编码与工程编码一致,如下:
        
    方法2:
        另外一种方法对参数进行'重新编码':
        String userName = new String(request.getParamter("userName").getBytes("ISO8859-         1"),"utf-8")
        注* ISO8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码。
9.Spring MVC的异常处理 ?
    子来源:https://www.cnblogs.com/junzi2099/p/7840294.html
    一共三种方法
    方法1:使用 @ ExceptionHandler 注解
        使用该注解有一个不好的地方就是:进行异常处理的方法必须与出错的方法在同一个Controller里面。
        使用如下:
        @Controller      
        public class GlobalController {               
            //处理异常
            @ExceptionHandler({MyException.class})       
            public String exception(MyException e) {       
                System.out.println(e.getMessage());       
                e.printStackTrace();       
                return "exception";       
            }       
​
            @RequestMapping("test")       
            public void test() {       
                throw new MyException("出错了!");       
            }                    
        }
        可以看到,这种方式最大的缺陷就是不能全局控制异常。每个类都要写一遍。
    方法2:实现 HandlerExceptionResolver 接口
        这种方式可以进行全局的异常控制。例如:
        @Component  
        public class ExceptionTest implements HandlerExceptionResolver{  
            public ModelAndView resolveException(HttpServletRequest request,                            HttpServletResponse response, Object handler, Exception ex) {  
                System.out.println("This is exception handler method!");  
                return null;  
            }  
        }
    方法3:使用 @ControllerAdvice+ @ ExceptionHandler 注解(略)
10. SpringMvc的控制器是不是单例模式,如果是,有什么问题,怎么解决?
    子来源(大神文章-通俗易懂):https://www.cnblogs.com/eric-fang/p/5629892.html
    解决:
        1、不要在controller中定义成员变量。
        2、万一必须要定义一个非静态成员变量时候,则加注解@Scope("prototype"),将其设置为多例模式
11. SpringMVC常用的注解有哪些?
    1> @RequestMapping
        用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都
        是以该地址作为父路径。
    2> @RequestBody
        注解实现接收http请求的json数据,将json转换为java对象。
    3> @ResponseBody
        注解实现将conreoller方法返回对象转化为json对象响应给客户。
12.SpingMvc中的控制器的注解一般用那个,有没有别的注解可以替代?
    一般用@Conntroller注解,表示是表现层,不能用别的注解代替。
13.如果在拦截请求中,我想拦截get方式提交的方法,怎么配置?
    可以在@RequestMapping注解里面加上method=RequestMethod.GET
14.怎样在方法里面得到Request,或者Session?
    直接在方法的形参中声明request,SpringMvc就自动把request对象传入。
15.如果想在拦截的方法里面得到从前台传入的参数,怎么得到?
    方法1:直接在形参里面声明这个参数就可以,但必须名字和传过来的参数一样。
    方法2:使用@RequestParam注解
16.如果前台有很多个参数传入,并且这些参数都是一个对象的,那么怎么样快速得到这个对象?
    直接在方法中声明这个对象,SpringMvc就自动会把属性赋值到这个对象里面。
    或者
    使用@RequestBody注解
​
18.SpringMvc用什么对象从后台向前台传递数据的?
    通过Model或者Map对象,可以在这个对象里面调用put方法,把对象加到里面,前台就可以通过el表达式拿到。
    model --> model.addAttribute("test", "return Model!!!");  
    map --> map.put("test", "return map!!!");  
19.怎么样把Model/Map里面的数据放入Session里面?
    子来源:https://blog.csdn.net/u012325167/article/details/52426523
    可以在类上面加上@SessionAttributes注解,里面包含的字符串就是要放入session里面的key。
20.SpringMvc里面拦截器是怎么写的:
    子来源:https://blog.csdn.net/binggetong/article/details/78831681
    有两种写法:
        一种是实现HandlerInterceptor接口,
        另外一种是继承适配器类,接着在接口方法当中,实现处理逻辑;
        然后在SpringMvc的配置文件中配置拦截器即可
    配置文件示例:
      
          
          
          
              
              
              
          
        
            
            
            
        
      
21.注解原理:(略)
    子来源(强的一批):
        https://www.cnblogs.com/yangming1996/p/9295168.html
    来源:
    注解本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。我们通过反
    射获取注解时,返回的是Java运行时生成的动态代理对象。通过代理对象调用自定义注解的方法,会最终调
    用AnnotationInvocationHandler的invoke方法。该方法会从memberValues这个Map中索引出对应的值。
    而memberValues的来源是Java常量池。

####

你可能感兴趣的:(面试,java)