Java面试题
# HashMap如何解决哈希冲突的
哈希冲突,是由于哈希算法输入数据是无限的,而计算后的输出结果的范围是有限的,所以总会不同数据经过计算可能得到相同的哈希值,这个情况下就会出现哈希冲突,
- 开放定址法(线性探测法)
- 从冲突位置开始,按一定顺序查找空闲位置存储冲突元素
- 链式寻址法
- 将冲突的key以单向链表方式存储
- 再哈希法
- 当第一个哈希函数冲突时,使用另一个哈希函数重新计算直到不再产生冲突为止
- 建立公共溢出区
- 哈希表分为基本表和溢出表。冲突元素一律放入溢出表
在JDK1.8版本中,哈希map默认使用单向链表处理冲突,当链表长度>8且哈希表容量>64时,再向链表中添加元素,就会触发链表向红黑树的转化,提高查询效率(从O(n)提升到O(log n))
# fail-safe机制和fail-fast机制分别有什么作用
fail-safe机制和fail-fast机制是多线程并发操作集合时的两种失败处理机制。
在集合遍历过程中,如果检测到数据被修改(如增删元素),底层是通过维护一个
modCount
记录修改的次数实现的,当modCount与预期修改次数expectedModCount不一致时会立即抛出ConcurrentModificationException
异常,使遍历失败。java.util包下的集合类,都是属于快速失败机制的,比如ArrayList、HashMap等,
fail-safe表示安全失败,它旨在即使面对意外情况也能恢复并继续运行,这使得它特别适用于不确定或者不稳定的环境,底层实现就是通过
getArray
获取数组引用然后通过Arrays.copyOf
得到一个数组的快照,而添加操作是基于这个快照完成的,不过进行遍历操作时无法获得实时结果。java.util.concurrent
包下的并发集合,都是属于安全失败的,如ConcurrentHashMap
、CopyOnWriteArrayList
等。
# JDK、JRE、JVM之间的区别
JDK(Java SE Development Kit)是 java 开发工具包,它提供了编译、运行Java程序所需的各种工具和资源,包括Java运行时环境JRE、Java编译器,以及常用的Java类库等
JRE(Java Runtime Environment) ,Java运行环境,用于运行Java的字节码文件,JRE中包括了 JVM以及JVM工作所需要的类库,只需要安装JRE来运行Java程序,而程序开发者必须安装JDK来编译、调试程序。
JVM(Java Virtual Mechinal),Java虚拟机,是一个运行在计算机上的程序,负责运行字节码文件,它是整个java实现跨平台的最核心的部分。
如果我们要开发Java程序,那就需要JDK来编译Java源文件。 如果我们只想运行已经编译好的Java字节码文件,也就是.class文件,那么就只需要JRE。 JDK中包含了JRE,JRE中包含了JVM。 另外,JVM在执行Java字节码时,需要把字节码解释为机器指令,而不同操作系统的机器指令是有可能 不一样的,所以就导致不同操作系统上的JVM是不一样的,所以我们在安装JDK时需要选择操作系统。 另外,JVM是用来执行Java字节码的,所以编译之后是Java字节码,那就都能在JVM上运行,比如Apache Groovy, Scala and Kotlin 等等。
# 为何JDK9要将String底层实现由char[]改成byte[]?
JDK 9并没有将String的底层实现由char [] 改成byte[],而是在 JDK 9中,引入了Compact Strings的优化,优化的目的是减少String对象的内存消耗。
jdk9中字符串由char[]数组来存储该字符串的所有字符,且内部采用utf-16编码,每个字符占用两个字符,但是如果字符串只包含英文字符或者ASCII字符,这些只需要一个字符就可以存储,这意味着字符串实际存储的空间比需要的空间多一倍 。引入了Compact String优化,String的底层实现由char[]中改为byte[],只在存储非ASCII字符时使用char[],对于纯ASCII字符就会存储再byte[]中,从而节省一半的内存空间。这个优化是在编译器和运行时实现,对开发者透明,只需通过虚拟机参数-XX:+UseCompressedstrings
控制是否优化。
# Java反射的优、缺点
优点
增加程序的灵活性,可以在运行的过程中动态对类进行修改和操作
提高代码的复用率,比如动态代理,就是用到了反射来实现
可以在运行时轻松获取任意一个类的方法、属性,并且还能通过反射进行动态调用
缺点
- 反射会涉及到动态类型的解析,所以VM无法对这些代码进行优化,导致性能要比非反射调用更低
- 使用反射以后,代码的可读性会下降
- 反射可以绕过一些限制访问的属性或者方法,可能会导致破坏了代码本身的抽象性
反射可以让我们的代码更加灵活、为各种框架提供开箱即用的功能提供了便利。
不过,反射让我们在运行时有了分析操作类的能力的同时,也增加了安全问题,比如可以无视泛型参数的安全检查(泛型参数的安全检查发生在编译时)。另外,反射的性能也要稍差点,不过,对于框架来说实际是影响不大的
# 为什么重写equals方法就一定要重写hash code方法
因为两个相等的对象的 hashCode
值必须是相等。也就是说如果 equals
方法判断两个对象是相等的,那这两个对象的 hashCode
值也要相等。
如果重写 equals()
时没有重写 hashCode()
方法的话就可能会导致 equals
方法判断是相等的两个对象,hashCode
值却不相等。