Java面试题总结

spring事务的传播机制

 

spring事务失效原因

1,数据库不支持事务:比如mysql数据库,MyISAM(马儿爱涩m)引擎不持支事务,需要使用InnoDB引擎才行
2,该类不受spring管理,使用@Transactional会失效:因为spring事务是基于aop动态代理实现的,需要从ioc容器中获取bean才能为该对象生成代理类,从而支持事务。
3,@Transactional注解下的事务方法不是public:spring源码中,获取事务属性方法时会判断是否为public,非public直接返回null
4,异常被业务代码catch时:spring catch到Throwable异常事务会进行回滚,业务代码捕获异常导致spring catch不到异常所以不回滚
5,@Transactional(rollbackFor属性配置错误:比如配置的是Error.class,但是实际异常是Exception,就不会回滚
6,同一个类中,非事务方法调用事务方法
7,事务传播属性使用错误:事务默认传播机制时required,比如需要使用required却使用了suports时

 

spring事务的隔离级别

⚪ DEFAULT:Spring中默认的事务隔离级别,以连接的数据库的事务隔离级别为准;
⚪ READ_UNCOMMITTED:读未提交,也叫未提交读,该隔离级别的事务可以看到其他事务中未提交的数据。该隔离级别因为可以读取到其他事务中未提交的数据,而未提交的数据可能会发生回滚,因此我们把该级别读取到的数据称之为脏数据,把这个问题称之为脏读;
⚪ READ_COMMITTED:读已提交,也叫提交读,该隔离级别的事务能读取到已经提交事务的数据,因此它不会有脏读问题。但由于在事务的执行中可以读取到其他事务提交的结果,所以在不同时间的相同 SQL 查询中,可能会得到不同的结果,这种现象叫做不可重复读;
⚪ REPEATABLE_READ:可重复读,它能确保同一事务多次查询的结果一致。但也会有新的问题,比如此级别的事务正在执行时,另一个事务成功的插入了某条数据,但因为它每次查询的结果都是一样的,所以会导致查询不到这条数据,自己重复插入时又失败(因为唯一约束的原因)。明明在事务中查询不到这条信息,但自己就是插入不进去,这就叫幻读 (Phantom Read);
⚪ SERIALIZABLE:串行化,最高的事务隔离级别,它会强制事务排序,使之不会发生冲突,从而解决了脏读、不可重复读和幻读问题,但因为执行效率低,所以真正使用的场景并不多。

 

mysql的优化

1、选择合适的字段类型
2、尽量把字段设置为NOT NULL
3、使用连接(JOIN)代替子查询(Sub-Queries)
4、使用联合(UNION)代替临时表

还有一些会导致索引失效的问题:
like以%开头;
or前后有不是索引的字段时;
组合索引不是使用第一列索引;
IS NULL,IS NOT NULL,NOT,< >,!=
字段上使用函数时

mysql优化:
使用join 代替子查询
避免使用函数索引
可以用in 替换 or
不要用select *

ArrayList 与 LinkedList 的区别:

数据结构不同:ArrayList底层使用的是 动态数组,初始长度为10,每次扩容为原来的1.5倍。连续的内存存储适合做查询操作。
LinkedList底层使用的是 双向链表,分散存储适合做插入和删除操作。


时间复杂度不同:ArrayList 支持下标随 机访问,时间复杂度为 O(1)
LinkedList 每次检索会把链表中节点遍历一遍 直到查到,时间复杂度为 O(n)


LinkedList不仅要存储数据,还需要维护指向前后节点的引用,更加占用内存。
理论上来说ArrayList适合做查询,不适合插入和删除操作。如果使用得当,操作ArrayList注意采用尾插法,并且根据业务指定初始长度,尽量避免数据的扩容,这样能够大大提高性能。(数组扩容:会新建一个数组将旧的数组中元素插入到新数组中,扩容损耗性能)

// 迭代器遍历
Iterator iterator = arrayList.iterator();
while(iterator.hasNext()){
  iterator.next();
}
// 增强for循环遍历
for(String str:arrayList){
  str;
}

 

HashMap 底层数据结构:

HashMap底层采用的是 数组+链表 或 红黑树的方式。数组初始长度为16,负载因子0.75(就是说数组中插入的元素达到数组长度的75% 数组就会进行扩容,每次扩容为原来的2倍)。数组长度超出64,并且链表长度超出8,链表就会进行树化。

for(Map.Entry<> entry:hashMap.entrySet()){
entry.getKey(); entry.getValue();
}

for(Object key:hashMap.keySet()){
hashMap.get(key);
}

 

JVM

双亲委派机制

根加载器 > 扩展加载器 > 类加载器/应用程序加载器 > 自定义加载器

类加载器收到类加载的请求,会将这个请求向上委托给父类加载器去完成,一直向上委托直到根加载器。
根加载器检查是否能够加载当前这个类,能加载就使用当前加载器加载,加载失败会通知子加载器进行加载。
如果所有的加载都失败了 就会抛出ClassNotFoundException
(正常我们编写的类都是 类加载器加载!)

双亲委派机制的作用

防止加载同一个class文件。通过委托的方式去询问父级是否已经加载过该class,如果加载过了就不需要重新加载。从而保证了数据安全。

这种方式,也保证Java核心class不被篡改,即使被篡改也不会被加载,即使被加载也不会是同一个class对象,因为不同的加载器加载同一个.class也不是同一个Class对象。这样则保证了Class的执行安全。

 

springboot自动装配原理

springboot的自动装配实际上就是从spring.factories文件中获取需要进行自动装配的类,并且生成bean对象,然后交给ioc容器管理。

springboot自动装配原理:
首先springboot项目启动类上有一个核心注解@SpringBootApplication,这是一个复合注解。
这个注解中有一个重要子注解@EnableAutoConfiguration,用于开启自动配置的功能。
它里面包含了两个注解,@AutoConfigurationPackage和@Import注解
@Import注解里面的,它的AutoConfigurationImportSelector这个类中最终会调用一个loadSpringFactories的方法,它会查找Meta-inf路径下的spring.factories的文件
这个文件里面都是key-value形式的,然后会解析这些文件找出以EnableAutoConfiguration为key的所有值,返回一个类的列表。这些就是自动装配的类,然后将他们交给ioc容器管理。

mybatis中#{} 和 ${}的区别

mybaits中提供了#和$两种占位符,都是实现动态sql的方式。他们可以动态传递参数到xml文件中。#占位符等同于jdbc里面的?占位符,它相当于向PreparedStatement里的预处理语句设置参数。而PreparedStatement是预编译的,并且在设置参数时如果有特殊字符会进行自动转义。所以#占位符可以防止sql注入。

使用$方式传参,相当于直接把参数拼接到sql中,不会进行特殊处理。

#和$最大的区别就是,#是占位符,$是动态参数。而动态参数无法防止sql注入。实际开发中应该尽可能使用#,$的动态传参可以用来动态传递表名和字段名

posted @ 2023-05-19 09:53  故國神遊  阅读(26)  评论(0编辑  收藏  举报