Java面试题

HashMap的实现原理

HashMap底层是数组实现,里面存放的是Node类的节点。如果不设置初始大小的话默认是16,每次扩容时,是扩容2倍大小。而且是先插入数据再扩容,插入数据时会先通过hash值检查数组中此位置是否有对应的元素,如果为null,直接插入位置。如果不是null,检查已存在的元素的hash值与新的传入的元素key的hash值是否一致,且内存地址一致,且equals返回true,然后检查已存在的元素是否是红黑树节点,如果不是就放在已存在元素的后面,如果是就放入红黑树

1
2
3
4
5
6
7
8
/**
计算hash值的时候,为什么要和hash值右移动16后再进行异或操作呢。如果使用&,散列的效果会差,使用^是让高位也参与运算,让下标更加的散列
**/
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//参考:https://blog.csdn.net/qq_42034205/article/details/90384772
1
2
3
4
/**
这里插入一些这句判断,如何保证数组不越界呢,因为上一步给n复制为当前数组的长度,如果一开始时16的话,这里就是15和key的hash值进行与操作,与操作只有1,1的时候才会时1,否正为0,永远不会超过15也就不会出现数组越界了。
**/
if ((p = tab[i = (n - 1) & hash]) == null)

Synchronized和ReentrantLock的区别

Synchronized 是Java 关键字,ReentrantLock是 juc的中Lock接口的实现

  • ReentrantLock类的tryLock可以提供获取锁等待的时间,可以避免死锁
  • ReentrantLock类可以查看锁的各种信息

二者的锁机制其实也是不一样的。ReentrantLock底层调用的是Unsafe的park方法加锁,synchronized操作的应该是对象头中mark word,这点我不能确定。

内存中的栈(stack)、堆(heap)和方法区(method area)的用法

  • 栈保存方法信息,具体石保存栈帧,栈帧用于存储局部变量、操作数栈、动态链接、方法出口等信息
  • 堆主要保存对象实例,具体分为新生代+养老区,比例为1:2,新生代又分eden,survival区,比例为8:1
  • 方法区在java8这之前实现的是永久区,java8后变为元空间,区别就是永久区依然是jvm内存的一部分,而元空间是机器物理内存的一部分。主要存放常量池,字段和方法数据,构造函数和普通方法的字节码,即类的结构信息

什么跨域?如何解决跨域

  • 当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域
  • 服务器端对于CORS的支持,主要是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。
  • 可以使用nginx反向代理处理跨域

为什么要重写HashCode方法和equals方法

在很多java容器例如hashmap中对与一个对象是否相等,除了比较在内存中地址,还会比较hash值以及equals方法

ThreadLocal

简单说ThreadLocal就是一种以空间换时间的做法,在每个Thread里面维护了一个以开地址法实现的ThreadLocal.ThreadLocalMap,把数据进行隔离,数据不共享,自然就没有线程安全方面的问题了

如果你提交任务时,线程池队列已满,这时会发生什么

如果你的线程池阻塞队列是无界队列LinkedBlockingQuene,任务会一直放在队列中,等待执行,因为他的长度近乎无穷大,但是阿里Java规范中禁止使用,且禁止使用Executors中创建线程池,因为创建出来的都是无界队列,如果是ArrayBlockQuene的话,会查看线程数是否已经达到最大线程数,如果没有则新建线程去执行,如果以达到最大线程数,则使用拒绝策略拒绝,默认的拒绝策略是AbortPolicy丢弃任务抛出异常

事务的隔离级别

  • 读未提交:两个事务同时开启,一个事务在读,另外一个事务在写,例如一个事务把余额改成了100,另一个事务读到100了,前一个事务出错,回滚了。造成脏读。
  • 读已提交:两个事务同时开启,一个事务把在读第一次余额100元,这个时候另一个事务改成101元提交了,这个事务又读了一次,变成101,造成不可重复读
  • 可重复读:一个事务在读一张表的所有数据5条数据,另一个事务新增了一条记录,并且提交了,前一个事务又读了一次,发现上一次读是5条数据,这一次读是6条。造成了幻读
  • 序列化:读和写操作都上锁,保证只有一个线程在读和写,不会有问题,但是效率极低。

写出一个安全的单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//双重检查
public class Singel {
private static Singel instance;
public static Singel getInstance(){
if(instance==null){
synchronized (Singel.class){
if(instance==null)
instance= new Singel();
}
}
return instance;
}
}
//利用静态内部类
public class Singel2 {
private Singel2(){

}

private static class Lazy5Inner{
private static final Singel2 instance = new Singel2();
}
public static Singel2 getInstance(){
return Lazy5Inner.instance;
}
}

写出一个快排

Spring MVC的工作流程

Spring容器管理的对象到底是什么出来的