面试汇总--初中级

1.Java、集合 

反射,静态代理和动态代理,Java动态代理和Cglib动态代理的区别?**** 

反射可以在运行时动态生成对象,获取对象属性,调用对象方法例如:Spring框架中Bean的创建

静态: 代理类是自己手工实现的,自己创建一个Java类,表示代理类。同时你所要代理的目标是确定的。

动态: jdk运行期间,动态创建class字节码并加载到JVM  

JDK动态代理是利用反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。实现InvocationHandler接口  

  • 业务目标对象实现接口 (要求)
  • 实现 InvocationHandler 接口
  • 使用 Proxy.newProxyInstance() 方法生成代理对象

cglib动态代理是利用asm开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。实现MethodInterceptor接口。

如果目标对象生成了接口,默认情况下会采用 JDK 的动态代理实现 AOP,也可强制使用 CGLIB 实现 AOP,如果目标对象没实现接口,必须采用 CGLIB

1.8的时候JDK的效率已高于cglib  

1、当bean实现接口时,会用JDK代理模式
2、当bean没有实现接口,用cglib实现  

深拷贝和浅拷贝 

浅拷贝只复制指向对象的指针,不复制对象本身,新旧对象共享同一块内存。但深拷贝(clone())会另造一个一样的对象,新对象跟原对象不共享内存,修改新对象不改变原对象。

static的应用场景 

修饰成员变量,修饰成员方法,静态代码块,修饰静态内部类,静态导包

String 和 StringBuffer、StringBuilder 的区别是什么?String 为什么是不可变的?(final)

可变性:String使用字符数组保存字符串,private  final  char value[],string对象是不可变的。StringBuilder与StringBuffer继承自AbstractStringBuilder类,AbstractStringBuilder中也是使用字符数组保存字符串 char[] value,所以两种对象都可变的。

2.线程安全性:String中的对象是不可变的,可以理解为常量,所以线程安全;AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。StringBuffer对方法加了同步锁synchronized关键字或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。

3.性能:String 类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String 对象。StringBuffer 每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用StirngBuilder 相比使用StringBuffer 仅能获得10%~15% 左右的性能提升,但却要冒多线程的不安全。

final和finally,finalize有啥区别?

final修饰的类,不能派生出新子类,不能作为父类被子类继承。一个类不能既被abstract声明,又被final声明。将变量或方法声明final,可以保证在使用的过程中不被修改。被声明final的变量必须在声明时给变量赋初始值,在以后引用中只能读取。被final声明的方法也同样只能使用,不能重写。
finally在异常处理时提供finally块来执行清除操作.不管有没有异常抛出、捕获,finally块都被执行。try块中的内容是在无异常时执行到结束。catch块中的内容,是在try块内容发生catch所声明的异常时,跳到catch块执行。finally块则异常是否发生,都会执行finally块的内容,所以代码逻辑中有需要无论发生什么都必须执行的代码,可放在finally块中。

finalize是方法.java允许用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的.它在object类中定义,所有的类都继承它。子类覆盖finalize()方法以整理系统资源或者被执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。
 

== 与 equals,hashcode 相同吗?为什么要同时重写equals和hashcode?

 基本数据类型,==比较的是值,没有equals()方法。对于引用数据类型来说,==比较的是对象的内存地址。 没有重写equals方法时,equals等同于==  

  • 如果两个对象通过equals()方法比较,判定相等,那么,调用二者各自的hashCode()方法必须产生同一个hash值。如果两个对象通过equals()方法比较,判定不相等, hashCode()方法可能相等也可能不等。

Object类的常见方法

object 类是根类,所有类直接或间接继承该类;public int hashCode();返回对象的哈希码值。不同对象的,hashCode()一般来说不会相同。同一个对象的hashCode()值肯定相同。public final Class getClass();返回此 Object 的运行时类,可以通过Class类中的一个方法,获取对象的真实类的全名称: public String getName()。 public String toString();返回该对象的字符串表示。一般建议重写该方法。然后调用对象的toString()方法。object类的equals()方法:指示其他某个对象是否与此对象“相等”。默认情况下比较对象的引用是否相同,重写后用于比较成员变量值是否相等

Java 中的异常处理,举几个遇到的异常?

ClassCastException      两个类型间转换不兼容引发的运行异常
ArrayIndexOutOfBoundsException 数组越界
NullPointerException      空指针异常
ArithmeticException   算术异常
NumberFormatException    数字格式异常
InputMismatchException   输入不匹配异常

接口和抽象类的区别是什么

抽象类:构造方法:有构造方法,用于子类实例化使用。
成员变量:可以是变量,也可以是常量。
成员方法:可以是抽象的,也可以是非抽象的。

接口:构造方法:没有构造方法
成员变量:只能是常量。默认修饰符:public static final
成员方法:jdk1.7只能是抽象的。默认修饰符:public abstract (推荐:默认修饰符请自己永远手动给出) jdk1.8可以写以default和static开头的具体方法
 

Arraylist 与 LinkedList ,Vector异同?

相同点:三个类都实现了List接口,存储数据的特点相同:有序,可重复。
不同点:
ArrayList:作为List集合的主要实现类,线程不安全,但执行效率高,底层使用Object数据来存储数据(Object[] elementData;),查询比较快;
Vector:作为List集合的古老实现类,线程安全,但是执行效率低,底层也使用Object数据来存储数据(Object[] elementData;),底层许多方法都用了synchronized关键字,所以线程安全,执行效率低。
LinkedList:底层使用了双向链表来存储数据,频繁的插入和删除操作效率高。
LinkedList核心代码(双向链表):
    private static class Node {
      E item;	//存储数据
      Node next;	//下一个节点
      Node prev;	//上一个节点
 
      Node(Node prev, E element, Node next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
      }
    }

使用HashMap时候,用String做Key有啥好处?

final修饰不可变性,线程安全

HashMap的底层实现 (1.7和1.8的区别),说一下put方法?

initailCapacity是初始容量:默认值为16   负载因子:默认值为0.75  

1.根据key通过哈希算法与运算得出数组下标

2.如果数据下标位置元素为空,则将key和value封装为Entry对象(JDK1. 7是Entry对象,JDK1.8是Node对象)并放入该位置

3.如果数组下标位置元素不为空,则要分情况讨论

如果是JDK1.7,则先判断是否需要扩容,如果大于12就进行扩容。每次扩容时,都是直接在其现有容量基础上直接乘2如果不用扩容就生成Entry对象,并使用头插法添加到当前位置的链表中
如果是JDK1.8,则会先判断当前位置上Node的类型,看是红黑树Node,还是链表Node 
如果是红黑树Node,则将key和value封装为一个红黑树节点并添加到红黑树中去,在这个过程中会判断红黑树中是否存在当前key,如果存在则更新value 如果此位之上的Node对象是链表节点,则将key和value封装为一个链表Node并通过尾插法插入到链表的最后位置去,因为是尾插法,所以需要遍历链表,在遍历链表的过程中会哦判断是否存在当前key,如果存在则更新value,遍历链表后,将新链表Node插入链表中,会看到当前链表的节点个数,如果链表大于等于8并且数组长度超过64,会将该链表转为红黑树   ,将key和value封装为Node插入到链表或者红黑树中后,在判断是否需要进行扩容,如果需要就扩容,如果不需要就结束put方法

HashMap 的长度为什么是2的幂次方

减少Hash碰撞,使Hash算法结果均匀  位运算 会更快

HashMap 多线程操作导致死循环问题

jdk1.8之前,因为多线程同时扩容,向链表添加元素时采用的是头插法,多线程操作链表会发生环化,产生死循环。1.8之后是链表转换树或者对树进行操作(重新平衡红黑树)时出现死循环问题。且 多线程情况使用hashMap 还会有数据丢失的问题(因为各线程之间的 map 是不可见的),所以 多线程情况下 建议使用 concurrentHahsMap。

HashSet 和 HashMap 区别

1.实现的接口不同 HashSet实现的是Set接口,HashMap实现的是Map接口
2.存储内容不同 HashSet存储的是对象,HashMap存储的是键值对
3.添加元素的方法不同 HashSet是通过add()方法添加元素 HashMap是通过put()方法添加元素
4.计算hashCode的方式不同 HashMap是通过Key来计算hashCode值 HashSet是通过成员变量来计算hashCode值,对于两个对象来说,hashCode值可能相同,所以通过equals()来判断对象的相等性,如果两个对象不相等则返回false 
 HashSet的构造方法底层都是调用 HashMap的构造方法 

ConcurrentHashMap 和 Hashtable ,HashMap的区别

1.线程安全不同  HashMap是非线程安全的 ,Hashtable  ConcurrentHashMap是线程安全的,多线程环境下可用;
 2.继承的父类不同 HashMap继承自AbstractMap类。但二者都实现了Map接口。
Hashtable继承自Dictionary类,Dictionary类是一个已经被废弃的类,没用了
3.包含的contains方法不同 HashMap是没有contains方法的,而包括containsValue和containsKey方法; hashtable则保留了contains方法,效果同containsValue,还有containsValue,containsKey。
4.是否允许null值 Hashmap是允许key和value为null值的, HashTable键值对都不能为空,否则包空指针异常。
5.计算hash值方式不同  HashMap有个hash方法重新计算了key的hash值,因为hash冲突变高,所以通过一种方法重算hash值的方法:这里计算hash值,先调用hashCode方法计算出来一个hash值,再将hash与右移16位后相异或,从而得到新的hash值。
Hashtable通过计算key的hashCode()来得到hash值就为最终hash值。
6.解决hash冲突方式不同  HashMap中,当出现冲突时 链表+红黑树
HashTable中, 都是以链表方式存储。

 

ConcurrentHashMap线程安全底层实现(1.7和1.8)

JKD1.8以前ConcurrentHashMap由Segment和HashEntry组成。一个ConcurrentHashMap里包含一个Segment数组,一个Segment又包含一个HashEntry。将数据分为一段段来存储,然后给每一段数据分配一把锁,当一个线程占用锁访问一段数据时,其他数据也能被其他线程访问。
1.8之后采用synchronized和CAS来保证并发安全:数组+链表+红黑树。Synchronized只锁定当前链表或红黑树的首节点,这样只要hash不冲突就不会产生并发。默认 Segment 的个数是 16 个

Java新特性(8,9,10,11...) 

1.8:

  • Lambda 表达式 − 允许把函数作为一个方法的参数(函数作为参数传递到方法中),只能使用一次。只能简化函数式接口的匿名内部类的写法。@FunctionalInterface

  • 方法引用 −  直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,更紧凑简洁,减少冗余代码。

  • 默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。

  • Stream API −新添加的Stream API(java.util.stream) 真正的函数式编程风格引

  • Date Time API − LocalDate/LocalTime 和 LocalDateTime 类增加线程安全

  • Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。

java10: var关键字:局部变量类型推断;java11:新增字符串处理的方法,复制判断和空白字符串等。HttpClient替换原有的HttpURLConnection。

2.数据机构与算法(思路)

二分查找法

class Solution {
    public int search(int[] nums, int target) {
        int low = 0, high = nums.length - 1;
        while(low <= high){
          int mid = (high-low)/2 + low;//二分,划为两个区域
          int num = nums[mid];
          // num = target时,要找的下标即为mid;
          if(num == target){ 
              return mid;
          }else if(numtarget时,说明要找的target的下标在num的左侧,故而将最高点设为原来的high-1
          }
        }
        return -1;//找不到目标值时,返回-1;
    }    
}

快速排序(优化) 分治

1.从序列中选择一个轴点元素pivot从最后一个元素向前遍历我们的策略是:每次选择第0位置的元素为轴点元素2.利用pivot将数组分割成2个子数组将小于pivot的元素放在pivot的左侧将大于pivot的元素放在pivot的右侧将等于pivot的元素放在pivot的哪侧都可以,本文选择左侧3.对子序列进行步骤1和步骤2操作直到不能再分割(子序列中只剩下一个元素)

冒泡排序(优化),归并排序

选择排序,插入排序,希尔排序

链表反转(迭代和递归)

环形链表(set和快慢指针)

统计素数个数(暴力和埃氏筛选)

删除数组重复项(双指针)

二叉树遍历(前中后层)

二叉树最小深度(广度和深度)

斐波那契数列第n位的值(暴力递归,双指针)

3.多线程与并发

线程的生命周期? 新建,就绪,运行,阻塞以及死亡

AQS 原理 AQS 对资源的共享方式  AQS底层使用了模板方法模式

抽象队列同步器  

如果被请求的共享资源空闲,则将当前请求资源的线程归为有效工作线程,并且该资源设置为锁定状态。其他线程如果要请求该共享资源,由于该资源被占有,因此无法请求成功,那么就需要一套线程阻塞等待以及唤醒时锁分配的机制。这个机制AQS使用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。在AbstractQueuedSynchronizer类中CLH队列是一个虚拟的双向队列(虚拟的双向对立是不存在队列实例,而是通过Node节点之间的关系关联)


synchronized 关键字的底层原理,JDK1.6 之后synchronized 做了哪些优化

非静态方法锁是锁对象  静态方法锁是锁类 不一样 所以不互斥  

通过monitor对象来完成的: monitorenter monitorexit 

无锁状态--->偏向锁状态--->轻量级锁状态(cas消除同步)--->重量级锁状态 

偏向锁 线程A第一次访问同步块时,先检测对象头Mark Word中的标志位是否为01,依此判断此时对象锁是否处于无所状态或者偏向锁状态(匿名偏向锁)

谈谈 synchronized和ReenTrantLock 的区别

  • ReentrantLock必须手动获取与释放锁,而synchronized不需要手动释放和开启锁
  • ReentrantLock只适用于代码块锁,而synchronized可以修饰类、方法、变量等
  • 锁机制其实也不一样。ReentrantLock底层调用的是Unsafe的park方法加锁,synchronized操作的应该是对象头中mark word
  • ReentrantLock的state初始化为0,表示未锁定状态。A线程lock()时,会调用tryAcquire()独占该锁并将state+1。其他线程再tryAcquire()时就失败,直到A线程unlock()到state=0(即释放锁)为止,其它线程才有机会获取该锁。释放锁之前,A线程是可重复获取此锁(state会累加。但获取多少次就要释放多么次,保证state是能回到零态。
  • synchronized为非公平锁 ReentrantLock则即可以选公平锁也可以选非公平锁 
  • synchronized是不可中断 ,除非加锁的代码中出现异常, ReentrantLock则可以中断。
  • ReentrantLock 和synchronized 都是可重入锁;

说说 synchronized 关键字和 volatile 关键字的区别

1.volatile 是线程同步的轻量级实现 ,性能肯定比synchronized关键字要好 。

2. volatile 只用于变量而 synchronized 可以修饰方法以及代码块 。
3.volatile 能保证数据的可见性,但不保证原子性。synchronized 两者都能保证。
4.volatile 用于解决变量在多个线程之间的可见性,而 synchronized 解决的是多个线程之间访问资源的同步性。

 

线程池参数?如何创建线程池?线程的状态,thread.yield()?线程池五种线程池,四种拒绝策略,三种阻塞队列    线程池大小= 最大线程数 + 阻塞队列大小

1、有任务时会创建核心线程数corePoolSize

2、线程满了(有任务但是线程被使用完)不会立即扩容,而是放到阻塞队列中,当阻塞队列满了之后才会继续创建线程。

3、队列满了,线程数达到最大会执行拒绝策略。

4、当线程数大于核心线程数,超过KeepAliveTime(闲置时间),线程会被回收,最终会保持corePoolSize个数线程。

拒绝策略执行则么办?
1、另外创建一个队列,当拒绝策略执行将任务放入队列。

通过定时任务去每隔一秒去查看线程池队列中是否有任务,没有则添加进去。

缺点: 任务会丢失,线程优雅关闭是指正常情况队列执行晚。会关闭。如果用这种方法创建则会丢失任务。

2、如果 是特别重要的话就 放入DB去持久化,给任务加个状态,通过状态来判断任务的执行情况。

3、其他情况要根据场景考虑 比如又些任务过期淘汰
 

ThreadPoolExecutor(int corePoolSize,// 核心线程数
                   int maximumPoolSize,//最大线程数
                   long keepAliveTime,//空闲线程存活时间
                   TimeUnit unit,//存活时间单位
                   BlockingQueue workQueue,//阻塞队列
                   RejectedExecutionHandler handler)//拒绝策略

ExecutorService threadPool = null;
threadPool = Executors.newCachedThreadPool();
//有缓冲的线程池,线程数 JVM 控制
threadPool = Executors.newFixedThreadPool(3);
//固定大小的线程池
threadPool = Executors.newScheduledThreadPool(2);
threadPool = Executors.newSingleThreadExecutor();
//单线程的线程池,只有一个线程在工作
threadPool = new ThreadPoolExecutor();
//默认线程池,可控制参数比较多  

ArrayBlockingQueue和PriorityBlockingQueue使用较少,一般使用LinkedBlockingQueue和Synchronous。线程池的排队策略与BlockingQueue有关。

RejectedExecutionHandler rejected = null;
rejected = new ThreadPoolExecutor.AbortPolicy();
//默认,队列满了丢任务抛出异常
rejected = new ThreadPoolExecutor.DiscardPolicy();
//队列满了丢任务不抛异常
rejected = new ThreadPoolExecutor.DiscardOldestPolicy();
//将最早进入队列的任务删,之后再尝试加入队列
rejected = new ThreadPoolExecutor.CallerRunsPolicy();
//如果添加到线程池失败,那么主线程会自己去执行该任务;如果执行程序已关闭(主线程运行结束),则会丢弃该任务

实现Runnable接口和Callable接口的区别

Runnable接口中的唯一抽象方法run()方法没有返回值,Callable接口中的唯一抽象方法call()方法有返回值;

执行execute()方法和submit()方法的区别是什么呢?

execute和submit都属于线程池的方法,execute只能提交Runnable类型的任务,而submit既能提交Runnable类型任务也能提交Callable类型任务。

execute会直接抛出任务执行时的异常,submit会吃掉异常,可通过Future的get方法将任务执行时的异常重新抛出。

execute所属顶层接口是Executor,  submit所属顶层接口是ExecutorService,实现类ThreadPoolExecutor重写了execute方法,抽象类AbstractExecutorService重写了submit方法。
 

高并发结合任务执行时间长短怎么使用线程池?

 CPU 核数 +1 ,减少线程上下文的切换  增加cpu线程

做缓存是第一步,增加服务器是第二步,线程池的设置,使用中间件对任务进行拆分和解耦

JUC 包中的Atomic原子类是哪4类? 介绍一下 AtomicInteger 类的原理和使用?

整形 AtomicInteger 数组类型  AtomicIntegerArray  引用类型 AtomicStampedReference 

属性修改类型AtomicIntegerFieldUpdater

TreadLocal是什么?内存泄露?

每个Thread 维护一个 ThreadLocalMap 映射表,key 是 ThreadLocal实例本身,value 是真正需要存储的 Object。 ThreadLocal 本身并不存储值,它只是作为一个 key 来让线程从 ThreadLocalMap 获取 value。 ThreadLocalMap 使用 ThreadLocal 的弱引用作为 Key 的,弱引用的对象在 GC 时会被回收。而value还存在着强引用.只有thead退出以后,value的强引用链条才会断掉. 如果创建ThreadLocal的线程一直持续运行,那么这个Entry对象中的value就有可能一直得不到回收,发生内存泄漏。

如何查看线程死锁?    

有三种方法可排查死锁:jps+jstack、jconsole、jvisualvm

面试汇总--初中级_第1张图片

 为什么使用红黑树而不用平衡树?

红黑树不追求"完全平衡",即不像AVL那样要求节点的 |balFact| <= 1,它只要求部分达到平衡,但是提出了为节点增加颜色,红黑是用非严格的平衡来换取增删节点时候旋转次数的降低,任何不平衡都会在三次旋转之内解决,而AVL是严格平衡树,因此在增加或者删除节点的时候,根据不同情况,旋转的次数比红黑树要多

红黑树 : 每个节点要么是红色,要么是黑色,但根节点永远是黑色的;

每个红色节点的两个子节点一定都是黑色;

红色节点不能连续(也即是,红色节点的孩子和父亲都不能是红色);

从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点;

所有的叶节点都是是黑色的(注意这里说叶子节点其实是上图中的 NIL 节点);

在树的结构发生改变时(插入或者删除操作),往往会破坏上述条件 3 或条件 4,需要通过调整使得查找树重新满足红黑树的条件;

如何保证多线程的原子性,可见性,和有序性?

原子性:Atomic包、CAS算法、synchronized、Lock
原子性做了互斥方法,同一个线程只能有一个进行操作
可见性:synchronized、volatile
一个主内存的线程如果进行了修改,可以及时被其他线程观察到,介绍了volatile如何被观察到的
有序性:happens-before原则
happens-before原则,观察结果,如果两个线程不能从happens-before原则观察出来,那么就不能观察他们的有序性,虚拟机可以随意的对他们进行重排序

CompletableFuture你知道吗?

CompletableFuture异步任务执行线程池,默认是把异步任务都放在ForkJoinPool中执行。

  • runAsync() 以Runnable函数式接口类型为参数,没有返回结果,supplyAsync() 以Supplier函数式接口类型为参数,返回结果类型为U;Supplier接口的 get()是有返回值的(会阻塞)

线程a,b,c,d运⾏任务,怎么保证当a,b,c线程执⾏完再执⾏d线程?

Thread中的join方法解决线程顺序问题

倒计时锁CountDownLatch实现让三个线程同时执行

4.JVM


Java 中会存在内存泄漏吗,简述一下?

答:会,长周期持有短周期的引用:全局性的集合,单例模式的使用,类的static变量,删除机制和清除策略

详解JVM内存结构及分区,说一下每个分区放什么?

答:面试汇总--初中级_第2张图片

jvm调优的常用命令和参数是是什么?

答:jps查JVM进程;jstat本地或远程JVM进程类的装载、内存、垃圾收集、JIT编译;jinfo查询JVM的属性和参数值;jmap当前Java堆和永久代;jhat分析jmap生成的dump文件,JDK自带的工具;jstack生成JVM所有线程快照,线程快照是JVM线程正在执行的方法,定位线程长时间停顿的原因。

-Xms ~ -Xmx:指定Java程序最大堆内存,使用java -Xmx5000M -version判断系统分配的最大堆内存。指定最小堆内存,设置成跟最大堆内存一样, 减少GC。

-Xmn:设置新生代大小。整个堆内存 = 新生代内存 + 老年代内存,此值对系统性能影响较大,Sun官方推荐为堆的3/8。

-Xss:指定线程的最大栈空间。该参数决定了java函数调用的深度,值越大调用深度越深,若值太小,容易发生栈溢出错误。

-XX:PermSize :指定方法区(永久区)的初始值,默认是物理内存的1/64,Java8永久区移除变成元数据区,由-XX:MetaspaceSize指定。

-XX:MaxPermSize :指定方法区(永久区)的最大值,默认是物理内存的1/4,Java8永久区移除之后,取而代之的是元数据区,由-XX:MaxMetaspaceSize指定。

-XX:NewRatio=n:老年代和新生代比值,n=2时,说明老年代和新生代的比值为2:1。

-XX:SurvivorRatio=n:Eden区和Survivor区的比值。n=8时,说明Eden和Survivor比值为8:1:1,因为Survivor有两个(from,to)。

描述一下 JVM 加载 Class 文件的原理机制?

答: 1.装载:查找和导入class文件;

2.连接:

      (1)检查:检查载入的class文件数据的正确性;

      (2)准备:为类的静态变量分配存储空间;

      (3)解析:将符号引用转换成直接引用(这一步是可选的)

3.初始化:初始化静态变量,静态代码块。
 解释下Tomcat类加载机制?

有几种类加载器?双亲委派模型机制?双亲委派解决了什么问题?

答: 引导类加载器 bootstrap classloader;扩展类加载器extensions class loader;应用程序类加载器 application classloader;自定义类加载器 java.lang.classloder

一个类加载器收到类加载请求,不会自己先去加载,而是把请求委托给父类的加载器执行,如果父类加载器还存在父类加载器,则进一步向上委托,依次递归,请求最终到达顶层的启动类加载器,如果父类加载器可以完成类加载,就成功返回,若父类加载器无法完成加载,子加载器才会尝试自己去加载.

解决了避免类的重复加载,确保类的全局唯一,保护程序,防止核心 API 随意篡改

垃圾回收常见问题

什么是GC? 为什么要有 GC?

如何判断一个对象是否存活?如何判断对象是否可以被回收?

Java 中垃圾收集的方法有哪些?

答:引用计数算法 ,GC Root Tracing,标记-清除算法,复制算法,标记-整理算法,分代收集算法

常用的性能优化方式有哪些?

减少新生代大小可以缩短新生代GC停顿时间,因为这样被复制到survivor区域或者被提升的数据更少,但是这样一来yangGC的频率就会很高,而且会有更多的垃圾进入到了老年代。如果增加新生代大小又会导致回收的时间和复制的时间变高,所以一般来说需要在这个中间进行折中。

主要的目的是减小GC的频率和Full GC的次数

jmap -heap 21711

JVM内存模型的相关知识了解多少,比如重排序,内存屏障,happen-before,主内存,工作内存

在Java中,有哪些包是直接定义在堆外内存中的?

答:sun.misc.Unsafe提供了方法来进行堆外内存的分配;用NIO包下的ByteBuffer分配直接内存.

Spring里的bean会被GC吗?

答:取决于两点:1.Bean的作用域;2.Spring容器的状态 。容器处于运行中,singleton 类型Bean不会被GC,prototype 类型Bean会被回收,每使用都new一个,用完就回收

垃圾收集器的性能

串行回收器:Serial、Serial old

并行回收器:ParNew、Parallel Scavenge、Parallel old吞吐量优先

并发回收器:CMS、G1在JDK1.8中默认使用的Parallel Scavenge/Parallel 0ld这一对垃圾回收器。

面试汇总--初中级_第3张图片

Parallel Scavenge:吞吐量优先。

吞吐量=运行用户代码时间 /(运行用户代码时间 + 垃圾收集时间)

5.网络编程与操作系统、Linux

TCP、UDP 协议的区别,TCP/IP协议分层?

1、基于连接与无连接;2、对系统资源的要求(TCP较多,UDP少);3、UDP程序结构较简单;

4、流模式与数据报模式 5、TCP保证数据正确性,UDP可能丢包;6、TCP保证数据顺序,UDP不保证。

UDP协议的最适用场景  对实时性要求较高,对可靠性要求较低的场景,实时聊天(语音、视频聊天),UDP支持广播。 

在浏览器中输入url地址 ->> 显示主页的过程?6个步骤

DNS解析 TCP连接 发送HTTP请求 服务器处理请求并返回HTTP报文 浏览器解析渲染页面

连接结束

HTTP长连接、短连接

  HTTP的长连接和短连接本质上是TCP长连接和短连接。HTTP属于应用层协议,在传输层使用TCP协议,在网络层使用IP协议。IP协议主要解决网络路由和寻址问题,TCP协议主要解决如何在IP层之上可靠的传递数据包,使在网络上的另一端收到发端发出的所有包,并且顺序与发出顺序一致。TCP有可靠,面向连接的特点

     在HTTP/1.0中,默认使用的是短连接。浏览器和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。如果客户端浏览器访问的某个HTML或其JavaScript文件、图像文件、CSS文件等;当浏览器每遇到一个Web资源,就建立一个HTTP会话。 HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头有加入这行代码:

Connection:keep-alive

   在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的 TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接要客户端和服务端都支持长连接。

TCP 三次握手和四次挥手?为啥关闭连接4次,建立连接3次?为什么四次挥手有time_wait状态?

①首先 Client 端发送连接请求报文

②Server 段接受连接后回复 ACK 报文,并为这次连接分配资源。

③Client 接收到 ACK 报文后也向 Server 发生 ACK 报文,并分配资源,TCP 连接就建立了。

第一次挥手:Clien发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。

第二次挥手:Server收到FIN后,发送一个ACK给Client,Server进入CLOSE_WAIT状态。

第三次挥手: Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。

第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,发送ACK给Server,Server进入CLOSED状态,完成四次握手。
因为ACK和SYN(ACK起应答用,而SYN起同步作用)在一个报文里来发送。ACK报文和FIN报文多数情况下都是分开发送的。

一些常见的 Linux 命令了解吗?

解压文件tar -xvzf test.tar.gz  动态查看日志文件tail -f exmaple.log   切换用户su -username   

创建目录mkdir newfolder  当前目录pwd   查看线程 ps –ef|grep tomcat 终止线程kill -9 19979

Cookie和Session的区别?

cookie保存在客户端;session保存在服务器;cookie保存数据类型只是字符串,session可以保存除了资源以外的数据类型;cookie的大小有4K限制,session没有限制;cookie可以被浏览器禁用,session则不能;cookie不安全,可被拷贝进行cookie欺骗,考虑到安全性,重要信息应用session;session在访问量增多时,会增加服务器的负担,从服务器性能方面考虑,非重要信息若需要保留应使用cookie;cookie不设置有效期的话,默认浏览器关闭失效,session服务器默认有效期1440秒,24分钟

cookie禁用,手动通过URL传值、隐藏表单传递Session ID

如果调用方请求你的接口很慢,你怎么排查?

后端接口响应慢分以下2种情况:

某个接口慢: 用SkyWalking。展示每一个与网络有关的耗时,读写数据库、读写Redis、SpringCloud调用、Dubbo调用等。立马定位是哪次操作耗时了。记录SQL语句,可以帮助定位。如果是报错直接用postman请求看报错信息,或盲猜一下代码。 或者查腾讯云ES日志,通过TID查日志。

数据库耗时长: 必要字段加索引,确定是否索引失效了,如果有回表查询,尽量优化为覆盖索引

架构不合理,代码不规范,需要代码评审。

所有访问接口的响应速度很慢。 系统崩溃无响应  压测时查看CPU、内存、load、rt、qps等指标
CPU  定位进程 (命令:top) 找占CPU最高的进程 top命令,记下进程号(PID)。假设最高是:1893
定位线程 (命令:top -Hp 进程号) Java是单进程多线程的  top -Hp 1893。假设最高是:4519
定位代码位置  (命令:jstack)  通过线程定位代码大概位置信息。printf %x 4519。结果为:11a7    jstack 1893 | grep  11a7 -A 30 --color

导致cpu使用飙升的操作  

无限循环的while,经常使用Young GC,频繁的GC
如果访问量很高,可能会导致频繁的GC甚至Full GC。当调用量很大时,内存分配将如此之快以至于GC线程将连续执行,这将导致CPU飙升。锁的不恰当使用。
 

Java--线上问题排查--方法/步骤_IT利刃出鞘的博客-CSDN博客_java线上问题排查

http 1.0 和 http 2.0的区别?

答:http2.x采用二进制格式,http1.x的解析是基于文本;http2.0多路复用,只需一个连接可实现并行http1.x有序并阻塞的;用Header压缩报头,http2.x降低了开销,http2.x让服务器可将响应主动“推送”到客户端缓存

请求类型Get与Post之间的区别?

答:GET在浏览器回退时是无害的,而POST会再次提交请求;

GET产生URL地址可被Bookmark,POST不可以;

GET请求会被浏览器主动cache缓存,而POST不会,除非手动设置;

GET请求只能进行url编码,而POST支持多种编码方式;

GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留;

GET请求在URL中传送的参数有长度限制,而POST没;

对参数的数据类型,GET只接受ASCII字符,而POST没有限制;

GET比POST不安全,因参数暴露在URL上,不能用来传递敏感信息,其实都不安全 HTTP 都可以网络抓包,应该使用 https。

GET参数通过URL传递,POST参数放在Request body中。

什么是restful风格?

每一个URI代表1种资源;
2、客户端使用GET、POST、PUT、DELETE4个表示操作方式的动词对服务端资源进行操作

请求路径相同,但根据不同的参数、请求方式不同而执行不同的方法,产生的结果也不同。

@GetMapping(path = “/add/{a}/{b}”) 等价于 @RequestMapping(path = “/add/{a}/{b}”,method = RequestMethod.GET)

6.MySQL

sql编写和执行顺序

编写: select  from join where group by having order by limit

执行: from-where-groupby-having-select-orderby-limit

面试汇总--初中级_第4张图片

  1. 首先执行 FROM 子句, 从学生成绩表中组装数据源的数据。
  2. 执行 WHERE 子句, 筛选学生成绩表中所有学生的数学成绩不为 NULL 的数据 。
  3. 执行 GROUP BY 子句, 把学生成绩表按 "班级" 字段进行分组。
  4. 计算 avg 聚合函数, 按找每个班级分组求出 数学平均成绩
  5. 执行 HAVING 子句, 筛选出班级 数学平均成绩大于 75 分的。
  6. 执行SELECT语句,返回数据,但别着急,还需要执行后面几个步骤。
  7. 执行 ORDER BY 子句, 把最后的结果按 "数学平均成绩" 进行排序。
  8. 执行LIMIT ,限制仅返回3条数据。结合ORDER BY 子句,即返回所有班级中数学平均成绩的前三的班级及其数学平均成绩。

有哪些索引?MySQL中聚镞索引和非聚镞索引的不同,性能如何?

1,普通索引:普通索引是最基本的索引,它没有任何限制,值可以为空;仅加速查询。
2,唯一索引: 索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。
3,主键索引:主键索引是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。
4,组合索引:多个字段上创建的索引,查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引遵循最左前缀原则。
5,全文索引:全文索引主要用来查找文本中的关键字,而不是直接与索引中的值相比较。fulltext索引跟其它索引大不相同,它更像是一个搜索引擎,而不是简单的where语句的参数匹配

①聚集索引:使用聚集索引的表,记录和索引保持着一致的顺序,这样只要找到索引的值就能直接从叶子节点里面获取到全部列数据   用拼音查 ,快于非聚集索引

②非聚集索引:记录和索引的顺序往往不同,可理解为索引下面的叶子节点存储的还是索引,想要获得真正的列数据,还需要再一次查询;  用部首查

InnoDB是聚集索引,MyISAM 是非聚集索引  

innodb支持事务,myisam不支持事务,innodb有外键,myisam没有外键。 innodb行锁,myisam表锁。

ACID是什么?怎么保证?

  • 原子性是由 undolog 日志来保存历史版本数据,它记录了需要回滚的日志信息。
  • 持久性由 redolog来进行保证,修改数据时MySQL先把这条记录所在的「页」(也就是B+树上的叶子节点)找到,把该页加载到内存,在内存中修改。写一份redo log,redo log记这次在某个页上做了什么修改、执行了什么sql。
  • 隔离性事务之间彼此是不会受相互打扰的,相互隔离的,通过 MVCC 技术
  • 保证原子性、持久化、隔离性都是为了保证一致性

Mysql执行计划怎么看?(type字段)

列名

含义

id

SELECT查询的序列标识符

select_type

SELECT关键字对应的查询类型 

table

用到的表名

partitions

匹配的分区,对于未分区的表,值为 NULL

type

表的访问方法

possible_keys

可能用到的索引

key

实际用到的索引

key_len

所选索引的长度

ref

当使用索引等值查询时,与索引作比较的列或常量

rows

预计要读取的行数

filtered

按表条件过滤后,留存的记录数的百分比

Extra

附加信息

联合索引什么情况下会失效? 什么情况索引会失效?最左匹配原则是什么?

CREATE INDEX index_name ON table_name (column_list)

查询联合索引从必须从最左侧开始,跳过后面的则失效,和sql语句位置没关系。

  • 减少或者避免 select *
  • 在使用+like ‘%abc…’ 开头会导致索引失效,全盘扫描
  • 在索引列上做计算、函数、类型转换会导致索引失效
  • 存储引擎不能使用索引范围右边的列
  • is null not is null 无法使用索引

MySQL有哪几种隔离级别?默认是哪个?

read-uncommitted 读未提交:在该级别,所有的事务都可以看到其他未提交事务的执行结果,本隔离级别很少用于实际应用,因为它的性能不比其他级别好多少。读取未提交的数据,也称之为脏读。

read-committed 读提交内容:这是大多数数据库系统的默认隔离级别(但不是MYSQL默认的),它满足了隔离的简单定义:一个事务只能看见已提交事务所做的改变。也支持所谓的不可重复读。

repeatable-read 可重读:是MYSQL默认的,确保统一事务的多个实例在并发读取数据时,会看到同样的数据行。

serializable 可串行化:这是最高的隔离级别,他通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简而言之,他是在每个读的数据行上加上共享锁。在这个级别可能导致大量的超时现象和锁竞争。

低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。
 

幻读是什么?RR隔离级别能解决幻读吗?那RR隔离级别是怎么解决幻读的?

在RR级别使用MVCC和next-key临键锁防止幻读 

锁住的是索引本身以及索引之前的间隙,是一个左开右闭的区间。当 SQL 执行按照非唯一索引进行数据的检索时,会给匹配到行上加上临键锁。

InnoDB 实现MVCC,是通过Read View+ Undo Log 实现的,Undo Log 保存了历史快照,Read View可见性规则帮助判断当前版本的数据是否可见。

  1. 获取事务自己的版本号,即事务ID
  2. 获取Read View
  3. 查询得到的数据,然后Read View中的事务版本号进行比较。
  4. 如果不符合Read View的可见性规则, 即就需要Undo log中历史快照;
  5. 最后返回符合规则的数据

 幻读: 第一个事务对一个表数据进行修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表数据,这种修改是向表中插入一行新数据。操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。

mysql的锁机制行锁、表锁、间隙锁、意向锁分别是做什么的?

按照粒度划分:行锁、表锁、间隙锁
行锁:每次操作锁住一行或多行记录,锁定粒度最小,发生锁冲突概率最低,并发读最高。
表锁:每次锁住整张表。锁定粒度大,发生冲突的概率最高,并发值最低。
间隙锁:每次锁定相邻的一组记录,锁定粒度结余行锁和表锁之间。

按操作类型可分为:读锁和写锁
读锁(S锁):共享锁,针对同一份数据,多个事务可以对其添加读锁,其他事务无法进行修改数据(其他事务无法添加写锁)。 写法:SELECT … LOCK IN SHARE MODE

写锁(X锁):排他锁,针对同一份数据,在当前上锁事务完成前,会阻塞其他事务的写锁或读锁。(仍旧是可以进行查询操作的,只不过不能加锁)。写法:SELECT … FOR UPDATE

意向锁:不是真正意义上的锁,只是用于辅助判断,提高加锁判断效率。在对表记录添加S或X锁之前,会先对表添加IS或IX锁,方便下一次加锁前条件判断,不必遍历事务判断。
 

如何避免数据库死锁?怎么解决数据库死锁?

 1、资源不共享,需要只由一个进程或线程使用 2、请求且保持,已锁定的资源保持着不释放 3、不剥夺,申请到的资源不能被别人剥夺 4、循环等待 

(1)尽量避免并发地执行涉及到修改数据的语句。 比如有一个修改上百条记录的update语句,我们可以修改成每10条一个update语句,或者干脆就每条记录一个update语句。
(2)要求每个事务一次就将所有要使用的数据全部加锁,否则就不予执行。 
(3)预规定封锁顺序,所有的事务都须按这个顺序对数据执行封锁。如不同的过程在事务内部对对象的更新执行顺序应尽量保持一致。 
(4) 将事务分割成为几个小事务来执行。【比如说把复杂的多表查询分散成多次单表查询】 
(5)数据存储空间离散法。数据存储空间离散法是指采取各种手段,将逻辑上在一个表中的数据分散到若干离散的空间上去,以便改善对表的访问性能。主要通过将大表按行或列分解为若干小表,或按不同的用户群分解两种方法实现。分散“数据热点”,如果数据不是太经常被访问,那么死锁就不会太经常发生。 
(6)将经常更新的数据库和查询数据库分开,主从复制,读写分离。

数据库的查询优化、排查慢sql以及sql优化是怎么进行的?详细点说明下

尽量避免全表扫描,首先应考虑在 WHERE 及 ORDER BY 涉及的列上建立索引。

尽量避免在 where 子句中对字段进行 null 值判断和表达式,将导致引擎放弃索引进行全表扫描

一个表的索引数最好不要超过 6 个

InnoDB中哈希索引实现机制是什么?索引为什么用的是b+ tree而不是b-tree、红黑树?

二叉查找树:解决了排序基本问题,但无法保证平衡,会退化为链表;
平衡二叉树:通过旋转解决平衡问题,但旋转效率太低;
红黑树:通过舍弃严格的平衡和引入红黑节点,解决了平衡二叉树旋转效率过低的问题,但在磁盘等场景下,树太高,IO 次数太多;
B 树:通过将二叉树改为多路平衡查找树,解决了树过高的问题;
B+ 树:在 B 树的基础上,将非叶节点改造为不存储数据的纯索引节点,进一步降低了树的高度;此外将叶节点使用指针连接成链表,范围查询更加高效。

 

数据库的乐观锁和悲观锁

乐观锁 : 增加一个版本号的字段version(数字类型),每次更新一行记录,都使得该行版本号加一,开始更新之前先获取version的值,更新提交的时候带上之前获取的version值与当前version值作比较,如果不相等则说明version值发生了变化则检测到了并发冲突,本次操作执行失败,如果相等则操作执行成功。写操作不频繁。DB的读大于写的业务场景

UPDATE TABLE 
SET columnA = 1, VERSION = VERSION + 1  WHERE ID = #{ID}   AND VERSION = #{oldVersion}

悲观锁:每一次行数据的访问都是独占的,只有当正在访问该行数据的请求事务提交以后,其他请求才能依次访问该数据,否则将阻塞等待锁的获取。悲观锁可以严格保证数据访问的安全。所以比较适合用在DB写大于读的情况。SELECT * FROM TABLE WHERE ID = 1 FOR UPDATE  
对于大表的常见优化手段说一下?

1.限定数据的范围  禁止不带任何限制数据范围条件的查询语句

2. 读/写分离  3、分表分库 3.1、垂直分区  3.2、水平分区

水平分表后如何确定数据在哪个表里

 分库分表中间件了,比如Apache ShardingSphere   hash取模 定位表  雪花算法 订单号作为shardingkey

更新一条sql语句过程

  • 首先执行器根据 MySQL 的执行计划来查询数据,先是从缓存池中查询数据,如果没有就会去数据库中查询,如果查询到了就将其放到缓存池中
  • 在数据被缓存到缓存池的同时,会写入 undo log 日志文件
  • 更新的动作是在 BufferPool 中完成的,同时会将更新后的数据添加到 redo log buffer 中
  • 完成之后就可以提交事务了,在提交的同时会做以下三件事:
    (1)将redo log buffer中的数据刷入到 redo log 文件中
    (2)将本次操作记录写入到bin log文件中
    (3)将bin log文件名字和更新内容在bin log中的位置记录到 redo log 中,同时在 redo log 最后添加commit标记。
  • 至此整个更新事务已经完成。

mysql存储过程

mysql> DELIMITER //

mysql> CREATE PROCEDURE ShowStuScore()

    -> BEGIN

    -> SELECT * FROM tb_students_score;

    -> END //

Query OK, 0 rows affected (0.09 sec)

7.Spring和SpringBoot,设计模式

Spring由哪些模块组成?

Spring-Core,Spring-Context,Spring-Aop,Spring-Dao,Spring-Web,Spring Web MVC,
Spring-ORM


 Spring框架有哪些不同类型的事件?

(1)上下文更新事件(ContextRefreshedEvent):在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。

(2)上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。

(3)上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。

4)上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。

(5)请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。

6 继承ApplicationEvent自定义事件 ,实现ApplicationListener接口,publishEvent()发布

Spring Bean 的作用域和生命周期?

作用域   singleton:唯一 bean 实例,Spring 中的 bean 默认都是单例的。无状态 

prototype:每次请求都会创建一个新的 bean 实例。(多例)
request:每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效。
session:每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。

 生命周期: 

实例化 Instantiation 属性赋值 Populate,初始化 Initialization,销毁 Destruction

单例模式了解吗?有哪几种?写个单例模式?Spring和普通单例模式有啥区别? 单例怎么保证线程安全?

饿汉式、懒汉式、内部类懒汉式、枚举型    

  • 设计模式单例,在整个应用中只有一个实例
  • spring单例,在一个IoC容器中只有一个实例

ThreadLocal是一个本地线程副本变量工具类。主要用于将私有线程和该线程存放的副本对象做一个映射,各个线程之间的变量互不干扰,在高并发场景下,可以实现无状态的调用,特别适用于各个线程依赖不通的变量值完成操作的场景。

BeanFactory和ApplicationContext有啥区别?

BeanFactroy采用的是延迟加载形式注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。

ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可发现Spring中存在的配置错误,检查所依赖属性是否注入。ApplicationContext启动后预载入所有的实例Bean,通过预载入单实例bean ,确保需要的时候,不用等待,因为它们已经创建好

Spring 事务中的隔离级别?事务管理类型(编程式和声明式)

   1.DEFAULT:  使用数据库默认的事务隔离级别.另外四个与JDBC的隔离级别相对应。
  2.UNCOMMITTED: 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
  3.COMMITTED: 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。
  4.REPEATABLE_READ: 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
  5.SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。

 

  脏读: 当事务正访问数据,并对数据进行了修改,而这种修改还没提交到数据库,这时另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没提交的数据, 那么另外一 个事务读到的数据是脏数据,依据脏数据所做的操作可能是不正确的。
  不可重复读: 在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。 那么,在第一个事务中的两次读数据之间,由于第二个事务修改,第一个事务两次读到的数据是不一样的。就发生了在一个事务内两次读到的数据是不一样的,称为是不可重复读。

 

Spring 事务中的事务传播行为?

支持当前事务
REQUIRED :如果当前存在事务,则加入该事务。如果当前没有事务,则创建一个新的事务

SUPPORTS:如果当前存在事务,则加入该事务 。如果当前没事务, 则以非事务的方式继续运行

MANDATORY :如果当前存在事务,则加入该事务 。如果当前没有事务,则抛出异常

不支持当前事务
REQUIRES_NEW :创建一个新事务,如果当前存在事务,则把当前事务挂起

NOT_SUPPORTED :以非事务方式运行,如果当前存在事务,则把当前事务挂起

NEVER :以非事务方式运行,如果当前存在事务,则抛出异常

其他 NESTED :如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来执行 。如果当前没有事务,则该取值等价于REQUIRED
 

SpringAOP了解吗?有哪几种实现方式?  ***

 通过实现spring提供的接口实现
 通过自定义方式织入实现
 通过注解的方式实现    使用AspectJ注解技术实现AOP功能

@Aspect注解,使类成为切面类,并被AOP识别和加载。

在类方法头部加@Pointcut注解,使该方法称为一个切入点。@Pointcut注解的execution表达式定义该方法在什么位置切入。

分别是@Before、@After、@Around、@AfterReturning、@AfterThrowing。被@Before注解的方法在被切入方法执行之前执行;被@After注解的方法在被切入方法执行之后执行,不考虑是否执行成功;被@AfterReturning注解的方法在被切入方法执行成功之后执行,当被切入方法发生异常时,该方法不被执行;被@Around注解的方法在被切入方法执行之前和执行之后都执行;被@AfterThrowing注解的方法,只有当被切入方法执行过程发生异常时才会执行。

AOP使用org.aspectj.lang.JoinPoint类型,用于获取被切入点传入的参数,任何切入方法的第一个参数都可以是JoinPoint。

before:前置通知(应用:各种校验):在方法执行前执行,如果通知抛出异常,阻止方法运行
afterReturning:后置通知(应用:常规数据处理):方法正常返回后执行,如果方法中抛出异常,通知无法执行,必须在方法执行后才执行,所以可以获得方法的返回值。
around:环绕通知(应用:十分强大,可以做任何事情):方法执行前后分别执行,可以阻止方法的执行,必须手动执行目标方法
afterThrowing:抛出异常通知(应用:包装异常信息):方法抛出异常后执行,如果方法没有抛出异常,无法执行
after:最终通知(应用:清理现场):方法执行完毕后执行,无论方法中是否出现异常

 

依赖注入IOC是什么?

创建对象权利交给spring容器

Spring 框架中都用到了哪些设计模式?

工厂模式:Spring使用工厂模式,通过BeanFactory和ApplicationContext创建对象;单例模式:Bean默认单例模式;策略模式:Resource的实现类,针对不同的资源文件,实现了不同方式的资源获取策略;代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;模板方法:可以将相同部分的代码放在父类中,而将不同的代码放入不同的子类中,用来解决代码重复的问题.比如RestTemplate, JmsTemplate, JpaTemplate;适配器模式:Spring AOP的增强或通知(Advice)使用到适配器模式,Spring MVC中也是用到了适配器模式适配Controller;观察者模式:Spring事件驱动模型就是观察者模式的一个经典应用;桥接模式:根据客户的需求动态切换不同的数据源。项目需要连接多数据库,每次访问中根据需要去访问不同数据库.


双检锁

懒汉有线程安全问题

public class Singleton {  
    private volatile Singleton instance = null;  
    public Singleton getInstance() {  
        if (instance == null) {  
            synchronized(this) {  
                if (instance == null) {  
                    instance = new Singleton();  
                }  
            }  
        }  
        return instance;  
    }  
}  

Springboot的Run方法过程,自动配置原理?Starter 的工作原理是什么?

SpringBootConfiguration:包含了Configuration注解,实现配置文件
ComponentScan:指定扫描范围
EnableAutoConfiguration:通过源码可以知道,该注解使用Import引入了AutoConfigurationImportSelector类,而AutoConfigurationImportSelector类通过SpringFactortisLoader加载了所有jar包的MATE-INF文件夹下面的spring.factories文件,spring.factories包含了所有需要装配的XXXConfiguration类的全限定名。XXXConfiguration类包含了实例化该类需要的信息,比如说如果这是个数据源Configuration类,那么就应该有数据库驱动、用户名、密码等等信息。
总结:
1.提供了一个配置类,该配置类定义了我们需要的对象的实例化过程;
2.提供了一个spring.factories文件,包含了配置类的全限定名;
3.将配置类和spring.factories文件打包为一个启动器starter;
4.程序启动时通过加载starter.jar包的spring.factories文件信息,然后通过反射实例化文件里面的类。

Spring的怎么用三级缓存解决循环依赖? 

第一级缓存:singletonObjects,存放经过初始化后的bean。当通过名字获取bean的时候,如果这个名字对应的bean在第一级缓存中,则直接从第一级缓存中获取返回,这样就不会导致多次创建bean了。
第二级缓存:earlySingletonObjects,存放不完整的bean,对象就是最终的对象,但是对象的属性可能不完整。当填充依赖对象的时候,先从一级二级缓存中查找,如果找到了,则直接拿出来赋值。如果没找到,则使用三级缓存创建,然后放入到二级缓存中。
场景:A依赖B和C,B依赖A,C依赖A。A填充B的时候要构造B,然后填充B的时候需要A,所以B就从一级二级缓存中查找A,没找到,就使用三级缓存创建最终的A填充到B的A属性。A填充C的时候要构造C,然后填充C的时候需要A,所以C就从一级二级缓存中查找A,找到了,直接填充。
第三级缓存:singletonFactories,存放bean对应的工厂对象,主要得到根据原始对象进行AOP之后的代理对象。在bean实例化之后就将生成最终对象的ObjectFactory对象放入到三级缓存中,当从二级缓存中获取不到对象的时候,就根据这个ObjectFactory生成最终对象。

 

SpringMVC的处理流程? 

面试汇总--初中级_第5张图片

DispatcherServlet实现frameservlet中的doService()方法,doService()调用doDispatch()方法。

mybatis分页插件的运行原理是什么?如何写一个插件?  MyBatis 分页插件 - PageHelper

分页插件的原理就是使用MyBatis提供的插件接口,实现自定义插件,在插件的拦截方法内,拦截待执行的SQL,然后根据设置的dialect(方言),和设置的分页参数,重写SQL ,生成带有分页语句的SQL,执行重写后的SQL,从而实现分页。实现一个关键的方法就是intercept,从而实现拦截

 ParameterHandler、ResultSetHandler、StatementHandler、Executor 这 4 种接口的插件,Mybatis 使用 JDK 的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这 4 种接口对象的方法时,就会进入拦截方法,具体就是 InvocationHandler 的 invoke()
方法,只会拦截那些你指定需要拦截的方法。

 自定义实现: 
public interface Interceptor {
  //拦截的方法
  Object intercept(Invocation invocation) throws Throwable;
  //返回拦截器的代理对象
  Object plugin(Object target);
  //设置一些属性
  void setProperties(Properties properties);

}

#{}和${}区别  

1.#{}是预编译处理,$ {}是字符串替换。

2.mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;mybatis在处理 $ { } 时,就是把 ${ } 替换成变量的值。

3.使用 #{} 可以有效的防止SQL注入,提高系统安全性。

接口的方法如何和xml绑定

namespace + bean id

mapper.xml文件中常见标签
除了常见的