面试复习
Java基础
8种基础数据类型:
- boolean(1位)、byte(8位)、short(16位)、char(16位)、int(32位)、long(64位)、float(32位)、double(64位)
构造器 Constructor 是否可被 重写?
- Constructor 不能被 override(重写) ,但能被重载
多态的概念?
- 引⽤变量发出的⽅法调⽤在编程时并不确定,⽽是在程序运⾏期间才确定
重载和重写的区别 ?
- 重载:同一个类中 多个同名的方法,根据参数不同,执行不同的逻辑
- 重写:子类对父类允许的方法的方法体重新编写
反射
获取反射的三种方法
- 实例对象.getClass()
- 类名.class
- Class.forName("类路径") // 包名.类名
通过反射来创建对象
- Class对象的newInstance()方法来创建Class对象对应类的实例
Class<?> c = String.class;
Object str = c.newInstance();
- 先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建对象,这种方法可以用指定的构造器构造类的实例。
//获取String的Class对象
Class<?> str = String.class;
//通过Class对象获取指定的Constructor构造器对象
Constructor constructor=c.getConstructor(String.class);
//根据构造器创建实例:
Object obj = constructor.newInstance(“hello reflection”);
集合
List,Set,Map
List:有序,可重复
Set:无序,不可重复
Map:键值对
Arraylist 与 LinkedList 区别?
- 都不保证线程安全
- 底层数据结构:
- Arraylist :动态数组
- LinkedList :双向链表
- 特点:
- Arraylist 支持快速随机访问(实现了 RandomAccess 接口)
- LinkedList 适用动态插入和删除
Arraylist 和 Vector 区别
- Arraylist 线程不安全
- Vector 线程安全
ArrayList 扩容机制
新容量扩容为原来的1.5倍
// 确保内部容量
private void ensureCapacityInternal(int minCapacity) {
// 默认构造下第一次时,elementData默认为空
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// minCapacity 为 DEFAULT_CAPACITY(10)
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
// 确保容量可用
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 如果需要的最小容量 大于 数组长度 就进行扩容
if (minCapacity - elementData.length > 0)
// 扩容函数
grow(minCapacity);
}
// 扩容
private void grow(int minCapacity) {
// 获取原来的容量大小
int oldCapacity = elementData.length;
// oldCapacity >> 1 :原容量的0.5倍
// 例:1000=8 --> 100=4
// 例:1111=15 ---> 111=7
// 新容量 = 原容量 + 原容量 * 0.5
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
HashMap
HashMap 基础
- 默认容量:16
- 加载因子:0.75
- 扩容阈值 = 默认容量 * 加载因子
HashMap 内部的数据结构?和底层实现?
数据结构:
- 1.7 数组 + 链表
- 1.8以后 数组 + 链表 / 红黑树(链表⻓度⼤于阈值 )
- 为什么是红黑树:红⿊树就是为了解决⼆叉查找树的缺陷,因为⼆叉查找树在某些情况下会退化成⼀个线性结构(链表)。
- 什么时候开始转换:
数组长度 >= 64 && 链表长度 >8
底层实现:hashcode --> 高低16位异或【 hash()方法 】 --> (n - 1) & hash
HashMap 通过 key 的hashCode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置(这⾥的 n 指的是数组的⻓度) 。
如果此时table的对应位置没有任何元素,也就是table[i]=null,那么就直接把Node放入table[i]的位置,并且这个Node的next=null。
如果此时table对应位置是一个Node,说明对应的位置已经保存了一个Node链表,则需要遍历链表,如果发现相同hash值则替换Node节点,如果没有相同hash值,则把新的Node插入链表的末端,作为之前末端Node的next,同时新Node的next=null。
如果添加元素之后,HashMap总节点数超过了阈值,则HashMap会进行扩容。
HashMap的put()
1.对key的hashCode()做hash运算,计算index;
2.如果没碰撞直接放到bucket⾥;
3.如果碰撞了,以链表的形式存在buckets后;
4.如果碰撞导致链表过⻓(⼤于等于TREEIFY_THRESHOLD),就把链表转换成红⿊树(JDK1.8中的改动);
5.如果节点已经存在就替换old value(保证key的唯⼀性)
6.如果bucket满了(超过load factor*current capacity),就要resize
HashMap 扩容
- 扩容大小为 原容量的2倍
HashMap 并发?
- HashMap() 非线程安全
- 多线程扩容,引起的死循环问题
- 多线程put的时候可能导致元素丢失
concurrentHashMap 底层原理
解决并发时的HashMap
1.7 对整个桶数组进⾏了分割分段(Segment),每⼀把锁只锁容器其中⼀部分数据,多线程访问容器⾥不同数据段的数据,就不会存在锁竞争,提⾼并发访问率。
1.8 synchronized只锁定当前链表或红⿊⼆叉树的⾸节点
LinkedHashMap的应用
- 有序的访问HashMap
红黑树实现原理和应用场景?
JUC
线程和进程
线程的生命周期?
线程的生命周期包含5个阶段,包括:新建、就绪、运行、阻塞、销毁。
- 1、新建状态(New):当一个 Thread类或其子类的对象被声明并创建时,就新创建了一个线程对象
- 2、就绪状态( Runnable):线程对象创建后,其他线程调用了该对象的 start ( ) 方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权(CPU时间片)
- 3、运行状态( Running):就绪状态的线程获取了CPU使用权时,进入运行状态,执行run()方法的程序代码
- 4、阻塞状态( Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。
- 阻塞的情况分三种:
- (一)、等待阻塞:运行的线程执行 wait ( ) 方法,JVM 会把该线程放入等待池中等待。( wait 会释放持有的锁)
- (二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中
- (三)、其他阻塞:运行的线程执行 sleep ( ) 方法 或 join ( ) 方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当 sleep ( ) 状态超时、 join ( ) 等待线程终止或者超时、或者 I/O 处理完毕时,线程重新转入就绪状态。(注意 sleep ( ) 是不会释放持有的锁)
- 阻塞的情况分三种:
- 5、死亡状态(Dead):线程执行完了所有工作 或者 因异常退出了run ( )方法,该线程结束生命周期
线程的创建?
- 继承thread类
- 实现Runnable接口
- 实现callable接口
- 通过线程池
什么是线程安全?如何实现?
- 线程安全:
- 原子性:一组线程要么同时执行成功,要么同时执行失败
- 有序性:执⾏的过程中的先后顺序
- 可见性:一个线程对共享变量值得修改,能够及时的被其他线程看到
- 实现线程安全
- 锁
- synchronized
锁
乐观锁 和 悲观锁
-
悲观锁和独占锁是一个意思,它假设一定会发生冲突,因此获取到锁之后会阻塞其他等待线程,性能开销大
- 悲观锁的代表是 synchronized
-
乐观锁不一样,它假设不会产生冲突,先去尝试执行某项操作,失败了再进行其他处理(一般都是不断循环重试),性能开销小
- 乐观锁的代表是 CAS
公平锁 和 非公平锁
- 公平锁:按排队顺序去获得锁
- 非公平锁:⽆视队列顺序直接去抢锁
可重入锁 和 不可重入锁
- 可重入锁:如果一个线程已经获取到了一个锁,那么它可以访问被这个锁锁住的所有代码块。
- 不可重入锁:如果一个线程已经获取到了一个锁,那么它不可以访问被这个锁锁住的所有代码块。
自旋锁与自适应自旋
synchronized
synchronized 了解
synchronized关键字可以保证被它修饰的⽅法或者代码块在任意时刻只能有⼀个线程执⾏
synchronized 三种使用方式
-
修饰实例方法:相当于给对象加锁,this
-
修饰静态方法:相当于给类加锁
-
修饰代码块:对括号里的对象加锁
synchronized 底层原理
-
synchronized 同步语句块的情况 (对象锁)
- synchronized 同步语句块的实现使⽤的是 monitorenter 和 monitorexit指令,其中 monitorenter指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。
-
synchronized 同步⽅法的的情况
- synchronized 同步⽅法是ACC_SYNCHRONIZED 标识,该标识指明了该⽅法是⼀个同步⽅法, JVM 通过该 ACC_SYNCHRONIZED 访问
标志来辨别⼀个⽅法是否声明为同步⽅法,从⽽执⾏相应的同步调⽤。
- synchronized 同步⽅法是ACC_SYNCHRONIZED 标识,该标识指明了该⽅法是⼀个同步⽅法, JVM 通过该 ACC_SYNCHRONIZED 访问
Java 对象在内存中的分布
-
一个对象在内存中包含三部分:对象头,实例数据 和 对齐填充
-
对象头:
- Class Metadata Address (类型指针)。存储类的元数据的指针。虚拟机通过这个指针找到它是哪个类的实例。
- Mark Word(标记字段)。存出一些对象自身运行时的数据。包括哈希码,GC 分代年龄,锁状态标志等。
Monitor
Mark Word 有一个字段指向 monitor 对象。monitor 中记录了锁的持有线程,等待的线程队列等信息。前面说的每个对象都有一个锁和一个等待队列,就是在这里实现的。 monitor 对象由 C++ 实现。其中有三个关键字段:
- _owner 记录当前持有锁的线程
- _EntryList 是一个队列,记录所有阻塞等待锁的线程
- _WaitSet 也是一个队列,记录调用 wait() 方法并还未被通知的线程。
Monitor的操作机制如下:
- 多个线程竞争锁时,会先进入 EntryList 队列。竞争成功的线程被标记为 Owner。其他线程继续在此队列中阻塞等待。
- 如果 Owner 线程调用 wait() 方法,则其释放对象锁并进入 WaitSet 中等待被唤醒。Owner 被置空,EntryList 中的线程再次竞争锁。
- 如果 Owner 线程执行完了,便会释放锁,Owner 被置空,EntryList 中的线程再次竞争锁。
CAS (Compare And Swap)
-
硬件级别的原子操作
-
是乐观锁的一种实现方式,是一种轻量级锁,JUC 中很多工具类的实现就是基于 CAS 的。
-
CAS操作包含三个操作数——内存位置(V)、预期原值(A)和新值(B)
-
一般执行流程:
- 先进性读取数据,得到原值和内存位置
- 准备写回数据时,再去获取原值,判断原值是否被修改
- 若未被其他线程修改则写回新值,若已被修改,则重新执行读取流程
ABA问题:
- 线程1读取了数据A
- 线程2读取了数据A
- 线程2通过CAS比较,发现值是A没错,可以把数据A改成数据B
- 线程3读取了数据B
- 线程3通过CAS比较,发现数据是B没错,可以把数据B改成了数据A
- 线程1通过CAS比较,发现数据还是A没变,就写成了自己要改的值
怎么防止ABA?
- 加标志位,时间戳等
AQS(Abstract Queued Synchronizer)
- AQS核⼼思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的⼯作线程,并且将共享资源设置为锁定状态即 state = 1 。如果被请求的共享资源被占⽤,将暂时获取不到锁的线程加⼊到队列中等待。
- AQS 使⽤CAS对该同步状态进⾏原⼦操作实现对其值的修改。
ReentrantLock 加锁和释放锁的底层原理
-
非公平锁,可重入锁
-
lock() 方法加锁,这时 AQS对象内部核心变量state = 1,并记录加锁线程;
-
unlock()释放锁,这时 AQS对象内部核心变量state = 0,并加锁线程为null;
死锁必须具备以下四个条件
-
互斥条件:该资源任意⼀个时刻只由⼀个线程占⽤。
-
请求与保持条件:⼀个进程因请求资源⽽阻塞时,对已获得的资源保持不放。
-
不剥夺条件:线程已获得的资源在末使⽤完之前不能被其他线程强⾏剥夺,只有⾃⼰使⽤完毕后才释放资源。
-
循环等待条件:若⼲进程之间形成⼀种头尾相接的循环等待资源关系
ThreadLocal
- 可以使⽤ get() 和 set() ⽅法来获取默认值或将其值更改为当前线程所存的副本的值,从⽽避免了线程安全问题。
JAVA内存模型
Java内存模型->原子性、可见性、有序性- volatile-> happens-before/内存屏障
- 原子性:一组线程要么同时执行成功,要么同时执行失败
- 有序性:执⾏的过程中的先后顺序
- 可见性:一个线程对共享变量值得修改,能够及时的被其他线程看到
volatile关键字
volatile有啥用?
- volatile 关键字的主要作⽤就是保证变量的可⻅性然后还有⼀个作⽤是防⽌指令重排序。
synchronized 关键字和 volatile 关键字的区别 ?
- volatile关键字是线程同步的轻量级实现,所以volatile性能肯定⽐synchronized关键字要好
- volatile关键字能保证数据的可⻅性,但不能保证数据的原⼦性。 synchronized关键字两者都能保证
- volatile关键字主要⽤于解决变量在多个线程之间的可⻅性,⽽ synchronized关键字解决的是多个线程之间访问资源的同步性
线程池
为什么需要线程池,线程池的创建方式?
-
线程池的好处:
- 降低资源消耗
- 提⾼响应速度
- 提⾼线程的可管理性
-
创建方式:
-
ThreadPoolExecutor 的方式
-
Executors 的方式
-
线程池的几个重要参数?
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
- corePoolSize:核⼼线程数线程数定义了最⼩可以同时运⾏的线程数量
- maximumPoolSize:当队列中存放的任务达到队列容量的时候,当前可以同时运⾏的线程数量变为最⼤线程数。
- keepAliveTime:当线程池中的线程数量⼤于 corePoolSize 的时候,如果这时没有新的任务提交,核⼼线程外的线程不会⽴即销毁,⽽是会等待,直到等待的时间超过了 keepAliveTime 才会被回收销毁;
- unit :keepAliveTime 参数的时间单位
- workQueue:当新任务来的时候会先判断当前运⾏的线程数量是否达到核⼼线程数,如果达到的话,新任务就会被存放在队列中
- handler:饱和策略
手写单例模式
JVM
JVM内存模型
- 线程公有:
- 方法区(metaspace),堆(heap)
- 方法区:
+ - 堆:
- 存放对象实例
- 方法区:
- 方法区(metaspace),堆(heap)
- 线程私有:
- 程序计数器,本地方法栈,虚拟机栈
- 程序计数器
- 字节码解释器通过改变程序计数器来依次读取指令,从⽽实现代码的流程控制
- 程序计数器⽤于记录当前线程执⾏的位置
- 本地方法栈
- 虚拟机栈
- 程序计数器
- 程序计数器,本地方法栈,虚拟机栈
GC机制和原理
GC分哪两种,Minor GC 和 Full GC 有什么区别?什么时候出发Full GC ?分别采用什么算法?
JVM 的 Classloader
双亲委派机制?
MySQL
什么是MySQL?
- MySQL 是一种关系型数据库,并且它是开源免费的,方便扩展。
MyISAM 和 InnoDB的区别
- 是否⽀持⾏级锁 :
- MyISAM 只有表级锁(table-level locking)
- InnoDB ⽀持⾏级锁(rowlevel locking)和表级锁,默认为⾏级锁。
- 是否⽀持事务和崩溃后的安全恢复:
- MyISAM 强调的是性能,但不提供事务⽀持
- InnoDB 提供事务⽀持事务, 具有事务(commit)、回滚(rollback)和崩溃修复能⼒(crash recovery capabilities)的事务安全(transaction-safe (ACID compliant))型表
- 是否⽀持外键:
- MyISAM不⽀持
- InnoDB⽀持
- 是否⽀持MVCC :
- 仅 InnoDB ⽀持
索引
MySQL索引使⽤的数据结构主要有BTree索引 和 哈希索引
查询缓存的使⽤ (MySQL 8.0 版本后移除 )
- 开启查询缓存后在同样的查询条件以及数据情况下,会直接在缓存中返回结果
什么是事务?
- 事务是逻辑上的⼀组操作,要么都执⾏,要么都不执⾏。
事物的四⼤特性(ACID)
- 原⼦性(Atomicity): 事务是最⼩的执⾏单位,不允许分割。事务的原⼦性确保动作要么全部完成,要么完全不起作⽤;
- ⼀致性(Consistency): 执⾏事务前后,数据保持⼀致,多个事务对同⼀个数据读取的结果是相同的;
- 隔离性(Isolation): 并发访问数据库时,⼀个⽤户的事务不被其他事务所⼲扰,各并发事务之间数据库是独⽴的;
- 持久性(Durability): ⼀个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发⽣故障也不应该对其有任何影响。
并发事务带来哪些问题?
多个⽤户对同⼀数据进⾏操作时,可能会导致以下的问题:
- 脏读(Dirty read) :
- 当⼀个事务正在访问数据并且对数据进⾏了修改,⽽这种修改还没有提交到数据库中,这时另外⼀个事务也访问了这个数据,然后使⽤了这个数据。因为这个数据是还没有提交的数据,那么另外⼀个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
- 丢失修改(Lost to modify) :
- 指在⼀个事务读取⼀个数据时,另外⼀个事务也访问了该数据,那么在第⼀个事务中修改了这个数据后,第⼆个事务也修改了这个数据。这样第⼀个事务内的修改结果就被丢失,因此称为丢失修改。 如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。
- 不可重复读(Unrepeatableread) :
- 指在⼀个事务内多次读同⼀数据。在这个事务还没有结束时,另⼀个事务也访问该数据。那么,在第⼀个事务中的两次读数据之间,由于第⼆个事务的修改导致第⼀个事务两次读取的数据可能不太⼀样。这就发⽣了在⼀个事务内两次读到的数据是不⼀样的情况,因此称为不可重复读。
- 幻读(Phantom read) :
- 幻读与不可重复读类似。它发⽣在⼀个事务(T1)读取了⼏⾏数据,接着另⼀个并发事务(T2)插⼊了⼀些数据时。在随后的查询中,第⼀个事务(T1)就会发现多了⼀些原本不存在的记录,就好像发⽣了幻觉⼀样,所以称为幻读。
不可重复读 和 幻读 的区别:
- 不可重复读 重点是修改,幻读 重点是新增 和 删除
事务隔离级别有哪些?MySQL的默认隔离级别是?
SQL 标准定义了四个隔离级别:
- READ-UNCOMMITTED(读取未提交):
- 最低的隔离级别,允许读取尚未提交的数据变更, 可能会导致脏读、幻读或不可重复读。
- READ-COMMITTED(读取已提交):
- 允许读取并发事务已经提交的数据, 可以阻⽌脏读,但是幻读或不可重复读仍有可能发⽣。
- REPEATABLE-READ(可重复读):
- 对同⼀字段的多次读取结果都是⼀致的,除⾮数据是被本身事务⾃⼰所修改, 可以阻⽌脏读和不可重复读,但幻读仍有可能发⽣。
- SERIALIZABLE(可串⾏化):
- 最⾼的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执⾏,这样事务之间就完全不可能产⽣⼲扰,也就是说, 该级别可以防⽌脏读、不可重复读以及幻读。
隔离级别 | 脏读 | 不可重复读 | 幻影读 |
---|---|---|---|
READ-UNCOMMITTED | √ | √ | √ |
READ-COMMITTED | × | √ | √ |
REPEATABLE-READ | × | × | √ |
SERIALIZABLE | × | × | × |
Spring
spring 框架了解
- 一种轻量级开发框架,主要是为了开发人员进行更高效的开发和提供系统的可维护性
创建Bean 的三种方式
-
使用 xml :
-
-
使用注解 + 类路径扫描:
- @Component : 侧重于通用的Bean类
- @Service:标识该类用于业务逻辑
- @Controler:标识该类为Spring MVC的控制器类
- @Repository: 标识该类是一个实体类,只有属性和Setter,Getter
-
基于java类配置:
-
@Configuration :
- @Configuration就相当于Spring配置文件中的
标签,里面可以配置bean
- @Configuration就相当于Spring配置文件中的
-
@Bean :
- @Bean相当于Spring配置文件中的
标签可以在Spring容器中注入一个bean - 返回值:相当于bean标签中的 class 属性
- 方法名:相当于bean标签中的 id 属性
- @Bean相当于Spring配置文件中的
-
对 Spring IOC 的理解
- IoC(Inverse of Control:控制反转)是⼀种设计思想,就是 将对象的创建、初始化、销毁控制权,交由Spring框架来管理 。
对SpringAOP 的理解
- 将共同调⽤的逻辑或责任(例如事务处理、⽇志管理、权限控制等)封装起来 ,降低耦合度
- Spring AOP就是基于动态代理的 【 动态代理 】
- JDK 动态代理【接口】
- 动态代理类 实现 InvocationHandler
- 私有 目标类
- 私有 切面类
- 通过构造器参数引入目标类和切面类
- 重写 invoke () 方法
- 通过 method.invoke()
- cglib 动态代理【类】
- JDK 动态代理【接口】
Spring 中的 bean 的作⽤域有哪些?
- singleton : 唯⼀ bean 实例, Spring 中的 bean 默认都是单例的。
- prototype : 每次请求都会创建⼀个新的 bean 实例。
- request : 每⼀次HTTP请求都会产⽣⼀个新的bean,该bean仅在当前HTTP request内有效。
- session : 每⼀次HTTP请求都会产⽣⼀个新的 bean,该bean仅在当前 HTTP session 内有效
Spring Bean 的生命周期
Bean实例化的三种方式
1,使用类构造器实例化(无参构造函数)
:直接通过Spring工厂返回类的实例对象
2,使用静态工厂方法实例化(简单工厂模式)
:Spring工厂调用自定义工厂的静态方法返回类的实例对象。
3,使用实例工厂方法实例化(工厂方法模式)
:Spring工厂调用工厂的普通方法(非静态方法)返回类的实例对象。
Spring 中的单例 bean 的线程安全问题了解吗?
- 对这个对象的⾮静态成员变量的写操作会存在线程安全问题。
- 解决办法:
- 在类中定义⼀个ThreadLocal成员变量,将需要的可变成员变量保存在 ThreadLocal 中。
Spring事务
spring 事务的特性(ACID)
- 原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用;
- 一致性(Consistency):一旦事务完成(不管是成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏;
- 隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏;
- 持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中;
Spring 事务中的隔离级别有哪⼏种?
TransactionDefinition 接⼝中定义了五个表示隔离级别的常量:
- TransactionDefinition.ISOLATION_DEFAULT: 使⽤后端数据库默认的隔离级别, Mysql 默认采⽤的 REPEATABLE_READ隔离级别 Oracle 默认采⽤的 READ_COMMITTED隔离级别.
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔离级别,允许读取尚未提交的数据变更, 可能会导致脏读、幻读或不可重复读
- TransactionDefinition.ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据, 可以阻⽌脏读,但是幻读或不可重复读仍有可能发⽣
- TransactionDefinition.ISOLATION_REPEATABLE_READ: 对同⼀字段的多次读取结果都是⼀致的,除⾮数据是被本身事务⾃⼰所修改, 可以阻⽌脏读和不可重复读,但幻读仍有可能发⽣。
- TransactionDefinition.ISOLATION_SERIALIZABLE: 最⾼的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执⾏,这样事务之间就完全不可能产⽣⼲扰,也就是说, 该级别可以防⽌脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会⽤到该级别。
Spring 事务中哪⼏种事务传播⾏为?
⽀持当前事务的情况:
- TransactionDefinition.PROPAGATION_REQUIRED: 如果当前存在事务,则加⼊该事务;如果当前没有事务,则创建⼀个新的事务。
- TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加⼊该事务;如果当前没有事务,则以⾮事务的⽅式继续运⾏。
- TransactionDefinition.PROPAGATION_MANDATORY: 如果当前存在事务,则加⼊该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)
不⽀持当前事务的情况:
- TransactionDefinition.PROPAGATION_REQUIRES_NEW: 创建⼀个新的事务,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以⾮事务⽅式运⾏,如果当前存在事
务,则把当前事务挂起。 - TransactionDefinition.PROPAGATION_NEVER: 以⾮事务⽅式运⾏,如果当前存在事务,则抛出
异常。
其他情况:
- TransactionDefinition.PROPAGATION_NESTED: 如果当前存在事务,则创建⼀个事务作为当前
事务的嵌套事务来运⾏;如果当前没有事务,则该取值等价于
TransactionDefinition.PROPAGATION_REQUIRED。
自定义注解 annation
SpringMVC
Spring MVC 下我们⼀般把后端项⽬分为 Service层(处理业务)、 Dao层(数据库操作)、 Entity层(实体类)、 Controller层(控制层,返回数据给前台⻚⾯)。
SpringMVC 工作原理 ?
- 用户发送请求至前端控制器DispatcherServlet。
- DispatcherServlet请求调用HandlerMapping处理器映射器获取Handler
- 处理器映射器找到Handler返回给DispatcherServlet
- DispatcherServlet调用HandlerAdapter处理器适配器执行Handler
- HandlerAdapter适配调用Handler
- 处理器处理后返回ModelAndView给HandlerAdapter处理器适配器
- HandlerAdapter将ModelAndView返回给DispatcherServlet
- DispatcherServlet将ModelAndView传给ViewReslover视图解析器
- ViewReslover解析后返回具体View
- DispatcherServlet根据View进行渲染视图
- DispatcherServlet响应用户
MyBatis
#{}和${}的区别是什么?
- 在 SQL语句中 ,在预编译时
-
{} 是 ?【安全,能够防止sql注入】
- ${} 直接就是 字符串【不安全,不能防止sql注入】
-
Dao 接⼝⾥的⽅法,参数不同时,⽅法能重载吗?
- 不能重载的,因为是 全限名+⽅法名 的保存和寻找策略。
Dao 接⼝的⼯作原理是什么?
- Dao 接⼝的⼯作原理是 JDK 动态代理, Mybatis 运⾏时会使⽤ JDK 动态代理为 Dao 接⼝⽣成代理 proxy 对象,代理对象 proxy 会拦截接⼝⽅法,转⽽执⾏ MappedStatement 所代表的 sql,然后将 sql 执⾏结果返回。
动态SQL?
- 9 种动态 sql 标签 trim | where | set | foreach | if | choose | when | otherwise | bind
Mybatis 都有哪些 Executor 执⾏器?
- SimpleExecutor 、 ReuseExecutor 、 BatchExecutor
SimpleExecutor : 每执⾏⼀次 update 或 select,就开启⼀个 Statement 对象,⽤完⽴刻关闭
Statement 对象。
`ReuseExecutor : 执⾏ update 或 select,以 sql 作为 key 查找 Statement 对象,存在就使⽤,不存在就创建,⽤完后,不关闭 Statement 对象,⽽是放置于 Map<String, Statement>内,供下⼀次使⽤。简⾔之,就是重复使⽤ Statement 对象。
BatchExecutor : 执⾏ update(没有 select, JDBC 批处理不⽀持 select),将所有 sql 都添加到批处理中(addBatch()),等待统⼀执⾏(executeBatch()),它缓存了多个 Statement 对象,每个Statement 对象都是 addBatch()完毕后,等待逐⼀执⾏ executeBatch()批处理。与 JDBC 批处理相
同。
为什么说 Mybatis 是半⾃动 ORM 映射⼯具?
- Mybatis 在查询关联对象或关联集合对象时,需要⼿动编写 sql 来完成,所以,称之为半⾃动 ORM 映射⼯具。
一对多(collection:集合)
什么是集合:
将嵌套映射的结果集合到一个list中,比如一个部门有多个员工,即1个部门对应多个员工。
<resultMap id="唯一的标识" type="映射的pojo对象">
<id column="表的主键字段或查询语句中的别名字段" jdbcType="字段类型" property="映射pojo对象的主键属性" />
<result column="表的一个字段(可以为任意表的一个字段)" jdbcType="字段类型" property="映射到pojo对象的一个属性"/>
<collection property="pojo的集合属性" ofType="集合中的pojo对象">
<id column="集合中pojo对象对应的表的主键字段" jdbcType="字段类型" property="集合中pojo对象的主键属性" />
<result column="可以为任意表的字段" jdbcType="字段类型" property="集合中的pojo对象的属性" />
</collection>
</resultMap>
多对一和一对一(association:关联)
什么是关联:
在mybatis中是用来处理"has a"关系,比如一个员工有一个部门,即一个员工关联一个部门,所以association能用来处理我们数据中所谓的一对一,多对一关系(一个部门有多个员工,但是对于员工来说,一个员工只能关联一个部门)。
<resultMap id="唯一的标识" type="映射的pojo对象">
<id column="表的主键字段或查询语句中的别名字段" jdbcType="字段类型" property="映射pojo对象的主键属性" />
<result column="表的一个字段(可以为任意表的一个字段)" jdbcType="字段类型" property="映射到pojo对象的一个属性"/>
<association property="pojo的集合属性" javaType="集合中的pojo对象">
<id column="集合中pojo对象对应的表的主键字段" jdbcType="字段类型" property="集合中pojo对象的主键属性" />
<result column="可以为任意表的字段" jdbcType="字段类型" property="集合中的pojo对象的属性" />
</collection>
</resultMap>
多对一和一对多区别:
- 多对一:关键字用 association,对象类型用 javaType
- **一对多:关键字用 collection,对象类型用 ofType **
计算机基础
计算机网络
OSI七层模型和TCP/IP四层模型
三次握手(建立连接)和四次挥手(断开连接)
三次握手简单示意图:
客户端 ——> 发送带有 SYN标志的数据包:一次握手
服务端 ——> 接受带有SYN标志的数据包,并发送带有 SYN/AKCK 的数据包:二次握手
客户端 ——> 发送 带有ACK 的数据包:三次握手
为什么要三次握手?
- 建立可靠的通信信道,确保双方的发送与接收正常
四次挥手简单示意图
客户端 ——> 发送一个FIN,关闭客户端到服务器的数据传送
服务端 ——> 接受 FIN ,并发回ACK,确认序号为收到序号 + 1
服务端 ——> 关闭与客户端的连接【半关闭】,发送 FIN 给客户端
客户端 ——> 发送 ACK ,并将确认序号设置为 收到序号 + 1
TCP 和 UDP 的区别?
从输入URL到页面加载发生了什么?
- 域名找到IP,如果缓存里没有就要请求DNS服务器;得到IP后开始与目的主机进行三次握手来建立TCP连接;连接建立后进行HTTP访问,传输并获取网页内容;传输完后与目的主机四次挥手来断开TCP连接。