一.JavaSE 部分
基础部分
Java基本数据有哪些类型?
byte:8位,最大存储数据量为255,存储数据范围为-128~127之间。
short:16位,
int:32位,最大数据存储容量为2的32次减1,数据范围为负2的31次减1。
long:64位,最大数据存储容量为2的64次减1,数据范围为负的63次减1。
float:32位,
double:64位,
boolean:只有true和false两个取值。
char:16位,存储Unicode代码,用单引号赋值。
Integer 和 int的区别
是基本数据类型,变量中直接存放数值,变量初始
是引用数据类型,对象的引用存储在变量中,变量
Integer是int包装类型,将int封装成Integer,符合java面向对象的特征可以使用各种方法,如与其他数据类型的转换
Integer和int深度对比:
-
两个通过new生成的Integer由于堆中地址不同,对象永远不会相等
-
int和Integer相比之下,只要数值相等,结果就会相等,因为当比较包装类和基本数据类型时,箱子会自动拆卸,Integer转化为int
-
通过new生成的Integer对象和非通过new生成的Integer与对象相比,由于前者存放在堆中,后者存放在堆中Java所以常量池永远不会相等
-
两个非通过new生成的Integer对象比较时,如果两个变量的值相等,且在-128-127之间,则结果相等。这是因为给Integer对象赋一个int值,java静态方法将在编译过程中自动调用valueOf(),根据java api中对Integer类型的valueOf对于-128到127之间的整数,将进行缓存,如果下次赋予相同的值将直接从缓存中取出,即享元模式
String和StringBuilder和StringBuffer区别
底层都是char[]
存储数据,JDK1.9后使用byte[] ,因为我们经常使用短字符串来存储它们byte这样可以节省更多的空间。
由于String底层的char[]有final
所以每次都是对的String所有的操作都会在内存中开辟空间,生成新的对象,所以String不可变
StringBuilder和StringBuffer是可变字符串,没有final此外,修改适用于字符串拼接StringBuffer线程安全,方法有synchronized
但性能较低,StringBuilder线程不安全,方法不安全synchronized性能,性能高
String a = “A” 和 String a = new String(“A”) 创建字符串的区别
String c = “A” 首先常量池找 “A,如果有,a将指向对象的地址 ,如果没有,在栈中创建三个char型的值’A在堆里创建一个String对象object,值为"A",接着object它将在字符串常量池中,最后将a指向对象的地址
new String(“A”) : 如果常量池中有A将首先创建上述相同的流程A然后在堆里创造一个String对象,它现有的值共享栈char值“A”。
以下代码创建了几个对象
- String s = “a” “b” “c” “d;这句话创造了多少对象?
与字符串常量相加的表达式相比,编译器在编译过程中会进行优化,直接将其编译成常量相加的结果。
-
String s; 创建多少对象? 对象没有创建。
-
String a = “abc”; String b = “abc”; 创建了几个对象
只在第一句话中创建了一个对象,a和b都指向相同的对象"abc",引用不是对象
== 和 equals 有什么区别?
==
比较对象比较地址,对于Object对象中的equals
该方法也被使用 == ,比较的是对象的地址,默认情况下使用对象的equals比较Object中的equals方法,即比较地址,如果要实现自己的比较方法,需要重写equals 方法。
包装类比如:Integer都是复写过equals比较方法int 值。
final 和 finally 和 finalize 的区别
当用final在修改类别时,表明该类别不能被其他类别继承。当我们需要让一个类别永远不会被继承时,我们可以使用它final修饰
finally它只能作为异常处理的一部分使用try/catch在句子中,并附有一个句子块,这意味着这个句子最终将被执行(无论是否抛出异常),通常用于需要释放资源
finalize()是在java.lang.Object也就是说,每个对象都有这样有这样的方法。这个方法在gc当对象被回收时,启动并调用。其实gc可以回收大部分对象(凡是new出来的对象,gc一般来说,我们不会使用它new以外部方式创建对象),因此程序员通常不需要实现它finalize的。
JDK 和 JRE 有什么区别?
JRE(Java Runtime Enviroment) :是Java,JRE是运行Java程序所需环境的集合包括JVM标准实现及 Java核心类库
JDK(Java Development Kit) :是Java提供开发工具包Java开发环境(提供编译器javac等工具,用于将java文件编译为class运行环境(提及文件) 供了JVM和Runtime辅助包,用于分析class运行文件)。JDK是整个Java包括核心Java运行环境(JRE),一堆Java工具tools.jar和Java标准类库 (rt.jar)。
面向对象的四个特征
抽象 : 总结一类对象的共同特征,包括数据抽象和行为抽象,抽象只关注对象的属性和行为,而不关注这种行为的细节 - 例子:定义一个persion类,是对的人
抽象
包装:数据访问只能通过定义的接口进行,包装是隐藏所有隐藏的东西,只能为外部世界提供最简单的编程接口,例如Java隐藏不需要暴露的内容和实现细节,或private修改,然后提供特殊的访问方法,如JavaBean。 - 例如:计算机主机将主板包装到外壳中,并提供USB接口、网卡接口、电源接口等。 JavaBean是一种包装。
继承:新类(子类、衍生类)继承了原始类的特征,子类可以从父类继承方法和实例变量,可以修改或增加新方法,使其更适合特殊需要。
多态性:多态性是指允许不同类型的对象响应相同的信息。当编译类型和操作类型不同时,对象的多种形式是多态性的,意思是屏蔽子类的差异
方法覆盖和重载
方法的覆盖是子类与父类的关系,方法的重载是同一类中方法的关系。 覆盖只能通过一种方法或一对方法产生关系;方法的重载是多种方法之间的关系。 重载要求参数列表相同;
普通类和抽象类
抽象类不能实例化, 需要通过子类实例化 抽象类可以有结构函数,子类必须继承父类的结构方法,抽象方法不能声明为静态。 抽象方法只需要说明,不需要实现,普通方法可以在抽象类中有主体 含有抽象方法的类必须申报为抽象类 抽象子类必须实现抽象子类中的所有抽象方法,否则子类也是抽象子类
接口和抽象类
使用定义界面interface,定义抽象使用abstract class
接口采用全局常量,抽象方法,(java8后:静态方法,默认方法)
抽象分为结构方法、抽象方法和普通方法
接口和类是实现关系的,抽象类和类是继承关系
IO流
你知道BIO,NIO,AIO么?讲一下你的理解
BIO (Blocking I/O):同步阻塞I/O 该模式以流的方式处理数据,数据的读取和写入必须阻塞在一个线程中,等待其完成。适用于连接数量小、固定的架构
NIO (New I/O):支持阻塞非阻塞模式,以块的方式处理数据,适用于连接数目多且连接比较短(轻操作)的架构,比如聊天器
AIO ( Asynchronous I/O):异步非阻塞I/O 模型,适用于连接数目多且连接比较长(重操作)的架构
java 中四大基础流
InputStream : 输入字节流, 也就是说它既属于输入流, 也属于字节流 ,
OutputStream: 输出字节流, 既属于输出流, 也属于字节流
Reader: 输入字符流, 既属于输入流, 又属于字符流
Writer: 输出字符流, 既属于输出流, 又属于字符流
读文本用什么流,读图片用什么流
文本用字符输入流,读图片用字节输入流
字符流和字节流有什么区别
字符流适用于读文本,字节流适用于读图片,视频,文件等。
字节流操作的基本单元为字节;字符流操作的基本单元为Unicode码元。
字节流默认不使用缓冲区;字符流使用缓冲区。
字节流通常用于处理二进制数据,实际上它可以处理任意类型的数据,但它不支持直接写入或读取Unicode码元;字符流通常处理文本数据,它支持写入及读取Unicode码元
BufferedInputStream 用到什么设计模式
主要运用了俩个设计模式,适配器和装饰者模式
带缓冲区的流
BufferedInputStream 带缓冲区的字节输入
BufferedOutputStream 带缓冲区的输出流
BufferedReader : 带缓冲区的字符输入流
BufferedWriter : 带缓冲区的字符输出流
集合篇
说一下Java中的集合体系
Collection接口
List:
-
ArrayList:底层数据结构是数组,查询性能高,增删性能低
-
Vector:底层数据结构是数组,查询性能高,增删性能低
-
LinkedList:底层数据结构是双向链表,查询性能低,增删性能高
Set:
-
HashSet:无序不重复的,使用HashMap的key存储元素,判断重复依据是hashCode()和equals()
-
TreeSet:有序不重复的,底层使用TreeMap的key存储元素,排序方式分为自然排序,比较器排序
Map接口
- HashMap:key的值没有顺序,线程不安
- TreeMap:key的值可以自然排序,线程不安全
- HashTable:它的key和value都不允许为null,线程安全
- Properties:它的key和value都是String类型的,线程安全
HashMap和HashTable的区别
HashMap和HashTable都是实现了Map接口的集合框架,他们的区别
-
HashTable是线程安全的,它的实现方法都加了synchronized关键字,因此它的性能较低
-
HashMap是线程不安全的,它实现方法没有加synchronized,因此它的性能较高
-
HashMap的key和value都允许为null,HashTable中的key和value都不能为null,如果不考虑线程安全,建议使用HashMap,如果需要考虑线程安全的高并发实现,建议使用ConcurrentHashMap
ArrayList和LinkedList区别
都属于线性结构,ArrayList是基于数组实现的,开辟的内存空间要求联系,可以根据索引随机访问元素性能高,但是插入和删除元素性能差,因为这会涉及到移位操作
LinkedList是基于双链表实现的,开配的内存空间不要求连续,因此不支持索引,查找元素需要从头查找,因此性能差,但是添加删除只需要改变指针指向即可,性能高. LinkedList会增加内存碎片化,增加内存管理难度
根据实际需要,如果项目中使用查找较多,使用ArrayList,如果使用增删较多,请使用LinkedList
ArrayList和Vector区别
ArrayList是线程不安全的,Vector相反是线程安全的,方法加了同步锁,线程安全但是性能差,ArrayList底层数组容量不足时,会自动扩容0.5倍,Vector会自动扩容1倍
一个User的List集合,如何实现根据年龄排序
第一种方式,让User类实现Comparable接口,覆写compareTo方法,方法中自定义根据年龄比较的算法
第二种方式,调用Collections.sort方法,传入一个比较器,覆写compare方法,方法中自定义根据年龄比较的算法
HashMap底层用到了那些数据结构?
JDK1.7及其之前:数组,链表 ; JDK1.8开始:数组,链表,红黑树
什么是Hash冲突
哈希冲突,也叫哈希碰撞,指的是两个不同的值,计算出了相同的hash,也就是两个不同的数据计算出同一个下标,通常解决方案有:
-
拉链法,把哈希碰撞的元素指向一个链表
-
开放寻址法,把产生冲突的哈希值作为值,再进行哈希运算,直到不冲突
-
再散列法,就是换一种哈希算法重来一次
-
建立公共溢出区,把哈希表分为基本表和溢出表,将产生哈希冲突的元素移到溢出表
HashMap为什么要用到链表结构
当我们向HashMap中添加元素时,会先根据key尽心哈希运算,把hash值模与数组长度得到一个下标,然后将该元素添加进去。但是如果产生了哈希碰撞,也就是不同的key计算出了相同的hash值,这就出问题了,因此它采用了拉链法来解决这个问题,将产生hash碰撞的元素,挂载到链表中
HashMap为什么要用到红黑树
当HashMap中同一个索引位置出现哈希碰撞的元素多了,链表会变得越来越长,查询效率会变得越来越慢。因此在JDK1.8之后,当链表长度超过8个,会将链表转坏为红黑树来提高查询
HashMap链表和红黑树在什么情况下转换的?
当链表的长度大于等于8,同时数组的长度大于64,链表会自动转化为红黑树,当树中的节点数小于等于6,红黑树会自动转化为链表
HashMap在什么情况下扩容?HashMap如何扩容的?
HashMap的数组初始容量是16,负载因子是0.75,也就是说当数组中的元素个数大于12个,会成倍扩容
tips:为啥子是0.75:负载因子过小容易浪费空间,过大容易造成更多的哈希碰撞,产生更多的链表和树,因此折衷考虑采用了0.75
为啥子是成倍扩容:需要保证数组的长度是2的整数次幂
为嘛数组的长度必须是2的整数次幂:我们在存储元素到数组中的时候,是通过hash值模与数组的长度,计算出下标的。但是由于计算机的运算效率,加减法>乘法>除法>取模,取模的效率是最低的。开发者们为了让你用的开心,也是呕心沥血。将取模运算转化成了与运算,即数组长度减1的值和hash值的与运算,以此来优化性能。但是这个转化有一个前提,就是数组的长度必须为2的整数次幂
HashMap是如何Put一个元素的
首先,将key进行hash运算,将这个hash值与上当前数组长度减1的值,计算出索引。此时判断该索引位置是否已经有元素了,如果没有,就直接放到这个位置
如果这个位置已经有元素了,也就是产生了哈希碰撞,那么判断旧元素的key和新元素的key的hash值是否相同,并且将他们进行equals比较,如果相同证明是同一个key,就覆盖旧数据,并将旧数据返回,如果不相同的话
再判断当前桶是链表还是红黑树,如果是红黑树,就按红黑树的方式,写入该数据,
如果是链表,就依次遍历并比较当前节点的key和新元素的key是否相同,如果相同就覆盖,如果不同就接着往下找,直到找到空节点并把数据封装成新节点挂到链表尾部。然后需要判断,当前链表的长度是否大于转化红黑树的阈值,如果大于就转化红黑树,最后判断数组长度是否需要扩容。
HashMap是如何Get一个元素的
首先将key进行哈希运算,计算出数组中的索引位置,判断该索引位置是否有元素,如果没有,就返回null,如果有值,判断该数据的key是否为查询的key,如果是就返回当前值的value
如果第一个元素的key不匹配,判断是红黑树还是链表,如果是红黑树,就就按照红黑树的查询方式查找元素并返回,如果是链表,就遍历并匹配key,让后返回value值
你知道HahsMap死循环问题吗
HashMap在扩容数组的时候,会将旧数据迁徙到新数组中,这个操作会将原来链表中的数据颠倒,比如a->b->null,转换成b->a->null
这个过程单线程是没有问题的,但是在多线程环境,就可能会出现a->b->a->b…,这就是死循环
在JDK1.8后,做了改进保证了转换后链表顺序一致,死循环问题得到了解决。但还是会出现高并发时数据丢失的问题,因此在多线程情况下还是建议使用ConcurrentHashMap来保证线程安全问题
说一下你对ConcurrentHashMap的理解
ConcurrentHashMap,它是HashMap的线程安全,支持高并发的版本
在jdk1.7中,它是通过分段锁的方式来实现线程安全的。意思是将哈希表分成许多片段Segment,而Segment本质是一个可重入的互斥锁,所以叫做分段锁。
在jdk1.8中,它是采用了CAS操作和synchronized来实现的,而且每个Node节点的value和next都用了volatile关键字修饰,保证了可见性
二.JavaEE&框架&中间件
数据库基础
平局值用什么,分组用什么
统计平局值:avg , 分组:group by
两个相同列的结果集求并集用什么
union 并集 , union all(允许重复并集)
完整查询SQL中的关键字的定义顺序
SELECT 列名 FROM 表1 JOIN 表2 ON 条件 WHERE 条件 GROUP BY 列名 HAVING 条件 ORDER BY 列名 LIMIT
完整的多表JOIN查询,SQL中关键字的执行顺序
FROM --> ON --> JOIN --> WHERE --> GROUP BY --> HAVING --> ORDER BY --> LIMIT
员工表employee字段有: id, username, amount ,deptname .
- 求每个部门总人数怎么做 ,
select 部门名,count(id) from employee group by deptname
- 求每个部门总工资怎么做?
select 部门名,sum(amount) from employee group by deptname
Spring部分
介绍一下Spring
Spring是一个开源的轻量级控制反转和面向切面编程的容器框架。轻量级是说它开发使用简单,功能强大。控制反转是指将对象的创建,销毁控制交给ioc容器,方便解耦合,降低维护难度,面向切面编程是指将相同的逻辑横向抽取出来,可以对一些通用业务如事务,日志进行集中管理
说下Spring框架的组成
- CoreContain核心容器模块:
- spring-core:提供框架的基本组成部分,包括 IoC 和依赖注入功能
- spring-beans:提供 BeanFactory,工厂模式
- context:提供国际化,事件传播,资源加载等功能
- spring-ExpressionLanguage:提供表达式语言
- Web模块
- Web:提供面向web的基本功能和面向web的应用上下文
- Web-MVC:为web应用提供模型视图控制(MVC)
- Web-Socket:在 web 应用程序中提供客户端和服务器端之间通信的方式
- 数据/集成模块
- JDBC:包含了Spring对JDBC数据访问进行封装的所有类
- ORM:为对象-关系映射提供交互层
- OXM:提供对Object/XML映射实现的抽象层
- JMS:主要包含了一些制造、消费和消息的功能
- 其他模块
- AOP:提供了面向切面编程相关实现
- Aspects:模块提供了与AspectJ的集成,是一个功能强大的AOP框架
- I
- Test:支持使用JUnit和TestNG对Spring组件进行测试
什么是Spirng的IOC
IOC控制反转,把对象的创建,属性设置,初始化,销毁等工作交给Spirng的IOC容器去管理,解放程序员的劳动力。
对象被注册到Spring的IOC容器中,使用的时候从容器中获取即可,非常方便。
它通过依赖注入,将需要的外部资源注入到组件中,使用IOC使得对象之间的耦合度降低,资源变得容易管理,从而使得代码更加优雅
你对AOP的理解
AOP,Aspect Oriented Programming 英文首字母缩写,意为面向切面编程,是Spring的核心思想之一
AOP是对OOP(面向对象编程)的一种补充,能够做到很多面向对象无法做到的事情,比如需要在所有方法执行前开启事务,打印日志,如果使用面向对象来编程,将会产生大量重复代码,而使用AOP,可以将那些与业务无关,却为业务模块所共同调用的逻辑封装起来,一次解决这些问题。而这些重复的代码,一般统称为横切逻辑代码
使用AOP,在不改变原有业务逻辑的情况下,实现解耦合,避免横切逻辑代码重复
AOP的使用场景包括日志记录,性能统计,安全控制,事务处理,异常处理等等
它是基于动态代理实现的,分为JDK动态代理和CGLIB动态代理。JDK动态代理只支持实现了接口的类 ,CGLIB支持没有实现接口的类。Spring默认使用JDK动态代理,如果被代理类没有实现接口,会选择CGLIB动态代理
Spring的Bean懒加载和非懒加载有什么区别
懒加载:需要使用对象的时候才创建,节省资源,但不利于提前发现错误
非懒加载,也叫迫切加载,容器启动时就创建对象,消耗资源,但有利于提前发现错误
spring中默认时迫切加载,即在项目启动时,spring会扫描符合条件的所有bean并将其初始化
如果需要懒加载,可以使用@Lazy注释或者xml中配置属性default-lazy-init=“true”
Spring的依赖注入方式有哪些
方式一:setter方式注入,通过反射调用无参构造方法生成对象,再通过对于的setter方法注入配置的值,支持注解和xml两种实现方式
方式二:构造器方式注入,通过反射调用有参构造方法生成对象,支持注解和xml两种实现方式
注解实现方式:@Autowired,它是默认按类型匹配的、@Resource,它是默认按名字匹配的
说一下定义切面相关的注解
@Aspect:定义切面
@Pointcut:定义切点 = cn.xx.service.*
@Before:前置通知,在目标方法运行之前运行
@After:后置通知,在目标方法运行结束之后运行(无论方法正常结束还是异常结束)
@AfterReturning:返回通知,在目标方法正常返回之后运行
@AfterThrowing:异常通知,在目标方法出现异常以后运行
@Around:动态代理,手动推进目标方法运行
Bean的四种注册方式
方式一:普通注册方式,直接通过class注册
方式二:简单静态工厂方式注册
方式三:简单实例工厂方式注册
方式四:FactoryBean方式注册
注册Bean的注解有哪些
@Controller/@RestController 一般用于定义控制层的类
@Service 一般用于定义服务层的类
@Repository 一般用于定义持久层类
@Component 定义一般类
@Configuration 定义配置类
IOC的启动流程有了解过吗
当Spring启动时,IOC容器会加载Spring的配置文件,包括XML配置或者注解,然后解析这些Bean并把相关定义信息封装成BeanDefinition对象,通过Bean注册器BeanDefinitionRegistry注册到IOC容器,也就是一个ConcurrentHashMap中
此时会找出所有的单例且非惰性加载的bean,根据其BeanDefinition进行Bean的实例化,它会判断如果bean中有方法覆盖,就使用JDK反射创建Bean,否则使用CGLIB方式生成代理。然后把实例化好的Bean缓存到一个ConcurrentHashMap中
Bean的生命周期讲一下
从宏观的角度来说就是:实例化 ,属性注入,初始化,使用,销毁。更细的生命周期如下
-
实例化:如果是单例且迫切加载的bean,在Spring容器启动时就会根据BeanDefinition进行实例化,如果时设置了懒加载或者多例模式的bean,在用的时候才会实例化
-
属性赋值:通过BeanDeifinition找到当前Bean所依赖的其他Bean,如果容器中有就直接拿过来,如果没有就根据创建流程区创建依赖的bean,然后通过反射给依赖的字段注入值
-
然后会调用BeanPostProcessor的前置处理器,对于@Autowired和@Transcational就是基于BeanPostProcessor来实现的。
-
接着会看Bean是否实现InitializingBean ,如果有会触发其afterPropertiesSet方法的调用
-
接着是调用我们自定义的bean的init-method方法,此时会调用执行
-
然后是调用BeanPostProcessor的后置处理
-
容器正常关闭,Bean进行销毁,会先调用实现了DisposableBean的destory方法。
-
接着调用我们指定的bean的destroy-method方法,此时会调用执行
单例多例的区别
单例和多例属于对象模式,单例模式指对象在整个系统中只存在一份,多例模式则可以有多个实例。
在spring的ioc容器中的bean默认都是单例的,如果需要使用多例,可以通过修改scope属性:scope=“prototype”
如果一个bean是单例模式的,在处理多次请求的时候,在ioc容器中只实例化一个bean,这个对象会被保存在一个map中,当有请求来的时候,会先从map中查看,如果有就直接使用这个对象,没有才会实例化新的对象。
如果是多例(prototype)模式的bean,每次请求来的时候,会直接实例化新的bean,没有map缓存的过程。
Spring的Bean被指定为prototype以及singleton有什么区别
这两者分别指的是多例和单例模式,singleton即单例模式,指对象在整个系统中只存在一份;prototype即多例模式系统中可以有多个实例。
如果一个bean是单例模式的,在处理多次请求的时候,在ioc容器中只实例化一个bean,这个对象会被保存在一个map中,当有请求来的时候,会先从map中查看,如果有就直接使用这个对象,没有才会实例化新的对象。
如果是多例模式的bean,每次请求来的时候,会直接实例化新的bean,没有map缓存的过程。
在spring的ioc容器中的bean默认都是单例的,如果需要使用多例,可以指定scope属性:scope=“prototype”
BeanFactory和ApplicationContext有什么区别
BeanFactory接口是IOC容器的核心接口,定义了管理bean的最基本方法,比如实例化,配置,管理,获取bean的方法
ApplicationContext接口是BeanFactory接口的子接口,除了继承BeanFactory中所有管理bean的方法,还拥有环境、国际化、资源、事件等服务相关的接口
BeanFactory是延迟加载,ApplicationContext是迫切加载
BeanFactory和FactoryBean的区别
BeanFactory接口是IOC容器的核心接口,定义了管理bean的最基本方法,比如实例化,配置,管理,获取bean的方法
FactoryBean是IOC容器创建bean的一种形式,可以通过实现此接口来创建实例化过程比较复杂的bean
IOC容器是如何保证Bean的单例的?
IOC容器会将单例模式的bean放入一个ConcurrentHashMap中,需要这个bean时直接到这个map中获取,如果没有找到才会实例化这个bean。而ConcurrentHashMap本身时线程安全的,也就保证了Bean是单例的
Spring如何解决Bean的循环依赖
循环依赖分为三种,构造器注入循环依赖 ,setter方式注入循环依赖,多例模式Bean的循环依赖。而Spring解决了单例bean的setter注入循环依赖
setter循环依赖的解决主要使用了三级缓存
- 一级缓存,用来缓存已经实例化好的bean,即单利Bean缓存池
- 二级缓存,用来缓存正在创建的bean
- 三级缓存,用来缓存创建bean的实例工厂ObjectFactory
假设有两个bean,A依赖B,B依赖A
当实例化好A,在属性注入环境,发现A依赖了B,会先将正在创建的A的实例工厂ObjectFactory放入三级缓存,然后去创建B的实例。
走Bean的实例化流程创建B,在B的属注入环节发现,B依赖了A,这个时候就会去三级缓存中,找到A的创建工厂ObjectFactory获取A的实例,并注入到B中。此时B就初始化好了,然后将B实例放入一级缓存。最后将B实例注入到A中,A也就创建好了
在getBean的时候,如果单利Bean缓存池没有Bean,就会走二级缓存尝试获取,如果也没有,就会走三级缓存拿到Bean的ObjectFacory创建Bean,然后把Bean放入二级缓存。
Spring构造器注入能循环依赖吗
构造注入不能解决循环依赖的原因是:如果A的构造其中依赖了B B的构造器中又依赖了A 在getSingleton中三级缓存需要调用getObject()构造器,来构造提早暴露但未设置属性的bean,此时就会产生无限递归创建
多例模式下Bean是不做缓存的,所以就没法暴露ObjectFactory,也就没办法解决循环依赖
说几个Spring的IOC的容器工厂类
BeanFactory:IOC容器顶层接口,提供了Bean获取的基础方法
DefaultListableBeanFactory:是整个 bean 加载的核心部分,Spring 注册及加载Bean 的默认实现
ApplicationContext:除了实现IOC基本功能外,还扩展了国际化支持,资源访问,事件发布
ClasspathXmlApplicationContext:从classpath中获取XML配置
你知道Spring的AOP主要基于什么设计模式实现吗
AOP的实现原理是基于动态代理,动态代理就是在运行时期动态的为原生类生成代理类以达到代码增强的目的,且代理类是持有原生类的,可以在代理类中调用原生类以及做一些增强业务。
动态代理分为JDK动态代理和CGLIB代理,CGLIB代理需要导入相关的jar包,两者的区别是JDK动态代理要求目标类需要实现至少一个接口。而CGLIB则是基于继承进行代理,原生类可以不实现任何接口
Spring中默认采用JDK动态代理,如果原生类没有实现任何接口,Spring会选择CGLIB代理,或者你可以在配置文件中强制指定使用CGLIB代理
你知道@Autowaire自动注入的实现原理吗?
自动注入是通过BeanPostProcessor 后置处理器AutowiredAnnotationBeanPostProcessor完成的,在Bean实例化过程中,触发了AutowiredAnnotationBeanPostProcessor的postProcessPropertyValues方法的调用执行,它就会扫描当前类中是否有@Autowired注解,然后得到自动注入依赖的bean的类型,并去容器中得到依赖的bean实例,如果没有就走Bean的实例化流程创建依赖的Ban,然后反射进行字段赋值。
你知道@Transcational注解的实现原理吗?
分为两个动作把,第一个是解析@Transcational注解,在Sping中有个后置处理器InfrastructureAdvisorAutoProxyCreator,在Bean的初始化过程中,它负责解析标记了@Transcational注解的类,生成代理。还创建了 TransactionAttributeSource ,它是对事务注解的封装,以及 TransactionInterceptor 事务拦截器。
在执行业务方法的时候,代码会进入事务拦截器TransactionInterceptor去执行事务相关的代码,TransactionInterceptor主要是通过调用TranscationManagerment的事务API,而TranscationManagerment又是调用connection的事务API完成事务操作。
Javaweb基础
常见Http状态码
200 成功返回状态
301 永久重定向,被请求的资源永久移动到新位置
302 临时重定向,被请求的资源临时移动到新的位置,项目中使用了oauth2,对目标资源访问无权限时就会见到,它是会重定向到授权地址
401 无权限访问
403 禁止访问,服务器已经接收到请求,但拒绝执行
404 找不到该资源
500 服务器内部错误 zuul找不到服务名就会见到
503 服务器内部错误 服务器维护或者过载
504 网关超时
Servlet的生命周期
Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过程:
- Servlet 初始化后调用 方法。
- Servlet 调用 方法来处理客户端的请求。
- Servlet 销毁前调用 方法。
- 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。
什么是过滤器?怎么创建一个过滤器
过滤器:在请求发送之后,处理之前对请求的一次拦截,可以更改请求状态或者参数值等。
创建过滤器:实现filter接口,重写doFilter方法,最后在web.xml中配置过滤器
讲一下Session的工作原理
服务端的session id会自动写入客户端的cookie中,每次请求客户端回自动把cookie带入后台,后台自动根据cookie中的sessionid就能找到session
Session和cookie有什么区别
session和cookie都是为了弥补http协议的无状态特性,解决会话问题
session是以ConcurrentHashMap结构存储在服务器端,同时生成一个sessionid返回客户端并存放到cookie中
cookie是将数据存储在客户浏览器端
session占用服务器的性能,但安全性较高,使用cookie减轻服务器的压力,但有被用户篡改风险因此安全性较低
说说preparedStatement和Statement的区别
statement的sql语句使用字符串拼接,很容易出错,而preparedStatement使用?作为占位符,不容易出错易于维护
statement不对sql语句作处理,直接交给数据库,而preparedStatement支持预编译,事先将编译好的sql语句放到数据库端,相当于缓存,因此效率更高
statement有sql注入风险,preparedStatement没有sql注入风险
请求转发和重定向的区别
转发是一次请求,可以共享同一组request和response,重定向是多次请求,不能共享同一组request和response
转发地址栏不会发生变化,重定向地址栏会发生变化
转发不能到外部应用,重定向可以到尾部应用
如果我们需要数据共享,使用转发,如果需要访问内部资源(WEB-INF),使用转发,如果需要跨域到外部资源,必须使用重定向
get和post请求的区别
最直观的区别,get把参数包含在url中,post是把参数放到request body中
post相对于get更安全,post发送的数据更大,get有url的长度限制
post更发送更多的数据类型,get只能发送ASCII字符
在restful中,get一般用户查询搜索数据,post一般用户添加或者修改数据
JSP的原理
jsp的本质就是servlet,每个JSP文件都回被编译成一个Serverlet去执行,在该Serverlet会对JSP中的动态内容进行替换,静态部分是标准的html,动态部分是java程序
SpringMVC部分
SpringMVC怎么样设定重定向和转发的
重定向是指将用户从当前请求重新定向到一个视图页面,或者是一个handler处理请求,以前的request域中信息全部失效,同时地址栏会发生变化,它是客户端行为
转发是指将用户从当前请求转发给另一个视图页面或者handler处理请求,以前的request域可以共享,地址栏不会发生变化,它是服务器行为
springmvc默认是使用转发方式跳转的,且会默认经过视图解析器,我们也可以通过指定,转发时在返回值前面加"forward:“,重定向时在返回值前面加"redirect:”,且此时就不会再经过视图解析器了
SpringMVC如何对时间格式的参数进行格式化
第一种需求,后台接收前台页面返回的string类型时间,要转换成的Date类型数据,可以使用@DateTimeFormat注解来接收参数
第二种需求,后台将Date类型数据返回给前台页面,默认是返回时间戳,如果想要优雅的格式,可以在模型的Date字段或get方法上使用@JsonFormat注解
SpringMVC常用的注解有哪些
@Controller:用来标识一个类是控制器类
@RequestMapping:用来映射请求路径和参数
@ResponseBody:将返回值放到responsebody中,通常返回json或者xml格式数据
@RequestBody:将前台请求参数转换成对象
@PathVariable:接收路径参数,通常用在restful接口中
@RestController:@Controller和@ResponseBody的组合注解
@ControllerAdvice:运用aop的思想,对全局做一些处理,比如结合@ExceptionHandler做全局异常捕获
如何定义SpringMVC的拦截器
SpringMVC 的拦截器主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、判断登录等功能上
第1步,定义拦截器:可以实现 HandlerInterceptor 接口来自定义拦截器,接口定义了三个方法,preHandler方法是在请求到达处理器之前执行,postHandler方法是在请求经过处理器之后、解析试图之前执行,afterCompletion方法是在视图渲染之后、返回客户端之前执行
第2步,配置拦截器:在springmvc的配置文件xml中,配置所有拦截路径,以及需要放行的路径
HandlerInterceptor和HandlerInterceptorAdapter的区别
HandlerInterceptor是接口,我们可以实现该接口来定义拦截器,HandlerInterceptorAdapter是抽象类,它实现了HandlerInterceptor接口的子接口AsyncHandlerInterceptor,我们可以继承该类来定义拦截器,它简化拦截器的实现,默认preHandler返回true
SpringMVC的执行原理
1.Http请求:客户端请求提交到DispatcherServlet-前端控制器
2.寻找处理器:由DispatcherServlet调用HandlerMapping-处理器映射器,根据url找到对应的的Handler
3.调用处理器:DispatcherServlet指定HandlerAdapter-处理器适配器去调用Handler
4.调用业务处理和返回结果:Handler调用业务逻辑处理完成后,返回ModelAndView
5.处理视图映射并返回模型: DispatcherServlet查询一个或多个ViewResoler-视图解析器,找到ModelAndView指定的视图
6.Http响应:将结果显示到客户端
SpringMVC的Controller是单例还是多例,有没有并发安全问题,如何解决
在spring中,bean默认都是单例的,controller也是交给spring容器管理的一个bean,因此它也是单例的。
单例的好处是减少了创建对象和垃圾回收的时间,节省了内存资源,但同时单例会造成线程不安全的问题,因为当所有请求访问同一个controller实例,controller中的成员变量是所有线程公用的,某个线程如果修改了这个变量,别的请求再来拿这个变量就编程修改后的值了
要解决这个问题,最直接有效的方式就是不要在controller中定义成员变量,如果你非要定义成员变量,两种方式
第一种,可以给controller上加注解@Scope(“prototype”),将controller设置为多例模式,每次请求都重新实例化一个controller
第二种,使用ThreadLocal变量,让每一个线程都有自己独立的变量
RequestMapping 和 GetMapping有什么区别
@Getmapping是一个组合注解,即是@RequestMapping(method = RequestMethod.GET)的缩写,意思是只接收get请求的方法
@Requestmapping如果没有指定请求方式,可以接收get,put等各种类型的请求
SpringBoot部分
相比Spring,Spring Boot有哪些优点
Springboot是一个基于spring的框架,对spring做了大量简化,使开发流程更快,更高效
它大量简化maven依赖,管理了大量的基础依赖
基于注解配置(JavaConfig),无需xml配置
内嵌Tomcat,部署流程简单
打包和部署更加灵活,允许独立运行
SpringBoot如何做全局异常处理
可以使用@ControllerAdvice注解,编写一个全局异常处理类,再自定义一个方法使用@ExceptionHandler来捕获具体的异常并作相应的处理
通常情况下后台向前台返回结果时,会把结果封装成包含有错误码,错误信息以及数据本身的json数据,因此我们可以使用自定义异常类,自定义枚举错误码,在捕获全局异常后,向前台返回一个包含错误码的信息
@SpringBootApplication注解的含义
@SpringBootApplication是SprnigBoot项目的核心注解,目的是开启自动配置,并表示该类为主启动类。它包含三个子标签
- @ComponentScan注解:开启ioc自动扫描注解,默认扫描当前包及其子包中@Controller,@Service等,并把这些bean加载到ioc器中
- @EnableAutoConfiguration注解:启用springboot自动配置,自动所有扫描classpath目录下面所有jar中的spring.factories文件实现配置类批量注册
- @SpringBootConfiguration注解:标志该类为springboot配置类
spring-boot-starter-parent的作用
这是SpringBoot的父工程,它的作用是帮我们管理了很多的基础jar包,同时它继承了spring-boot-dependencies,在spring-boot-dependencies项目中通过管理了大量的依赖,同时通过维护了这些依赖的版本号
但是在项目中,还需要通过 去导入具体的依赖才能使用
spring-boot-starter-web的作用
此项目是Springboot和Springmvc整个的jar包,构建了web项目的基本环境,集成了日志,tomcat,springmvc,json支持等等
SpringBoot中如何读取配置
方式一:使用@Value读取配置文件
方式二:使用@ConfigurationProperties读取配置文件
SpringBoot中日志的level有哪些
日志级别从低到高分别为:
TRACE < DEBUG <INFO <WARN < ERROR
如果设置为 WARN,则低于 WARN 的信息都不会输出
Spring中默认使用INFO级别输出到控制台
SpringBoot中如何管理事务
事务(transaction)是指业务逻辑上对数据库进行的一系列持久化操作,要么全部成功,要么全部失败。
在Springboot中,可以通过xml配置和注解配置
xml方式通过配置DataSourceTransactionManager和transactionManager实现
注解方式配置通过在主启动类上加上@EnableTransactionManagement开启事务管理器,在具体的实现层service类上加上@Transactional 实现事务
SpringBoot自动配置原理
在启动类上我们会打上: @SpringBootApplication 注解,它是一个组合标签,包括:
- SpringBootConfuration ,本质是一个 Configuration ,代表Spring的配置类。
- IOC自动扫描的注解 ,ComponentScan 会去扫描类上是否有:@Component ,@Respository ,@Service @Controller ,如果有,就会把这个类自动注册到Spring容器中。
- EnableAutoConfiguration :就是启动SpringBoot自动配置的注解
在 @EnableAutoConfiguration 注解中,注册了一个选择器,其中有一个方法会去返回很多的自动配置的的全限定名,这些类会自动注册到Spring容器中,
那它是怎么去找到这些所谓的自动配置类的呢?
他会通过Spring的SPI接口,也就是通过一个SpringFactoryLoader去扫描 classpath中的所有的jar包中的 MET-INF/spring.factories 中的自动配置类,比如: DispatchServlert就对应了DispatchServlertAutoConfiguration自动配置类 , 它通过@Bean+方法的方式注册了一个 DispatchServlert 到Spring容器中了
SpringBoot启动流程
1.开启秒表计时
2.starting监听器,
3.处理应用参数
4.加载环境对象
5.打印横幅
6.创建Spring容器对象:AnnotationConfigApplicationContext
7.容器刷新的前置工作
8.刷新容器 ,这里会执行spring的ioc属性容器的refrsh方法,Bean的加载,初始化等都在这个里面,Tomcat的启动也在这个方法里面。
9.刷新容器后置工作
10.秒表停止
11.started事件
12.调用runner
13.running.listeners.
Mybatis部分
MyBatis中${}取值和#{}取值的区别
{}能够防止SQL注入,因为底层使用PreparedStatement对象,预编译,性能较高
${}不能防止SQL注入,因为底层使用Statement对象,不会预编译而是拼接字符串,性能较低
能使用#{}时尽量使用#{},如果需要动态传入表名或者字段名需要用 比 如 , 像 O R D E R B Y 时 只 能 使 用 {}比如,像 ORDER BY 时只能使用 比如,像ORDERBY时只能使用{}
MyBatis关联查询中,延迟加载和饥饿加载的区别
延迟加载,是先从单表查询,需要使用关联数据的时候才发起关联查询,不用的时候不查询关联的数据,又叫懒加载,饥饿加载,是在查询时将关联的数据立即查询出来加载进内存,不管用不用
MyBatis对象关联查询和集合关联查询怎么做
单个关联对象用associate ,适用于多对一的关联查询,使用javaType来定义实体类型,集合用collection,适用于一对多的关联查询,使用ofType来定义集合的泛型类型
MyBatis一级缓存和二级缓存的区别
缓存,是指将从数据库查询出的数据存放在缓存中,下次使用相同查询时不必再从数据库查询,而是直接从缓存中读取,从而减轻数据库查询的压力,提高性能
mybaits中的一级缓存,是SqlSession级别,默认开启,使用同一个SqlSession发送相同的SQL时命中;它的生命周期和SqlSession一致,当调用SqlSession.close()方法时会释放缓存
mybatis中的二级缓存,是namespace级别,默认不开启,执行同一个namespace的相同statement,发送相同的SQL时命中;它的生命周期是程序结束
当SQL中执行了update()、delete()、insert()操作,则缓存中的数据都会清空
MyBaits的Mapper接口没有实现类为社么可以用@Autowired直接注入
动态代理,赋值给mapper接口引用的对象其实是一个代理对象,这个代理对象是由 JDK 动态代理创建的。在解析mapper的时候,mybatis会通过java反射,获取到接口所有的方法
当调用接口中方法时,将通过接口全限定名+方法名对应找到映射文件中namespace和id匹配的sql,然后将执行结果返回
在MyBatis如何动态修改SQL
使用Mybatis的拦截器可以做到
MyBatis的动态SQL标签有哪些?
if标签:条件判断
choose、when、otherwise标签:选择结构,类似java中的switch
trim标签:对包含的内容加上前缀,后缀
where标签:主要是用来简化SQL语句中where条件判断的,能智能的处理and or,不必担心多余导致语法错误
foreach标签:遍历元素
Mybatis的mapper如何传递多个参数
方式一,可以使用map进行传参,SQL中使用map的key来引用取值
方式二,可以在SQL中使用#{param1},#{param2}…来引用取值,它是根据mapper接口对应方法中形参的顺序进行匹配的,不管接口方法的参数名字叫个啥,SQL都只能使用param1,param2,等来取值
方式三,可以使用@Param注解,给mapper接口方法的参数命名,在SQL中直接使用取的名字来引用
Mybatis,关联对象查询,使用嵌套子查询和JOIN连表有什么区别
嵌套子查询,指的是在查询一个主对象的时候,使用单表查询,在resultmap中额外发送一个子sql查询关联对象,然后映射给主对象
连表join查询,指的是查询一个主对象的时候,使用join连表的方式把主对象和关联对象的数据一次性查出来,用resultmap映射结果
他们的区别,join连表查询只发一条sql就能把数据查询出来,嵌套子查询会有一个n+1的问题,就是说如果主查询出来n条数据,那么会额外发送n条子sql去查询对应的关联对象,加上主查询那1次,也就是n+1次,因此它的性能相对较低的,一般我们会使用join连表查询
为什么要使用连接池
对数据库的操作都需要取得连接,使用完都需要关闭连接,如果每次操作需要打开关闭连接,这样系统性能很低下。连接池就可以动态的管理这些连接的申请,使用和释放,我们操作数据库只需要在连接池里获取连接,使用完放回连接池,这样大大节省了内存,提高效率。
数据库连接池的原理主要分为三部分
- 第一,连接池的建立,在系统初始化时建立几个连接对象以便使用。
- 第二,连接池的管理,客户请求连接数据库时,首先查看连接池中是否有空闲连接,如果有直接分配,如果没有就等待,直到超出最大等待时间,抛出异常
- 第三,连接池的关闭,当系统关闭时,连接池中所有连接关闭
Redis部分
讲一下你理解的Redis,为什么Redis很快
Redis是一种高性能的,开源的,C语言编写的非关系型数据库,可以对关系型数据库起到补充作用,同时支持持久化,可以将数据同步保存到磁盘
说Redis很快是相对于关系型数据库如mysql来说的,主要有以下因素
- 第一,数据结构简单,所以速度快
- 第二,直接在内存中读写数据,所以速度快
- 第三,采用多路IO复用模型,减少网络IO的时间消耗,避免大量的无用操作,所以速度快
- 第四,单线程避免了线程切换和上下文切换产生的消耗,所以速度快
你常用的Redis的数据存储结构有哪些,他们的使用场景分别是什么
Redis存储形式是键值对,支持value形式包括String,List,Set,ZSet,Hash。
String可以用作缓存,计数器,防攻击,验证码、登录过期等,List可以用来做队列,秒杀等,Set可以用来去重
Redis每种存储结构说 4 个命令吧
1.String
- set key value 设置值
- get key 取值
- mset key value key value… 设置多个值
- mget key key 获取多个值
- incr key 将key中的值自增1
- decre key 将key中的值自减1
2.List
- lpush key value value… 从最左边设置值
- rpush key value value… 从最右边设置值
- lrange key start stop 查询key中指定区间的元素
- lpop key 移出并返回key中最左边的元素
- rpop key 移出并