一、Java基础篇
1.接口和抽象类的区别
相似点:
(1)接口和抽象类都不能被实例化
(2)实现接口或继承抽象类的普通子类都必须实现这些抽象方法
不同点:
(1)抽象类可以包含普通方法和代码块,接口里只能包含抽象方法,静态方法和默认方法,
(2)抽象类可以有构造方法,而接口没有
(3)抽象类中的成员变量可以是各种类型的,接口的成员变量只能是 public static final 类型的,并且必须赋值
2.重载和重写的区别
重载发生在同一个类中,方法名相同、参数列表、返回类型、权限修饰符可以不同
重写发生在子类中,方法名相、参数列表、返回类型都相同,权限修饰符要大于父类方法,声明异常范围要小于父类方法,但是final和private修饰的方法不可重写
3.==和equals的区别
==比较基本类型,比较的是值,==比较引用类型,比较的是内存地址
equlas是Object类的方法,本质上与==一样,但是有些类重写了equals方法,比如String的equals被重写后,比较的是字符值,另外重写了equlas后,也必须重写hashcode()方法
4.异常处理机制
(1)使用try、catch、finaly捕获异常,finaly中的代码一定会执行,捕获异常后程序会继续执行
(2)使用throws声明该方法可能会抛出的异常类型,出现异常后,程序终止
5.HashMap原理
1.HashMap在Jdk1.8以后是基于数组+链表+红黑树来实现的,特点是,key不能重复,可以为null,线程不安全
2.HashMap的扩容机制:
HashMap的默认容量为16,默认的负载因子为0.75,当HashMap中元素个数超过容量乘以负载因子的个数时,就创建一个大小为前一次两倍的新数组,再将原来数组中的数据复制到新数组中。当数组长度到达64且链表长度大于8时,链表转为红黑树
3.HashMap存取原理:
(1)计算key的hash值,然后进行二次hash,根据二次hash结果找到对应的索引位置
(2)如果这个位置有值,先进性equals比较,若结果为true则取代该元素,若结果为false,就使用高低位平移法将节点插入链表(JDK8以前使用头插法,但是头插法在并发扩容时可能会造成环形链表或数据丢失,而高低位平移发会发生数据覆盖的情况)
篇幅限制下面就只能给大家展示小册部分内容了,这边整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记的【点击此处即可】免费获取
6.想要线程安全的HashMap怎么办?
(1)使用ConcurrentHashMap
(2)使用HashTable
(3)Collections.synchronizedHashMap()方法
7.ConcurrentHashMap原如何保证的线程安全?
JDK1.7:使用分段锁,将一个Map分为了16个段,每个段都是一个小的hashmap,每次操作只对其中一个段加锁
JDK1.8:采用CAS+Synchronized保证线程安全,每次插入数据时判断在当前数组下标是否是第一次插入,是就通过CAS方式插入,然后判断f.hash是否=-1,是的话就说明其他线程正在进行扩容,当前线程也会参与扩容;删除方法用了synchronized修饰,保证并发下移除元素安全
8.HashTable与HashMap的区别
(1)HashTable的每个方法都用synchronized修饰,因此是线程安全的,但同时读写效率很低
(2)HashTable的Key不允许为null
(3)HashTable只对key进行一次hash,HashMap进行了两次Hash
(4)HashTable底层使用的数组加链表
9.ArrayList和LinkedList的区别
ArratList的底层使用动态数组,默认容量为10,当元素数量到达容量时,生成一个新的数组,大小为前一次的1.5倍,然后将原来的数组copy过来;因为数组在内存中是连续的地址,所以ArrayList查找数据更快,由于扩容机制添加数据效率更低
LinkedList的底层使用链表,在内存中是离散的,没有扩容机制;LinkedList在查找数据时需要从头遍历,所以查找慢,但是添加数据效率更高
10.如何保证ArrayList的线程安全?
(1)使用collentions.synchronizedList()方法为ArrayList加锁
(2)使用Vector,Vector底层与Arraylist相同,但是每个方法都由synchronized修饰,速度很慢
(3)使用juc下的CopyOnWriterArrayList,该类实现了读操作不加锁,写操作时为list创建一个副本,期间其它线程读取的都是原本list,写操作都在副本中进行,写入完成后,再将指针指向副本。
11.String、StringBuffer、StringBuilder的区别
String 由 char[] 数组构成,使用了 final 修饰,对 String 进行改变时每次都会新生成一个 String 对象,然后把指针指向新的引用对象。
StringBuffer可变并且线程安全
StringBuiler可变但线程不安全。
操作少量字符数据用 String;单线程操作大量数据用 StringBuilder;多线程操作大量数据用 StringBuffer。
12.hashCode和equals
hashCode()和equals()都是Obkect类的方法,hashCode()默认是通过地址来计算hash码,但是可能被重写过用内容来计算hash码,equals()默认通过地址判断两个对象是否相等,但是可能被重写用内容来比较两个对象
所以两个对象相等,他们的hashCode和equals一定相等,但是hashCode相等的两个对象未必相等
如果重写equals()必须重写hashCode(),比如在HashMap中,key如果是String类型,String如果只重写了equals()而没有重写hashcode()的话,则两个equals()比较为true的key,因为hashcode不同导致两个key没有出现在一个索引上,就会出现map中存在两个相同的key
13.面向对象和面向过程的区别
面向对象有封装、继承、多态性的特性,所以相比面向过程易维护、易复用、易扩展,但是因为类调用时要实例化,所以开销大性能比面向过程低
4.深拷贝和浅拷贝
浅拷贝:浅拷贝只复制某个对象的引用,而不复制对象本身,新旧对象还是共享同一块内存
深拷贝:深拷贝会创造一个一摸一样的对象,新对象和原对象不共享内存,修改新对象不会改变原对对象。
15.多态的作用
多态的实现要有继承、重写,父类引用指向子类对象。它的好处是可以消除类型之间的耦合关系,增加类的可扩充性和灵活性。
16.什么是反射?
反射是通过获取类的class对象,然后动态的获取到这个类的内部结构,动态的去操作类的属性和方法。
应用场景有:要操作权限不够的类属性和方法时、实现自定义注解时、动态加载第三方jar包时、按需加载类,节省编译和初始化时间;
获取class对象的方法有:class.forName(类路径),类.class(),对象的getClass()
17.Java创建对象得五种方式?
(1)new关键字 (2)Class.newInstance (3)Constructor.newInstance
(4)Clone方法 (5)反序列化
二.Java多线程篇
1.进程和线程的区别,进程间如何通信
进程:系统运行的基本单位,进程在运行过程中都是相互独立,但是线程之间运行可以相互影响。
线程:独立运行的最小单位,一个进程包含多个线程且它们共享同一进程内的系统资源
进程间通过管道、 共享内存、信号量机制、消息队列通信
篇幅限制下面就只能给大家展示小册部分内容了,这边整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记的【点击此处即可】免费获取
2. 什么是线程上下文切换
当一个线程被剥夺cpu使用权时,切换到另外一个线程执行
3.什么是死锁
死锁指多个线程在执行过程中,因争夺资源造成的一种相互等待的僵局
4.死锁的必要条件
互斥条件:同一资源同时只能由一个线程读取
不可抢占条件:不能强行剥夺线程占有的资源
请求和保持条件:请求其他资源的同时对自己手中的资源保持不放
循环等待条件:在相互等待资源的过程中,形成一个闭环
想要预防死锁,只需要破坏其中一个条件即可,比如使用定时锁、尽量让线程用相同的加锁顺序,还可以用银行家算法可以预防死锁
5.Synchrpnized和lock的区别
(1)synchronized是关键字,lock是一个类
(2) synchronized在发生异常时会自动释放锁,lock需要手动释放锁
(3)synchronized是可重入锁、非公平锁、不可中断锁,lock的ReentrantLock是可重入锁,可中断锁,可以是公平锁也可以是非公平锁
(4)synchronized是JVM层次通过监视器实现的,Lock是通过AQS实现的
6.什么是AQS锁?
AQS是一个抽象类,可以用来构造锁和同步类,如ReentrantLock,Semaphore,CountDownLatch,CyclicBarrier。
AQS的原理是,AQS内部有三个核心组件,一个是state代表加锁状态初始值为0,一个是获取到锁的线程,还有一个阻塞队列。当有线程想获取锁时,会以CAS的形式将state变为1,CAS成功后便将加锁线程设为自己。当其他线程来竞争锁时会判断state是不是0,不是0再判断加锁线程是不是自己,不是的话就把自己放入阻塞队列。这个阻塞队列是用双向链表实现的
可重入锁的原理就是每次加锁时判断一下加锁线程是不是自己,是的话state+1,释放锁的时候就将state-1。当state减到0的时候就去唤醒阻塞队列的第一个线程。
7.为什么AQS使用的双向链表?
因为有一些线程可能发生中断 ,而发生中断时候就需要在同步阻塞队列中删除掉,这个时候用双向链表方便删除掉中间的节点
8.有哪些常见的AQS锁
AQS分为独占锁和共享锁
ReentrantLock(独占锁):可重入,可中断,可以是公平锁也可以是非公平锁,非公平锁就是会通过两次CAS去抢占锁,公平锁会按队列顺序排队
Semaphore(信号量):设定一个信号量,当调用acquire()时判断是否还有信号,有就获取一个信号量,没有就阻塞等待其他线程释放信号量,当调用release()时释放一个信号量,唤醒阻塞线程。
应用场景:允许多个线程访问某个临界资源时,如上下车,买卖票
CountDownLatch(倒计数器):给计数器设置一个初始值,当调用CountDown()时计数器减一,当调用await() 时判断计数器是否归0,不为0就阻塞,直到计数器为0。
应用场景:启动一个服务时,主线程需要等待多个组件加载完毕,之后再继续执行
CyclicBarrier(循环栅栏):给计数器设置一个目标值,当调用await() 时会计数+1并判断计数器是否达到目标值,未达到就阻塞,直到计数器达到目标值
应用场景:多线程计算数据,最后合并计算结果的应用场景
9.sleep()和wait()的区别
(1)wait()是Object的方法,sleep()是Thread类的方法
(2)wait()会释放锁,sleep()不会释放锁
(3)wait()要在同步方法或者同步代码块中执行,sleep()没有限制
(4)wait()要调用notify()或notifyall()唤醒,sleep()自动唤醒
10.yield()和join()区别
yield()调用后线程进入就绪状态
A线程中调用B线程的join() ,则B执行完前A进入阻塞状态
11.线程池七大参数
核心线程数:线程池中的基本线程数量
最大线程数:当阻塞队列满了之后,逐一启动
最大线程的存活时间:当阻塞队列的任务执行完后,最大线长的回收时间
最大线程的存活时间单位
阻塞队列:当核心线程满后,后面来的任务都进入阻塞队列
线程工厂:用于生产线程
任务拒绝策略:阻塞队列满后,拒绝任务,有四种策略(1)抛异常(2)丢弃任务不抛异常(3)打回任务(4)尝试与最老的线程竞争
12.Java内存模型
JMM(Java内存模型 )屏蔽了各种硬件和操作系统的内存访问差异,实现让Java程序在各平台下都能达到一致的内存访问效果,它定义了JVM如何将程序中的变量在主存中读取
具体定义为:所有变量都存在主存中,主存是线程共享区域;每个线程都有自己独有的工作内存,线程想要操作变量必须从主从中copy变量到自己的工作区,每个线程的工作内存是相互隔离的
由于主存与工作内存之间有读写延迟,且读写不是原子性操作,所以会有线程安全问题
13.保证并发安全的三大特性?
原子性:一次或多次操作在执行期间不被其他线程影响
可见性:当一个线程在工作内存修改了变量,其他线程能立刻知道
有序性:JVM对指令的优化会让指令执行顺序改变,有序性是禁止指令重排
14.volatile
保证变量的可见性和有序性,不保证原子性。使用了 volatile 修饰变量后,在变量修改后会立即同步到主存中,每次用这个变量前会从主存刷新。
单例模式双重校验锁变量为什么使用 volatile 修饰? 禁止 JVM 指令重排序,new Object()分为三个步骤:为实例对象分配内存,用构造器初始化成员变量,将实例对象引用指向分配的内存;实例对象在分配内存后实才不为null。如果分配内存后还未初始化就先将实例对象指向了内存,那么此时最外层的if会判断实例对象已经不等于null就直接将实例对象返回。而此时初始化还没有完成。
篇幅限制下面就只能给大家展示小册部分内容了,这边整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记的【点击此处即可】免费获取
15.线程使用方式
(1)继承 Tread 类
(2)实现 Runnable 接口
(3)实现 Callable 接口:带有返回值
(4)线程池创建线程
16.ThreadLocal原理
原理是为每个线程创建变量副本,不同线程之间不可见,保证线程安全。每个线程内部都维护了一个Map,key为threadLocal实例,value为要保存的副本。
但是使用ThreadLocal会存在内存泄露问题,因为key为弱引用,而value为强引用,每次gc时key都会回收,而value不会被回收。所以为了解决内存泄漏问题,可以在每次使用完后删除value或者使用static修饰ThreadLocal,可以随时获取value
17.什么是CAS锁
CAS锁可以保证原子性,思想是更新内存时会判断内存值是否被别人修改过,如果没有就直接更新。如果被修改,就重新获取值,直到更新完成为止。这样的缺点是
(1)只能支持一个变量的原子操作,不能保证整个代码块的原子操作
(2)CAS频繁失败导致CPU开销大
(3)ABS问题:线程1和线程2同时去修改一个变量,将值从A改为B,但线程1突然阻塞,此时线程2将A改为B,然后线程3又将B改成A,此时线程1将A又改为B,这个过程线程2是不知道的,这就是ABA问题,可以通过版本号或时间戳解决
18.Synchronized锁原理和优化
Synchronize是通过对象头的markwordk来表明监视器的,监视器本质是依赖操作系统的互斥锁实现的。操作系统实现线程切换要从用户态切换为核心态,成本很高,此时这种锁叫重量级锁,在JDK1.6以后引入了偏向锁、轻量级锁、重量级锁
偏向锁:当一段代码没有别的线程访问,此时线程去访问会直接获取偏向锁
轻量级锁:当锁是偏向锁时,有另外一个线程来访问,会升级为轻量级锁。线程会通过CAS方式获取锁,不会阻塞,提高性能,
重量级锁:轻量级锁自旋一段时间后线程还没有获取到锁,会升级为重量级锁,重量级锁时,来竞争锁的所有线程都会阻塞,性能降低
注意,锁只能升级不能降级
19.如何根据 CPU 核心数设计线程池线程数量
IO 密集型:线程中十分消耗Io的线程数*2
CPU密集型: cpu线程数量
20.AtomicInteger的使用场景
AtomicInteger是一个提供原子操作的Integer类,使用CAS+volatile实来现线程安全的数值操作。
因为volatile禁止了jvm的排序优化,所以它不适合在并发量小的时候使用,只适合在一些高并发程序中使用
三.JVM篇
1.JVM运行时数据区(内存结构)
线程私有区:
(1)虚拟机栈:每次调用方法都会在虚拟机栈中产生一个栈帧,每个栈帧中都有方法的参数、局部变量、方法出口等信息,方法执行完毕后释放栈帧
(2)本地方法栈:为native修饰的本地方法提供的空间,在HotSpot中与虚拟机合二为一
(3)程序计数器:保存指令执行的地址,方便线程切回后能继续执行代码
线程共享区:
(4)堆内存:Jvm进行垃圾回收的主要区域,存放对象信息,分为新生代和老年代,内存比例为1:2,新生代的Eden区内存不够时时发生MinorGC,老年代内存不够时发生FullGC
(5)方法区:存放类信息、静态变量、常量、运行时常量池等信息。JDK1.8之前用持久代实现,JDK1.8后用元空间实现,元空间使用的是本地内存,而非在JVM内存结构中
2.什么情况下会内存溢出?
堆内存溢出:(1)当对象一直创建而不被回收时(2)加载的类越来越多时(3)虚拟机栈的线程越来越多时
栈溢出:方法调用次数过多,一般是递归不当造成
3.JVM有哪些垃圾回收算法?
(1)标记清除算法: 标记不需要回收的对象,然后清除没有标记的对象,会造成许多内存碎片。
(2)复制算法: 将内存分为两块,只使用一块,进行垃圾回收时,先将存活的对象复制到另一块区域,然后清空之前的区域。用在新生代
(3)标记整理算法: 与标记清除算法类似,但是在标记之后,将存活对象向一端移动,然后清除边界外的垃圾对象。用在老年代
4.GC如何判断对象可以被回收?
(1)引用计数法:已淘汰,为每个对象添加引用计数器,引用为0时判定可以回收,会有两个对象相互引用无法回收的问题
(2)可达性分析法:从GCRoot开始往下搜索,搜索过的路径称为引用链,若一个对象GCRoot没有任何的引用链,则判定可以回收
GCRoot有:虚拟机栈中引用的对象,方法区中静态变量引用的对象,本地方法栈中引用的对象
5.典型垃圾回收器
CMS:以最小的停顿时间为目标、只运行在老年代的垃圾回收器,使用标记-清除算法,可以并发收集。
G1 :JDK1.9以后的默认垃圾回收器,注重响应速度,支持并发,采用标记整理+复制算法回收内存,使用可达性分析法来判断对象是否可以被回收。
6.类加载器和双亲委派机制
从父类加载器到子类加载器分别为:
BootStrapClassLoader 加载路径为:JAVA_HOME/jre/lib
ExtensionClassLoader 加载路径为:JAVA_HOME/jre/lib/ext
ApplicationClassLoader 加载路径为:classpath
还有一个自定义类加载器
当一个类加载器收到类加载请求时,会先把这个请求交给父类加载器处理,若父类加载器找不到该类,再由自己去寻找。该机制可以避免类被重复加载,还可以避免系统级别的类被篡改
7.JVM中有哪些引用?
强引用:new的对象。哪怕内存溢出也不会回收
软引用:只有内存不足时才会回收
弱引用:每次垃圾回收都会回收
虚引用:必须配合引用队列使用,一般用于追踪垃圾回收动作
篇幅限制下面就只能给大家展示小册部分内容了,这边整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记的【点击此处即可】免费获取
8.类加载过程
(1)加载 :把字节码通过二进制的方式转化到方法区中的运行数据区
(2)连接:
验证:验证字节码文件的正确性。
准备:正式为类变量在方法区中分配内存,并设置初始值,final类型的变量在编译时已经赋值了
解析:将常量池中的符号引用(如类的全限定名)解析为直接引用(类在实际内存中的地址)
(3)初始化 :执行类构造器(不是常规的构造方法),为静态变量赋初值并初始化静态代码块。
9.JVM类初始化顺序
父类静态代码块和静态成员变量->子类静态代码块和静态成员变量->父类代码块和普通成员变量->父类构造方法->子类代码块和普成员变量->子类构造方法
10..对象的创建过程
(1)检查类是否已被加载,没有加载就先加载类
(2)为对象在堆中分配内存,使用CAS方式分配,防止在为A分配内存时,执行当前地址的指针还没有来得及修改,对象B就拿来分配内存。
(3)初始化,将对象中的属性都分配0值或null
(4)设置对象头
(5)为属性赋值和执行构造方法
11.对象头中有哪些信息
对象头中有两部分,一部分是MarkWork,存储对象运行时的数据,如对象的hashcode、GC分代年龄、GC标记、锁的状态、获取到锁的线程ID等;另外一部分是表明对象所属类,如果是数组,还有一个部分存放数组长度
12.JVM内存参数
-Xmx[]:堆空间最大内存
-Xms[]:堆空间最小内存,一般设置成跟堆空间最大内存一样的
-Xmn[]:新生代的最大内存
-xx:[survivorRatio=3]:eden区与from+to区的比例为3:1,默认为4:1
-xx[use 垃圾回收器名称]:指定垃圾回收器
-xss:设置单个线程栈大小
一般设堆空间为最大可用物理地址的百分之80
13.GC的回收机制和原理
GC的目的实现内存的自动释放,使用可达性分析法判断对象是否可回收,采用了分代回收思想,
将堆分为新生代、老年代,新生代中采用复制算法,老年代采用整理算法,当新生代内存不足时会发生minorGC,老年代不足时会发送fullGC
四.Mysql篇
1.MyIsAm和InnoDB的区别
InnoDB有三大特性,分别是事务、外键、行级锁,这些都是MyIsAm不支持的,
另外InnoDB是聚簇索引,MyIAm是非聚簇索引,
InnoDB不支持全文索引,MyIAm支持
InnoDB支持自增和MVCC模式的读写,MyIAm不支持
MyIsAM的访问速度一般InnoDB快,差异在于innodb的mvcc、行锁会比较消耗性能,还可能有回表的过程(先去辅助索引中查询数据,找到数据对应的key之后,再通过key回表到聚簇索引树查找数据)
2.mysql事务特性
原子性:一个事务内的操作统一成功或失败
一致性:事务前后的数据总量不变
隔离性:事务与事务之间相互不影响
持久性:事务一旦提交发生的改变不可逆
3.事务靠什么保证
原子性:由undolog日志保证,他记录了需要回滚的日志信息,回滚时撤销已执行的sql
一致性:由其他三大特性共同保证,是事务的目的
隔离性:由MVCC保证
持久性:由redolog日志和内存保证,mysql修改数据时内存和redolog会记录操作,宕机时可恢复
4.事务的隔离级别
在高并发情况下,并发事务会产生脏读、不可重复读、幻读问题,这时需要用隔离级别来控制
读未提交: 允许一个事务读取另一个事务已提交的数据,可能出现不可重复读,幻读。
读提交: 只允许事务读取另一个事务没有提交的数据可能出现不可重复读,幻读。
可重复读: 确保同一字段多次读取结果一致,可能出现欢幻读。
可串行化: 所有事务逐次执行,没有并发问日
Inno DB 默认隔离级别为可重复读级别,分为快照度和当前读,并且通过间隙锁解决了幻读问题。
5.什么是快照读和当前读
*快照读读取的是当前数据的可见版本,可能是会过期数据,不加锁的select就是快照都
*当前读读取的是数据的最新版本,并且当前读返回的记录都会上锁,保证其他事务不会并发修改这条记录。如update、insert、delete、select for undate(排他锁)、select lockin share mode(共享锁) 都是当前读
篇幅限制下面就只能给大家展示小册部分内容了,这边整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记的【点击此处即可】免费获取
6.MVCC是什么
MVCC是多版本并发控制,为每次事务生成一个新版本数据,每个事务都由自己的版本,从而不加锁就决绝读写冲突,这种读叫做快照读。只在读已提交和可重复读中生效。
实现原理由四个东西保证,他们是
undolog日志:记录了数据历史版本
readView:事务进行快照读时动态生成产生的视图,记录了当前系统中活跃的事务id,控制哪个历史版本对当前事务可见
隐藏字段DB_TRC_ID: 最近修改记录的事务ID
隐藏字段DB_Roll_PTR: 回滚指针,配合undolog指向数据的上一个版本
7.MySQL有哪些索引
主键索引:一张表只能有一个主键索引,主键索引列不能有空值和重复值
唯一索引:唯一索引不能有相同值,但允许为空
普通索引:允许出现重复值
组合索引:对多个字段建立一个联合索引,减少索引开销,遵循最左匹配原则
全文索引:myisam引擎支持,通过建立倒排索引提升检索效率,广泛用于搜索引擎
8.聚簇索引和非聚簇索引的区别
聚簇索引:聚簇索引的叶子节点存放的是主键值和数据行;辅助索引(在聚簇索引上创建的其它索引)的叶子节点存放的是主键值或指向数据行的指针。
优点:根据索引可以直接获取值,所以他获取数据更快;对于主键的排序查找和范围查找效率更高;
缺点:如果主键值很大的话,辅助索引也会变得很大;如果用uuid作为主键,数据存储会很稀疏;修改主键或乱序插入会让数据行移动导致页分裂;所以一般我们定义主键时尽量让主键值小,并且定义为自增和不可修改。
非聚簇索引(辅助索引):叶子节点存放的是数据行地址,先根据索引找到数据地址,再根据地址去找数据
他们都是b+数结构
9.MySQL如何做慢SQL优化
可以查看执行计划分析数据的扫描类型、索引是否生效,常见的慢查询优化有:
(1)尽量减少select的数据列,尽量使用覆盖索引
(2)orderby查找时使用索引进行排序,否则的话需要进行回表
(3)groupby查询时,同样要用索引,避免使用到临时表
(4)分页查询时,如果limit 后面的数字太大,可以使用子查询查出主键,再limit主键后n条数据就能走覆盖索引
(5) 使用复杂查询时,使用关联查询来代替子查询,并且最好使用内连接
(6)使用count函数时直接使用count的话count(*)的效率最高
count(*)或count(唯一索引)或count(数字):表中总记录数,count(字段)不会统计null
(7) 在写update语句时,where条件要使用索引,否则会锁会从行锁升级为表锁
(8)表中数据是否太大,是不是要分库分表
10.为什么要用内连接而不用外连接?
用外连接的话连接顺序是固定死的,比如left join,他必须先对左表进行全表扫描,然后一条条到右表去匹配;而内连接的话mysql会自己根据查询优化器去判断用哪个表做驱动。
子查询的话同样也会对驱动表进行全表扫描,所以尽量用小表做驱动表。
11.MySQL整个查询的过程
(1)客户端向 MySQL 服务器发送一条查询请求
(2)服务器首先检查查询缓存,如果命中缓存,则返回存储在缓存中的结果。否则进入下一阶段
(3)服务器进行 SQL 解析、预处理、再由优化器生成对应的执行计划
(4)MySQL 根据执行计划,调用存储引擎的 API 来执行查询
(5)将结果返回给客户端,同时缓存查询结果
注意:只有在8.0之前才有查询缓存,8.0之后查询缓存被去掉了
12.执行计划中有哪些字段?
我们想看一个sql的执行计划使用的语句是explain+SQL,表中的字段包括:
type:扫描类型,效率从底到高为ALL(全表扫描)>index(全索引扫描,我们的需要的数据在索引中可以获取)>range(使用索引进行范围查找)>ref(使用非唯一索引列进行了关联查询)> eq_ref (使用唯一索引进行关联查询)>const(使用唯一索引查询一行数据)>system(表中只有一行数据)
extra(额外的):mysql如何查询额外信息,常见的有:
filesort:在排序缓冲区中进行排序,需要回表查询数据
index:表示使用覆盖索引
index scan:排序时使用了索引排序,但如果是按照降序排序的话就会使用反向扫描索引
temporary:查询时要建立一个临时表存放数据
rows:找到了多少行数据
key:实际使用到的索引
id:select查询的优先级,id越大优先级越高,子查询的id一般会更大
select_type:查询的类型,是普通查询还是联合查询还是子查询,常见类型有simple(不包含子查询),primary(标记复杂查询中最外层的查询),union(标记primart只后子查询)
table:者一行的数据是数哪张表的
possible_keys(可能的):当前查询语句可能用到的索引,可能为null(如果用了索引但是为null有可能是表数据太少innodb认为全表扫描更快)
篇幅限制下面就只能给大家展示小册部分内容了,这边整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记的【点击此处即可】免费获取
ref(编号):显示索引的哪一行被使用了
13.哪些情况索引会失效
(1)where条件中有or,除非所有查询条件都有索引,否则失效
(2)like查询用%开头,索引失效
(3)索引列参与计算,索引失效
(4)违背最左匹配原则,索引失效
(5)索引字段发生类型转换,索引失效
(6)mysql觉得全表扫描更快时(数据少),索引失效
14.B和B+数的区别,为什么使用B+数
二叉树:索引字段有序,极端情况会变成链表形式
AVL数:树的高度不可控
B数:控制了树的高度,但是索引值和data都分布在每个具体的节点当中,若要进行范围查询,要进行多次回溯,IO开销大
B+树:非叶子节点只存储索引值,叶子节点再存储索引+具体数据,从小到大用链表连接在一起,范围查询可直接遍历不需要回溯7
15.MySQL有哪些锁
基于粒度:
*表级锁:对整张表加锁,粒度大并发小
*行级锁:对行加锁,粒度小并发大
*间隙锁:间隙锁,锁住表的一个区间,间隙锁之间不会冲突只在可重复读下才生效,解决了幻读
基于属性:
*共享锁:又称读锁,一个事务为表加了读锁,其它事务只能加读锁,不能加写锁
*排他锁:又称写锁,一个事务加写锁之后,其他事务不能再加任何锁,避免脏读问题
16.Mysql内连接、左连接、右连接的区别
内连接取量表交集部分,左连接取左表全部右表匹部分,右连接取右表全部坐表匹部分
篇幅限制下面就只能给大家展示小册部分内容了,这边整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记的【点击此处即可】免费获取
18.如何设计数据库?
(1)抽取实体,如用户信息,商品信息,评论
(2)分析其中属性,如用户信息:姓名、性别...
(3)分析表与表之间的关联关系
然后可以参考三大范式进行设计,设计主键时,主键要尽量小并且定义为自增和不可修改。
19.where和having的区别?
where是约束声明,having是过滤声明,where早于having执行,并且where不可以使用聚合函数,having可以
20.三大范式
第一范式:每个列都不可以再拆分。
第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。
第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主键。
21.char和varchar的区别
char是不可变的,最大长度为255,varchar是可变的字符串,最大长度为2^16
22.InnoDB 什么情况下会产生死锁
事务1已经获取数据A的写锁,想要去获取数据B的写锁,然后事务2获取了B的写锁,想要去获取A的写锁,相互等待形成死锁。
mysql解决死锁的机制有两个:1.等待, 直到超时 2.发起死锁检测,主动回滚一条事务
死锁检测的原理是构建一个以事务为顶点、 锁为边的有向图, 判断有向图是否存在环, 存在即有死锁。
我们平时尽量减少事务操作的资源和隔离级别
23.MySQL 删除自增 id,随后重启 MySQL 服务,再插入数据,自增 id 会从几开始?
innodb 引擎:
MySQL8.0前,下次自增会取表中最大 id + 1。原理是最大id会记录在内存中,重启之后会重新读取表中最大的id
MySQL8.0后,仍从删除数据 id 后算起。原理是它将最大id记录在redolog里了
myisam:
自增的 id 都从删除数据 id 后算起。原理是它将最大id记录到数据文件里了
24.MySQL插入百万级的数据如何优化?
(1)一次sql插入多条数据,可以减少写redolog日志和binlog日志的io次数(sql是有长度限制的,但可以调整)
(2)保证数据按照索引进行有序插入
(3)可以分表后多线程插入
五.常用开发框架系列
1.什么是Spring?
Spring是个轻量级的框架,通过IOC达到松耦合的目的,通过AOP可以分离应用业务逻辑和系统服务进行内聚性的开发,不过配置各种组件时比较繁琐,所以后面才出选了SpringBoot的框架。
2.IOC是什么?
IOC是控制反转,是一种思想,把对象的创建和调用从程序员手中交由IOC容器管理,降低对象之间的依赖关系。
创建一个bean的方式有xml方式、@Bean注解方式、@Componte方式
我们在对一个bean进行实例化后,要对他的属性进行填充,大多数我们都是使用 @Autowire直接的填充依赖注入的,他是有限按照类型进行匹配。
3.AOP是什么?
AOP是面向切面编程,可以将那些与业务不相关但是很多业务都要调用的代码抽取出来,思想就是不侵入原有代码的情况下对功能进行增强。
SpringAOP是基于动态代理实现的,动态代理是有两种,一种是jdk动态代理,一种是cglib动态代理;
jdk动态代理是原理是利用反射来实现的,需要调用反射包下的Proxy类的newProxyInstance方法来返回代理对象,这个方法中有三个参数,分别是用于加载代理类的类加载器,被代理类实现的接口的class数组和一个用于增强方法的InvocaHandler实现类。
cglib动态代理原理是利用asm开源包来实现的,是把被代理类的class文件加载进来,通过修改它的字节码生成子类来处理
jdk动态代理要求被代理类必须有实现的接口,生成的动态代理类会和代理类实现同样的接口,cglib则,生成的动态代理类会继承被代理类。Spring默认使用jdk动态代理,当被代理的类没有接口时就使用cglib动态代理
4.如何定义一个全局异常处理类?
想要定义一个全局异常处理类的话,我们需要在这个类上添加@ContaollerAdvice注解,然后定义一些用于捕捉不同异常类型的方法,在这些方法上添加@ExceptionHandler(value = 异常类型.class)和@ResponseBody注解,方法参数是HttpServletRequest和异常类型,然后将异常消息进行处理。
如果我们需要自定义异常的话,就写一个自定义异常类,该类需要继承一个异常接口,类属性包括final类型的连续id、错误码、错误信息,再根据需求写构造方法;
5.如何使用aop自定义日志?
第一步:创建一个切面类,把它添加到ioc容器中并添加@Aspect注解
第二步: 在切面类中写一个通知方法,在方法上添加通知注解并通过切入点表达式来表示要对哪些方法进行日志打印,然后方法参数为JoinPoint
第三步:通过JoinPoint这个参数可以获取当前执行的方法名、方法参数等信息,这样就可以根据需求在方法进入或结束时打印日志
6.循环依赖是什么,怎么解决的?
循环依赖就是在创建 A 实例的时候里面包含着 B 属性实例,所以这个时候就需要去创建 B 实例,而创 建 B 实例过程中也包含着 A 实例。 这样 A 实例还在创建的过程当中,所以就导致 A 和 B 实例都创建不出来。
spring通过三级缓存来解决循环依赖:
一级缓存:单例池,缓存经过了已经初始化完毕的Bean
二级缓存 :半成品池,缓存还未初始化完毕的Bean
三级缓存:缓存的是获取Bean的代理对象的表达式
我们在创建 A 的过程中,先将 A 放入三级缓存 ,这时要创建B,B要创建A就直接去三级缓存中查找,并且判断需不需要进行 AOP 处理,如果需要就在三级缓存中获取A的代理对象,不需要就取A原始对象。然后将取出的对象放入二级缓存中,这个时候其他需要依赖 A 对象的直接从二级缓存中去获取即可。当B初始化完成进入一级缓存后,A 继续执行生命周期,当A完成了属性的注入后,就可以放入一级缓存了
spring2.6之前默认会解决循环依赖。在spring2.6之后需要通过配置开启解决循环依赖
7.Bean 的作用域
(1)Singleton:一个IOC容器只有一个
(2)Prototype:每次调用getBean()都会生成一个新的对象
(3)request:每个http请求都会创建一个自己的bean
(4)session:同一个session共享一个实例
(5)application:整个serverContext只有一个bean
(6)webSocket:一个websocket只有一个bean
篇幅限制下面就只能给大家展示小册部分内容了,这边整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记的【点击此处即可】免费获取
8.Bean 生命周期
实例化 Instantiation->属性赋值 Populate->初始化 Initialization->销毁 Destruction
在这四步的基础上面,Spring 提供了一些拓展点:
*Bean 自身的方法: 包括了 Bean 本身调用的方法和通过配置文件中的 init-method 和 destroy-method 指定的方法
*Bean 级生命周期接口方法:包括了 BeanNameAware、BeanFactoryAware、InitializingBean 和 DiposableBean 这些接口的方法
*容器级生命周期接口方法:包括了 InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 这两个接口实现,一般称它们的实现类为“后处理器”。
*工厂后处理器接口方法: 包括了 AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer 等等非常有用的工厂后处理器接口的方法。工厂后处理器也是容器级的。在应用上下文装配配置文件之后立即调用。
9.Spring 事务原理?
spring事务有编程式和声明式,我们一般使用声明式,在某个方法上增加@Transactional注解,这个方法中的sql会统一成功或失败。
原理是:
当一个方法加上@Transactional注解,spring会基于这个类生成一个代理对象并将这个代理对象作为bean,当使用这个bean中的方法时,如果存在@Transactional注解,就会将事务自动提交设为false,然后执行方法,执行过程没有异常则提交,有异常则回滚、
10.spring事务失效场景
(1)事务方法所在的类没有加载到容器中
(2)事务方法不是public类型
(3)同一类中,一个没有添加事务的方法调用另外以一个添加事务的方法,事务不生效
(4)spring事务默认只回滚运行时异常,可以用rollbackfor属性设置
(5)业务自己捕获了异常,事务会认为程序正常秩序
11.spring事务的隔离级别
default:默认级别,使用数据库自定义的隔离级别
其它四种隔离级别与mysql一样
12.spring事务的传播行为
(1)支持当前事务,如果不存在,则新启一个事务
(2)支持当前事务,如果不存在,则抛出异常
(3)支持当前事务,如果不存在,则以非事务方式执行
(4)不支持当前事务,创建一个新事物
(5)不支持当前事务,如果已存在事务就抛异常
(6)不支持当前事务,始终以非事务方式执行
标签:事务,Java,索引,对象,2024,程序员,线程,内存,方法 From: https://blog.csdn.net/CTZL123456/article/details/140924351篇幅限制下面就只能给大家展示小册部分内容了,这边整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记的【点击此处即可】免费获取