0%

Java 常见面试问题

整理下面试常见的,自己可能掌握不牢固的问题。

多线程

现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行?

join方法

在java中wait和sleep方法的不同?

最大的不同是wait会在等待时释放锁,而sleep会持有锁。wait通常会被用于线程之间的交互,sleep通常被用于暂停执行。

volatile能提供什么保证?

volatile变量提供顺序保证和可见性保证,比如JVM或者JIT会为了更好的性能对语句重排顺序,使用volatile修饰的变量即使在没有同步块的情况下赋值也不会与其他语句重拍序。volatile提供happens-before保证,确保一个线程的修改能对其他线程可见。
在某些情况下还能提供原子性,比如long和double类型的数据。

Java中能创建volatile数组吗?

能,Java中能创建volatile数组,但是只是一个指向数组的引用,而不是整个数组,也就是说如果多个线程同时改变数组的元素,volatile标识符就不能起到保护作用。、

volatile标识符能使一个非原子操作变成一个原子操作吗?

一个典型的例子就是一个long类型。Java中读取long类型变量不是原子的,需要分成两步,如果一个线程正在修改该long变量的值,另一个线程可能就只能看到该值的一半(前32位)。但是如果对一个volatile修饰的long或者double变量的读写是原子的。

Java 基础

数据类型

怎么将byte转换为String?

可以使用String接收byte[]参数的构造器来进行转换,需要注意的是使用正确的编码。

1
new String("你好啊".getBytes(), StandardCharsets.UTF_8);

我们能将int强制转换位byte类型的变量吗?如果该值大于byte类型的范围,将会出现什么现象?

是的,可以强制转换,但是Java中int是32位的,而byte是8位的,所以,如果强制转化,int类型的高24位将会被丢弃,byte类型的范围是-127到128。

Java中的++操作符是线程安全的吗?

不是,这个操作实际上涉及三个指令:读取变量值,增加,再存储会内存,这个过程有可能出现多个线程交叉。

3*0.1 == 0.3 将会返回什么?true 还是 false?

false,因为有些浮点数不能精确度的表示出来。
可以用BigDecimal转换后进行精确计算:

1
BigDecimal bigDecimal = new BigDecimal("0.2");
2
System.out.println(bigDecimal.multiply(new BigDecimal("6")).doubleValue());

这里注意要用BigDecimal的String构造器,如果是用double类型,仍然是不精确的。
参考:Java中浮点型数据Float和Double进行精确计算的问题

可以用Switch中使用String吗?

从Java7开始,我们可以在switch case中使用字符串,但这仅仅是一个语法糖。内部实现在switch中使用字符串的hash code。

String和StringBuilder/StringBuffer

String是只读字符串,它所引用的字符串内容是不能被改变的。而StringBuffer和StringBuilder表示的字符串对象是可以修改的。StringBuilder是JDK1.5引入的,区别在于它是在单线程环境下使用的,所有方面都没有被synchronized修饰,因此它的效率也比StringBuffer高。
关于性能问题,有个知乎问题:JAVA 中的 StringBuilder 和 StringBuffer 适用的场景是什么? 评论区提到,现在用+号拼接字符串已经没有了性能损失,除非在一个循环里面重复执行,因为这样会多次创建StringBuilder

有一个面试题问:有没有哪种情况用 + 做字符串连接比调用 StringBuffer / StringBuilder 对象的 append 方法性能更好?如果连接后得到的字符串在静态存储区中是早已存在的,那么用+做字符串连接是优于 StringBuffer / StringBuilder 的 append 方法的。

集合

ArrayList和LinkedList区别是什么

ArrayList底层使用数组,随机访问效率高,插入和删除元素的效率低,需要连续空间;LinkedList底层使用链表,随机访问元素效率低,插入和删除效率高,不需要连续空间。

LinkedHashMap 和 PriorityQueue 的区别是什么?

PriorityQueue保证最高或者最低优先级的元素总在队列头部,但是LinkedHashMap维持的顺序是元素插入的顺序。当遍历一个PriorityQueue时,没有任何顺序保证,但是LinkedHashMap保证按照插入顺序。

Java中怎么打印数组?

用Arrays.toString()和Arrays.deepToString()方法。

TreeMap是用什么实现的?

红黑树。

ArrayList和HashMap的默认大小是多少?

ArrayList的默认大小是是个元素,HashMap的默认大小是16个元素(必须是2的幂)

Iterator和ListIterator的区别是什么?

  • Iterator可以用来遍历Set和List集合,但是ListIterator只能用来遍历List。
  • Iterator对集合只能是前向遍历,但是ListIterator可以前向也可以后向。
  • ListIterator实现了Iterator接口,并且包含其他功能。比如:增加元素,替换元素,获取前一个和后一个元素的索引等等。

快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?

Iterator的安全失败是基于对底层集合做拷贝,因此,它不受源集合上修改的影响。java.util包下面的所有集合都是快速失败的,而java.util.current包下面的所有类都是安全失败的。快速失败的迭代器会抛出CurrentModificationException异常,而安全失败的迭代器永远不会抛出这样的异常。

用什么方法来实现集合的排序?

可以使用有序集合比如TreeSet或者TreeMap,你也可以使用Collections.sort()来排序

JVM

64位JVM中,int的长度是多少位?

Java中,int类型变量的长度是一个固定值,与平台无关,都是32位。

对象分配规则

  • 对象优先分配在Eden区,如果Eden区没有足够的空间时,虚拟机执行一次Minor GC.
  • 大对象直接进入老年代。这么做的目的是避免频繁在Eden区和两个Survivor区之间发生大量的内存拷贝。
  • 长期存活的对象进入老年代。虚拟机为每个对象定义了一个年龄计数器,如果对象经过一次Minor GC那么对象会进入Survivor区,之后每经过一次Minor GC对象的年龄就会增加一,直到达到阈值,对象进入老年代。
  • 动态判断对象的年龄。如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代。
  • 空间分配担保。每次进行Minor GC时,JVM会计算Survivor区移动到老年区的对象的平均大小,如果这个值大于老年区的剩余值则进行一次Full GC,如果小于就检查HandlePromotionFailure设置,设置为true的话只进行Minor GC,如果false则进行Full GC