java面试总结宝典上部

本文涵盖了Java面试的众多核心知识点,包括JVM常用命令、XSS和CSRF攻击、CORS、MySQL索引和事务隔离级别、锁的研究、HashMap扩容、线程安全与并发工具类、Spring AOP原理、JDK新特性、JVM内存管理和GC算法等,旨在帮助读者全面复习和准备Java面试。
摘要由CSDN通过智能技术生成

目录

1.@postconstruct @init-method @afterPropertiesSet 顺序

2. JVM常用命令

3.Xss攻击

4.CSRF

5.CORS

6. Mysql索引

7. Mysql事务隔离级别

8. Mysql调优注意事项

8.1 慢查询

9 Mysql 锁研究

9.1 行锁:记录锁(Record Locks)

9.2 间隙锁 [  Gap lock  ]: (可重复读隔离级别来解决幻读的问题)

9.3 MVCC

9.4 行锁升级为表锁的情况

10 HashMap

10.1 扩容过程:

10.2 Hashmap为什么长度是2的幂

11 Mysq两种引擎

11.1MyISAM

12 redis原理

13 casandra特点

14. 密码交易安全

15 redis key过大

16. AOP动态代理加反射,静态代理,cglib

17 Volatile

18 CAS算法

19 concurrentHashmap改进

20 有序的map

21 NIO

21.1 Select机制

 21.2 Epoll机制

21.3 总结:

22 反射创建对象方式

23 Class.forName和ClassLoader的区别

24 深入ClassLoader

1.为什么要自定义class loader

2.如何打破1双亲委托模式

3.现实应用中打破双亲委托的情况

25 代理实现的三种方式

1.静态代理

2.动态代理

3.CGlib代理

26 Spring AOP 原理

27 JDK1.8新特性

28 Java并行流的实现

29.JVM原理及GC算法

1.CMS收集器(针对老年代,两次stop the world)

2.G1收集器

3.比较一下:

4.JVM回收算法

29 GC类型

30 JVM结构

31 JVM内存分配流程

32 JVM内存溢出

33 JVM重要概念

34 Happens-before规则

35 内存屏障

36 Spring加载流程

37 Spring事务的传播属性

38 线程池

39.Netty线程模型

40.Spring boot启动流程

41.CPU 负载过高排查

42.Voliatile和synchronized

43.线程生命周期

44.Spring单例

45.三个线程输出ABC循环10次

46.原子类原理

47.JUC用过那些并发工具类

48.CAS

49.CountDownLatch

原理

50.ThreadLocal注意点

51 无锁数据结构

52 Countdownlatch和CyclicBarrier

53 AQS

1.AQS的基础CLH队列

2.Volatile和CAS

54 公平锁、非公平锁

55 TCP三次握手

为什么挥手是四次?

Close wait和Time wait

56 页面打开流程

57 长连接

58 HTTPS

59 Session和Cookie

 60 分布式唯一ID生成策略

Snowflake算法

UUID

Redis

61 延迟关闭交易


 

1.@postconstruct @init-method @afterPropertiesSet 顺序

Constructor > @PostConstruct > InitializingBean > init-method

2. JVM常用命令

jmap(JVM Memory Map)命令用于生成heap dump文件

jstack用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因

3.Xss攻击

xxs攻击  xss表示Cross Site Scripting(跨站脚本攻击),通过插入恶意脚本,实现对用户游览器的控制

   假如用户提交的数据含有js代码,不做任何处理就保存到了数据库,读出来的时候这段js代码就变成了可执行的代码,将会产生意向不到的效果。一般用户提交的数据永远被认为是不安全的,在保存之前要做对应的处理。这次我就遇到了这个问题,

   我提交的内容<script>alert(1111)</script>,或者<a href="www.baidu.com">百度</a>,读出来的时候,将直接弹出1111,或者百度是有效的超链接,这个显然是不行的。

4.CSRF

 CSRF - Cross-Site Request Forgery - 跨站请求伪造

CSRF攻击之所以能够成功,是因为攻击者可以伪造用户的请求,该请求中所有的用户验证信息都存在于Cookie中,因此攻击者可以在不知道这些验证信息的情况下直接利用用户自己的Cookie来通过安全验证。由此可知,抵御CSRF攻击的关键在于:在请求中放入攻击者所不能伪造的信息,并且该信息不存在于Cookie之中。鉴于此,系统开发者可以在HTTP请求中以参数的形式加入一个随机产生的token,并在服务器端建立一个拦截器来验证这个token,如果请求中没有token或者token内容不正确,则认为可能是CSRF攻击而拒绝该请求。

用户不知情的情况下,浏览器利用cookie访问恶意网站,所以,可以这样防止:

1 要求输入验证码

2 利用token,不容易伪造

3 检查referrer字段,不是完全可以相信的,因为可以伪造

5.CORS

SOP就是Same Origin Policy同源策略,指一个域的文档或脚本,不能获取或修改另一个域的文档的属性。也就是Ajax不能跨域访问,我们之前的Web资源访问的根本策略都是建立在SOP上的。它导致很多web开发者很痛苦,后来搞出很多跨域方案,比如JSONP和flash socket

后来出现了CORS-CrossOrigin Resources Sharing,也即跨源资源共享,它定义了一种浏览器和服务器交互的方式来确定是否允许跨域请求。它是一个妥协,有更大的灵活性,但比起简单地允许所有这些的要求来说更加安全。

6. Mysql索引

二叉查找树(左边小于根节点,右边大于根节点)-->平衡二叉树(AVL),其中包括红黑树(相对平衡)---->B tree平衡多路查找树(多路,减少节点数目,每个节点都是一个磁盘块,查找过程中寻找磁盘块,高度就是IO读取次数,看下图)

索引

模拟查找关键字29的过程:

  1. 根据根节点找到磁盘块1,读入内存。【磁盘I/O操作第1次】
  2. 比较关键字29在区间(17,35),找到磁盘块1的指针P2。
  3. 根据P2指针找到磁盘块3,读入内存。【磁盘I/O操作第2次】
  4. 比较关键字29在区间(26,30),找到磁盘块3的指针P2。
  5. 根据P2指针找到磁盘块8,读入内存。【磁盘I/O操作第3次】
  6. 在磁盘块8中的关键字列表中找到关键字29。

B+Tree相对于B-Tree有几点不同:

  1. 非叶子节点只存储键值信息。
  2. 所有叶子节点之间都有一个链指针。
  3. 数据记录都存放在叶子节点中

索引

提升查找速度的关键就在于尽可能少的磁盘I/O,那么可以知道,每个节点中的key个数越多,那么树的高度越小,需要I/O的次数越少,因此一般来说B+Tree比BTree更快,因为B+Tree的非叶节点中不存储data,就可以存储更多的key

数据库中的B+Tree索引可以分为聚集索引(clustered index)和辅助索引(secondary index)。上面的B+Tree示例图在数据库中的实现即为聚集索引,聚集索引的B+Tree中的叶子节点存放的是整张表的行记录数据。辅助索引与聚集索引的区别在于辅助索引的叶子节点并不包含行记录的全部数据,而是存储相应行数据的聚集索引键,即主键。当通过辅助索引来查询数据时,InnoDB存储引擎会遍历辅助索引找到主键,然后再通过主键在聚集索引中找到完整的行记录数据。

 

聚簇索引和非聚簇索引

  • MyISAM使用的是非聚簇索引,所有索引的叶子节点存储的是具体记录的物理地址;innodb主键使用的是聚簇索引,叶子节点存储的时候具体的记录,如果是主键索引,只能能找到记录行的全部数据,如果是辅助索引,那么叶子节点存储的是是主键,需要到主键的B+ tree上进行二次查询。

  • 如果主键比较大的话,那辅助索引将会变的更大,因为辅助索引的叶子存储的是主键值;过长的主键值,会导致非叶子节点占用占用更多的物理空间
  • 主键为什么自增?因为主键是聚簇索引,逻辑顺序和物理顺序一致,如果不是自增,需要调整磁盘物理地址,消耗很大
  • 聚簇索引的叶子节点包括数据行,如果是主键索引,直接查询到数据;如是辅助索引,叶子节点是主键值,需要再次去主键索引树进行查询。叶子节点是全部的数据吗?是的,并且是一个链式结构所以innodb引擎不建议利用uuid作为主键,选择自增序列。一方面和物理顺序一样,减少io次数;另一方面节省空间,因为其他辅助索引的叶子节点都存储了主键值。
  • 非聚簇索引的叶子节点仅仅包含地址。

 

7. Mysql事务隔离级别

事务的隔离级别分为:未提交读(read uncommitted)、已提交读(read committed)、可重复读(repeatable read)、串行化(serializable)

  • 脏读:指一个线程中的事务读取到了另外一个线程中未提交的数据。
  • 不可重复读(虚读):指一个线程中的事务读取到了另外一个线程中提交的update的数据。
  • 幻读:指一个线程中的事务读取到了另外一个线程中提交的insert的数

可能出现脏读,幻读,不可重复读

可能出现不可重复读和幻读 

  

可能出现 幻读,和读已提交读区别是,重复读取的值不变!!!

上面三个隔离级别对同一条记录的读和写都可以并发进行,但是串行化格式下就只能进行读-读并发。只要有一个事务操作一条记录的写,那么其他要访问这条记录的事务都得等着

8. Mysql调优注意事项

 

总体说,三方面: 第一sql语句,第二建表语句(数据类型,字段属性,长度,是否为空),第二索引,第三mysql参数

8.1 慢查询

1、查看slowlog,分析slowlog,分析出查询慢的语句。

2、按照一定优先级,进行一个一个的排查所有慢语句。

3、分析top sql,进行explain调试,查看语句执行时间。

4、调整索引或语句本身。

使用explain查看执行情况

使用索引,根据rows看看查询了多少行,判断是不是有问题,是不是缺少索引。

  • 合理使用索引,避免全表扫描
  • 尽量使用等值查询
  • 避免使用union和limit,如果使用limit,采用内连接
  • 最左匹配原则
  • 如果是联合索引,第一个字段必须有,才能生效
  • 列值如果大量为空,或者差别不大,mysql可能放弃索引,扫描全表
  • 控制索引数量,索引提高了查询效率,但是影响insert和update
  • 任何地方都不要用select *
  • in 查询参数过长问题
  • 避免大事物操作
  • in 查询如果列值类型是vachar,参数是int,不走索引

null值查询使用is null/is not null查询,而empty string使用=或者!=查询即可

null列在mysql查询中走索引,mysql认为值为NULL是有值的,可以测试查询select * from table where name is not null;

 

联合索引生效情况:

(1)    select * from myTest  where a=3 and b=5 and c=4;   ----  abc顺序
abc三个索引都在where条件里面用到了,而且都发挥了作用


(2)    select * from myTest  where  c=4 and b=6 and a=3;
where里面的条件顺序在查询之前会被mysql自动优化,效果跟上一句一样


(3)    select * from myTest  where a=3 and c=7;
a用到索引,b没有用,所以c是没有用到索引效果的


(4)    select * from myTest  where a=3 and b>7 and c=3;     ---- b范围值,断点,阻塞了c的索引
a用到了,b也用到了,c没有用到,这个地方b是范围值,也算断点,只不过自身用到了索引


(5)    select * from myTest  where b=3 and c=4;   --- 联合索引必须按照顺序使用,并且需要全部使用
因为a索引没有使用,所以这里 bc都没有用上索引效果


(6)    select * from myTest  where a>4 and b=7 and c=9;
a用到了  b没有使用,c没有使用


(7)    select * from myTest  where a=3 order by b;
a用到了索引,b在结果排序中也用到了索引的效果,a下面任意一段的b是排好序的


(8)    select * from myTest  where a=3 order by c;
a用到了索引,但是这个地方c没有发挥排序效果,因为中间断点了,使用 explain 可以看到 filesort


(9)    select * from mytable where b=3 order by a;
b没有用到索引,排序中a也没有发挥索引效果
————————————————
版权声明:本文为CSDN博主「smileTimLi」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_35275233/article/details/87888809

 

9 Mysql 锁研究

Mysql默认采用的是可重复读(即repeatable read)隔离级别,并且会以Next-Key Lock的方式(即record lock和gap lock的结合)对数据行进行加锁,这样可以有效防止幻读的发生,但是在平时开发过程中,一些不正确的行为可能会导致间隙锁锁定范围变得很大,线程间互相等待从而导致死锁,下面将提供一些解决思路供大家参考:

9.1 行锁:记录锁(Record Locks)


(1)记录锁, 仅仅锁住索引记录的一行,在单条索引记录上加锁。
(2)record lock锁住的永远是索引,而非记录本身,即使该表上没有任何索引,那么innodb会在后台创建一个隐藏的聚集主键索引,那么锁住的就是这个隐藏的聚集主键索引。
所以说当一条sql没有走任何索引时,那么将会在每一条聚合索引后面加X锁,这个类似于表锁,但原理上和表锁应该是完全不同的
 

9.2 间隙锁 [  Gap lock  ]: (可重复读隔离级别来解决幻读的问题)

在索引记录之间的间隙中加锁,或者是在某一条索引记录之前或者之后加锁,并不包括该索引记录本身。

 

间隙锁生效:

1. 条件查询,会比如a>1 and a<6那么这个范围都会加间隙所。如果a>3,那么大于3的所有记录都会加间隙锁,甚至包括不存在的记录,为了防止幻读。

2. 如果使用相等条件查询一个不存在的记录,锁住全表,导致其他事务插入阻塞。

比如  select * from where xxx for update. 其中where的条件记录不存在。

再比如  update a  set a='11' where b='222' 其中b存在就会全表锁住。为了防止这种情况,b列如果是唯一索引就没有问题,不会升级为全表锁。

 

间隙锁在InnoDB的唯一作用就是防止其它事务的插入操作,以此来达到防止幻读的发生,所以间隙锁不分什么共享锁与排它锁。如果InnoDB扫描的是一个主键、或是一个唯一索引的话,那InnoDB只会采用行锁方式来加锁,而不会使用Next-Key Lock的方式,也就是说不会对索引之间的间隙加锁。

当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(GAP LOCK),间隙锁和行锁合称Next-Key Lock

举例来说,假如user表中只有101条记录,其empid的值分别是 1,2,...,100,101,下面的SQL:

select * from  user where user_id > 100 for update;
是一个范围条件的检索,InnoDB不仅会对符合条件的user_id值为101的记录加锁,也会对user_id大于101(这些记录并不存在)的“间隙”加锁。

InnoDB使用间隙锁的目的,一方面是为了防止幻读,以满足相关隔离级别的要求,对于上面的例子,要是不使用间隙锁,如果其他事务插入了user_id大于100的任何记录,那么本事务如果再次执行上述语句,就会发生幻读;另外一方面,是为了满足其恢复和复制的需要
 

对于select from where for update操作,一定要保证where条件字段的值一定存在,不然可能会导致间隙锁锁定的范围变得很大,阻塞别的事务,也有可能会导致死锁

利用MVCC实现一致性非锁定读,保证在同一个事务中多次读取相同的数据返回的结果是一样的解决了不可重复读问题.

利用Gap Locks和Next-key可以阻止其他事务在锁定区间内插入数据,解决了幻读问题.

9.3 MVCC

MVCC是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,一个保存了行的创建时间,一个保存行的过期时间(或删除时间)。当然存储的并不是实际的时间值,而是系统版本号(system version number)。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较

  • 乐观锁机制,解决可重复读
  • 每行记录都会保存两个隐藏列,一个是创建时间,一个是过期时间,实际系统版本号,每开启事务都会递增
  • 读取时,读取创建时间小于自己的事务id,所以不会读取到事务读取过程中,并发插入的数据
  • 读取时,如果别的事务并发修改了同一条记录,则在过期时间记录值是修改的事务id,读取的时候还是读取创建时间小于自己事务id的,不会受到影响,实现了可重复读
  • 读取时,如果别的事务删除了同一条记录,则在过期时间记录值是删除的事务id,读取时候也不影响,跟更新一样

https://www.jianshu.com/p/f692d4f8a53e

 

9.4 行锁升级为表锁的情况

众所周知,MySQL 的 InnoDB 存储引擎支持事务,支持行级锁(innodb的行锁是通过给索引项加锁实现的)。得益于这些特性,数据库支持高并发。如果 InnoDB 更新数据使用的不是行锁,而是表锁呢?是的,InnoDB 其实很容易就升级为表锁,届时并发性将大打折扣了

经过我操作验证,得出行锁升级为表锁的原因之一是: SQL 语句中未使用到索引,或者说使用的索引未被数据库认可(相当于没有使用索引)。未被认可,通常来讲,就是mysql优化器认为直接扫表比索引还快,所以忽略。比如如果一列重复率很高,那么在此列建立索引,会被mysql忽略

 

10 HashMap

10.1 扩容过程:

  • 若threshold(阈值)不为空,table的首次初始化大小为阈值,否则初始化为缺省值大小16
  • 默认的负载因子大小为0.75,当一个map填满了75%的bucket时候,就会扩容,扩容后的table大小变为原来的两倍
  • 假设扩容前的table大小为2的N次方,有上述put方法解析可知,元素的table索引为其hash值的后N位确定
  • 扩容后的table大小即为2的N+1次方,则其中元素的table索引为其hash值的后N+1位确定,比原来多了一位
  • 重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫作rehashing

因此,table中的元素只有两种情况:
元素hash值第N+1位为0:不需要进行位置调整
元素hash值第N+1位为1:调整至原索引+old Capacity 位置
扩容或初始化完成后,resize方法返回新的table

10.2 Hashmap为什么长度是2的幂

  • index=hash&(table.length-1)如果table的长度是2的n次幂,那么table.length-1永远都是1,按照位运算规则,hash值是1,则结果是1,hash值是0,结果是0。如果table.length-1不全部都是1,出现0,那么hash值里面0或者1结果都是0,索引会冲突。
  • 2的n次幂,比如长度16,table.length-1结果是15。全部是1,高位自动补充为0.按照位运算的规则,其实结果还是hash值的最后n位。因为高位和0与运算后,都是0。能够快速计算索引位置。length(2的整数次幂)的特殊性导致了length-1的特殊性(二进制全为1)。位运算快于十进制运算,hashmap扩容也是按位扩容
  • 既然hash后的索引由最后n位确定,那么hash值的最后n位是否均匀呢?如果只有高位变化了岂不是要冲突。我们看看hashmap计算hash值的方法
     return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

        计算出hash值后,进行异或操作,异或操作的对象是hash值无符号右移动16位,高位全部是0.因为hash值是32位的,这样做的目的异或的对象是hash值的高位,也就是hash值的低16和高16进行异或操作,hash值的高16和“全部为0”进行异或,此部分不变化。很巧妙的把高16的变化反应到hash值结果计算中

  • rehash呢?扩容后的table大小即为2的N+1次方,则其中元素的table索引为其hash值的后N+1位确定,比原来多了一位。n+1位如果是0,索引不变。第n+1位如果是1,调整后索引为=原索引+old capacity

 

 

在多线程使用场景中,应该尽量避免使用线程不安全的HashMap,而使用线程安全的ConcurrentHashMap。那么为什么说HashMap是线程不安全的,下面举例子说明在并发的多线程使用场景中使用HashMap可能造成死循环。代码例子如下(便于理解,仍然使用JDK1.7的环境):

public class HashMapInfiniteLoop {
private static HashMap<Integer,String> map = new HashMap<Integer,String>(2,0.75f);
public static void main(String[] args) {
map.put(5, "C");
new Thread("Thread1") {
public void run() {
map.put(7, "B");
System.out.println(map);
};
}.start();
new Thread("Thread2") {
public void run() {
map.put(3, "A);
System.out.println(map);
};
}.start();
}
}
  • 上个例子中,size等于2,我们多线程放入3个元素,会触发扩容,在扩容的过程中,链表可能形成闭环,导致死循环
  • jdk1.8之前,hashmap中的链表都是头插法,1.8之后为了防止闭环问题,改为尾插法 
  • 另外,treemap是有序的,所以底层实现是红黑树,不是map
  • linkedhashmap底层是哈希表,为了实现有序的功能,把entry加入了双向链表

11 Mysq两种引擎

11.1索引结构不同

 

 

 

  • Myisam的索引是B+tree,但是叶子节点存储的是地址,不是记录,索引和数据文件是分开的,这种叫非聚集索引
  • nnoDB引擎索引结构的叶子节点的数据域,存放的就是实际的数据记录(对于主索引,此处会存放表中所有的数据记录;对于辅助索引此处会引用主键,检索的时候通过主键到主键索引中找到对应数据行),或者说,InnoDB的数据文件本身就是主键索引文件,这样的索引被称为“聚簇索引”,一个表只能有一个聚簇索引。

11.2 InnoDB支持事务,MyISAM不支持

11.3InnoDB支持行级锁,MyISAM支持表级锁

11.4 MyISAM保存表的具体行数,查询很快,InnoDB需要扫描全表

 

https://www.jianshu.com/p/d0ee1ca57f14

12 redis原理

单线程 IO多路复用

13 casandra特点

14. 密码交易安全

输入
md5(thirdid+inputpwd+idno)
md5[md5(thirdid+inputpwd+idno)+randomkey]

数据库密码其实是md5(thirdid+inputpwd+idno)加上随机数后的结果

15 redis key过大

Redis使用过程中经常会有各种大key的情况, 比如:

  • 1: 单个简单的key存储的value很大
  • 2: hash, set,zset,list 中存储过多的元素(以万为单位)

由于redis是单线程运行的,如果一次操作的value很大会对整个redis的响应时间造成负面影响,所以,业务上能拆则拆,下面举几个典型的分拆方案。

第一个场景:

 

单个简单的key存储的value很大-------------分拆为多个key-value存储,减轻IO压力,多个redis intance处理

第二个场景:

hash, set,zset,list 中存储过多的元素

类似于场景一种的第一个做法,可以将这些元素分拆。

以hash为例,原先的正常存取流程是 hget(hashKey, field) ; hset(hashKey, field, value)
现在,固定一个桶的数量,比如 10000, 每次存取的时候,先在本地计算field的hash值,模除 10000, 确定了该field落在哪个key上。

newHashKey  =  hashKey + (*hash*(field) % 10000);   
hset (newHashKey, field, value) ;  
hget(newHashKey, field)

 

16. AOP动态代理加反射,静态代理,cglib

关键点:

 Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInterfaces(), this)

  • 生成代理对象:注意入参,classloader,代理类的接口,invocation handler实现类。第三个参数就是用来分发方法调用的。
  • 定义invocation handler:每个代理对象关联一个 invocation handler
package com.puhui.web.advice;

import com.google.common.base.Strings;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

pu
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值