Java基础高频面试题(一)

面试官:说一下ArrayList和LinkedList的区别?

关于这个问题你起码得回答到这个程度:

  1. 首先,它们的底层数据结构不同,ArrayList底层是基于数组实现的,LinkedList底层是基于链表实现的;
  2. 由于底层数据结构不同,它们所适用的场景也不同,ArrayList更适合随机查找,LinkedList更适合删除和添加;
  3. 另外ArrayList和LinkedList都实现了List接口,但是LinkedList还额外实现了Deque接口,所以LinkedList还可以当作队列来使用。

但是实际工作中,很少用到LinkedList,反正我工作这么久了从来没用过这个集合类,甚至连写这个类的作者也吐槽:我写的,但我从来没用过。哎,我就是玩儿~

为什么用的少,很简单的一个道理,虽然LinkedList基于链表实现,虽然链表插入元素和删除元素效率比数组高,但是,插入和删除之前还是得先遍历呀!遍历链表就导致它的效率一下降低。而且有人专门做过测试,在数据量比较大的时候,两者执行的效率差不多。

这个题目问的不仅仅是Java集合的知识,也是对数据结构的考查,一定要结合自己的工作经验和面试官谈谈。

面试官:说一下ThreadLocal

  1. ThreadLocal是Java中所提供的线程本地存储机制,可以利用该机制将数据缓存在某个线程内部,该线程可以在任意时刻、任意方法中获取缓存的数据;
  2. ThreadLocal底层是通过ThreadLocalMap来实现的,每个Thread对象 (注意不是ThreadLocal对象) 中都存在一个ThreadLocalMap, Map的key为ThreadLocal对象, Map的value为需要缓存的值;
  3. 如果在线程池中使用ThreadLocal会造成内存泄漏,因为当ThreadLocal对象使用完之后,应该要把设置的key、value,也就是Entry对象进行回收,但线程池中的线程不会回收,而线程对象是通过强引用指向ThreadLocalMap,ThreadLocalMap也是通过强引用指向Entry对象,线程不被回收,Entry对象也就不会被回收,从而出现内存泄漏。解决办法是,在使用了ThreadLocal对象之后,手动调用ThreadLocal的remove方法, 手动清除Entry对象;
  4. ThreadLocal经典的应用场景就是连接管理(一个线程持有一个连接, 该连接对象可以在不同的方法之间进行传递,线程之间不共享同一个连接)。

这个问题看似简单,但是坑还是挺多的,尤其是工作中没用过的话,随便扩展一下能问的你一脸懵逼,回头我会单独做一期讲解ThreadLocal的坑。

面试官:如何查看线程死锁?

  1. 可以通过jstack命令来进行查看,jstack命令中会显示发生了死锁的线程;
  2. 或者两个线程去操作数据库时,数据库发生了死锁,这时可以查看数据库的死锁情况。
# 查询是否锁表
show open tables where In_use > 0;
# 查询进程
show processlist;
# 查看正在锁的事务
select * from information_schema.INNODB_LOCKS;
# 查看等待锁的事务
select * from information_schema.INNODB_LOCK_WAITS;
# 注意后两句在mysql8.x版本没有

面试官:什么是深拷贝和浅拷贝?

深拷贝和浅拷贝就是指对象的拷贝,一般对象中存在两种类型的属性,一种是基本数据类型,一种是引用类型。

  1. 浅拷贝是指,只会拷贝基本数据类型的值,以及实例对象的引用地址,并不会复制引用地址所指向的对象,也就是说浅拷贝出来的对象,内部的类属性指向的是同一个对象;
  2. 深拷贝是指,既会拷贝基本数据类型的值,也会针对实例对象的引用地址所指向的对象进行复制,深拷贝出来的对象,内部的类执行指向的不是同一个对象。

面试官:泛型中extends和super的区别?

  1. 表示包括T在内的任何T类的子类
  2. 表示包括T在内的任何T的父类

面试官:String、StringBuilder和StringBuffer的区别及使用场景?

被问烂的面试题了……

  1. String是final修饰的,不可变,每次操作都会产生新对象;StringBuilder和StringBuffer都是在原对象上操作;
  2. StringBuffer是线程安全的,StringBuilder线程不安全;StringBuffer方法都是synchronized修饰的;

性能:StringBuilder > StringBuffer > String(仅在大量字符串拼接的时候)

场景:经常需要改变字符串内容时使用后面两个,优先使用StringBuilder,多线程使用共享变量时使用StringBuffer。

面试官:接口和抽象类的区别?

这个问题我就有话说了,在很长的一段时间里,我一直不太清楚这两个东西有什么异同,尤其是刚开始学Java的时候,一直不会用抽象类,也不知道什么时候该用抽象类,抽象类的使用场景是什么?我以前就觉得抽象类就很鸡肋,为什么要有这个。但是随着对Java理解的深入,也慢慢能体会到它存在的意义,对于这个问题,对于初级和中高级程序员,我想面试官想听到的答案其实是不一样的。

初级:

  1. 抽象类可以存在普通成员函数(即可以有实现的方法),而接口中只能存在public abstract方法;
  2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;
  3. 抽象类只能继承一个,接口可以实现多个。

中高级:

  1. 接口的设计目的,是对类的行为进行约束,也就是提供一种机制,可以强制要求不同的类具有相同的行为。它只约束了行为的有无,但不对如何实现行为进行限制;

  2. 而抽象类的设计目的,是代码复用。当不同的类具有某些相同的行为,且其中一部分行为的实现方式一致时,可以让这些类都派生于一个抽象类。注意抽象类是先有子类再有父类,对子类的一些共性进行抽象,然后才有了父类抽象类。

    在父类中既可以有抽象方法,也可以有实现了的方法,所以这也是为什么抽象类不允许实例化,因为抽象类中有未实现的方法,必须由子类提供具体的实现。

  3. 抽象类是对类本质的抽象,表达的是is a的关系,比如:BMW is a Car。抽象类包含并实现子类的通用特性,将子类存在差异化的特性进行抽象,交由子类去实现。

    接口是对行为的抽象,表达的是like a的关系,比如:Bird like a AirPlane(鸟像飞机一样可以飞),但其本质是一只鸟。接口的核心是定义行为,即实现类可以做什么,至于实现类主体是谁、是如何实现的,接口并不关心。

使用场景:

当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口。

面试官:Java中int,char,long各占多少字节数?

所谓的占用字节数,就是申请内存的时候所占的空间大小。

基本类型 字节数 最小值&最大值
byte 1字节 最小-2^7
最大2^7-1
char 2字节 最小\u0000(即为0)
最大\uffff(即为65535)
int 4字节 最小-2^31
最大2^31-1
long 8字节 最小-2^63
最大2^63-1

面试官:了解Integer缓存池吗?

Integer缓存池在Java5中就已经引入,范围是固定的-128到127。在Java6后,最大值映射到java.lang.Integer.IntegerCache.high,可以使用JVM的启动参数(-XX:AutoBoxCacheMax=size)设置最大值。

缓存通过一个for循环实现。从小到大的创建尽可能多的整数并存储在一个名为cache的整数数组中。这个缓存会在Integer类第一次被使用的时候被初始化出来。以后,就可以使用缓存中包含的实例对象,而不是创建一个新的实例(在自动装箱的情况下)。

面试官:String s1 = new String(“abc”)创建了几个对象?

这题也是面试的一个坑,有很多背面试题的同学上来就说两个,那就错了。

首先字符串"abc"是一个常量存在于常量池中,我们使用new关键字创建的对象存在于堆中,因此出现了两个对象,所以网上很多文章直接说是两个,这是片面的。

在回答这个问题的时候一定要跟面试官说清楚前提,常量池中是否存在字符串常量"abc",如果存在,则以上语句会创建一个对象,如果不存在,则会创建两个对象。

第一波面试题有没有触碰到你的知识盲点呢,欢迎在下方留下你的评论~