最详细Java八股文(面试必备,持续更新)

1、Java 创建对象有几种方式?

这题目看似简单,要好好回答起来还是有点小复杂的,我们来看看,到底有哪些方式可以创建对象?

  • 使用 new 关键字,这也是我们平时使用的最多的创建对象的方式,示例:
User user=new User();
  • 使用反射方式创建对象,
    使用 newInstance(),但是得处理两个异常 InstantiationException、IllegalAccessException:
User user=User.class.newInstance();
Object object=(Object)Class.forName("java.lang.Object").newInstance()
  • 使用 clone 方法,前面题目中 clone 是 Object 的方法,所以所有对象都有这个方法。
  • 使用反序列化创建对象,调用 ObjectInputStream 类的 readObject() 方法。
    我们反序列化一个对象,JVM 会给我们创建一个单独的对象。JVM 创建对象并不会调用任何构造函
    数。一个对象实现了 Serializable 接口,就可以把对象写入到文件中,并通过读取文件来创建对
    象。
  • 总结
    创建对象的方式关键字:new、反射、clone 拷贝、反序列化。

2、获取一个类Class对象的方式有哪些?

搞清楚类对象和实例对象,但都是对象。

  • 第一种:通过类对象的 getClass() 方法获取,细心点的都知道,这个 getClass 是 Object 类里面的
    方法。
User user=new User();
//clazz就是一个User的类对象
Class<?> clazz=user.getClass();
  • 第二种:通过类的静态成员表示,每个类都有隐含的静态成员 class。
//clazz就是一个User的类对象
Class<?> clazz=User.class;
  • 第三种:通过 Class 类的静态方法 forName() 方法获取。
Class<?> clazz = Class.forName("com.tian.User");

3、ArrayList 和 LinkedList 的区别有哪些?

  • ArrayList
    优点:ArrayList 是实现了基于动态数组的数据结构,因为地址连续,一旦数据存储好了,查询操作效率会比较高(在内存里是连着放的)。
    缺点:因为地址连续,ArrayList 要移动数据,所以插入和删除操作效率比较低

  • LinkedList
    优点:LinkedList 基于链表的数据结构,地址是任意的,所以在开辟内存空间的时候不需要等一个连续的地址。对于新增和删除操作,LinkedList 比较占优势。LinkedList 适用于要头尾操作或插入指定位置的场景。
    缺点:因为 LinkedList 要移动指针,所以查询操作性能比较低

  • 适用场景分析
    当需要对数据进行对随机访问的时候,选用 ArrayList。
    当需要对数据进行多次增加删除修改时,采用 LinkedList。
    如果容量固定,并且只会添加到尾部,不会引起扩容,优先采用 ArrayList。

当然,绝大数业务的场景下,使用 ArrayList 就够了,但需要注意避免 ArrayList 的扩容,以及非顺序的插入。

篇幅限制下面就只能给大家展示小册部分内容了,这边整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

 需要全套面试笔记的【点击此处即可】免费获取

4、用过 ArrayList 吗?说一下它有什么特点?

只要是搞 Java 的肯定都会回答“用过”。所以,回答题目的后半部分——ArrayList 的特点。
可以从这几个方面去回答:

  • Java 集合框架中的一种存放相同类型的元素数据,是一种变长的集合类,基于定长数组实现,当加入数据达到一定程度后,会实行自动扩容,即扩大数组大小。

底层是使用数组实现,添加元素

  • 如果 add(o),添加到的是数组的尾部,如果要增加的数据量很大,应该使用 ensureCapacity()方法,该方法的作用是预先设置 ArrayList 的大小,这样可以大大提高初始化速度。
  • 如果使用 add(int,o),添加到某个位置,那么可能会挪动大量的数组元素,并且可能会触发扩容机制。

高并发的情况下,线程不安全。多个线程同时操作 ArrayList,会引发不可预知的异常或错误。
ArrayList 实现了 Cloneable 接口,标识着它可以被复制。注意:ArrayList 里面的 clone() 复制其实是浅复制。

5、有数组了为什么还要搞个 ArrayList 呢?

通常我们在使用的时候,如果在不明确要插入多少数据的情况下,普通数组就很尴尬了,因为你不知道需要初始化数组大小为多少,而 ArrayList 可以使用默认的大小,当元素个数到达一定程度后,会自动扩容。
可以这么来理解:我们常说的数组是定死的数组,ArrayList 却是动态数组。

6、说说什么是 fail-fast?

fail-fast 机制是 Java 集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生 fail-fast 事件。
例如:
当某一个线程 A 通过 iterator 去遍历某集合的过程中,若该集合的内容被其他线程所改变了,那么线程 A 访问集合时,就会抛出 ConcurrentModificationException 异常,产生 fail-fast 事件。这里的操作主要是指 add、remove 和 clear,对集合元素个数进行修改。
解决办法:建议使用“java.util.concurrent 包下的类”去取代“java.util 包下的类”。
可以这么理解:在遍历之前,把 modCount 记下来 expectModCount,后面 expectModCount 去和 modCount 进行比较,如果不相等了,证明已并发了,被修改了,于是抛出ConcurrentModificationException 异常。

7、说说Hashtable 与 HashMap 的区别

本来不想这么写标题的,但是无奈,面试官都喜欢这么问 HashMap。

  1. 出生的版本不一样,Hashtable 出生于 Java 发布的第一版本 JDK 1.0,HashMap 出生于 JDK1.2。
  2. 都实现了 Map、Cloneable、Serializable(当前 JDK 版本 1.8)。
  3. HashMap 继承的是 AbstractMap,并且 AbstractMap 也实现了 Map 接口。Hashtable 继承Dictionary。
  4. Hashtable 中大部分 public 修饰普通方法都是 synchronized 字段修饰的,是线程安全的,HashMap 是非线程安全的。
  5. Hashtable 的 key 不能为 null,value 也不能为 null,这个可以从 Hashtable 源码中的 put 方法看到,判断如果 value 为 null 就直接抛出空指针异常,在 put 方法中计算 key 的 hash 值之前并没有判断 key 为 null 的情况,那说明,这时候如果 key 为空,照样会抛出空指针异常。
  6. HashMap 的 key 和 value 都可以为 null。在计算 hash 值的时候,有判断,如果key==null ,则其 hash=0 ;至于 value 是否为 null,根本没有判断过。
  7. Hashtable 直接使用对象的 hash 值。hash 值是 JDK 根据对象的地址或者字符串或者数字算出来的 int 类型的数值。然后再使用除留余数法来获得最终的位置。然而除法运算是非常耗费时间的,效率很低。HashMap 为了提高计算效率,将哈希表的大小固定为了 2 的幂,这样在取模预算时,不需要做除法,只需要做位运算。位运算比除法的效率要高很多。
  8. Hashtable、HashMap 都使用了 Iterator。而由于历史原因,Hashtable 还使用了Enumeration 的方式。
  9. 默认情况下,初始容量不同,Hashtable 的初始长度是 11,之后每次扩充容量变为之前的2n+1(n 为上一次的长度)而 HashMap 的初始长度为 16,之后每次扩充变为原来的两倍。

另外在 Hashtable 源码注释中有这么一句话:

Hashtable is synchronized. If a thread-safe implementation is not needed, it is
recommended to use HashMap in place of Hashtable . If a thread-safe highlyconcurrent implementation is desired, then it is recommended to use
ConcurrentHashMap in place of Hashtable.

大致意思:Hashtable 是线程安全,推荐使用 HashMap 代替 Hashtable;如果需要线程安全高并
发的话,推荐使用 ConcurrentHashMap 代替 Hashtable。

这个回答完了,面试官可能会继续问:HashMap 是线程不安全的,那么在需要线程安全的情况下还要考虑性能,有什么解决方式?
这里最好的选择就是 ConcurrentHashMap 了,但面试官肯定会叫你继续说一下ConcurrentHashMap 数据结构以及底层原理等。

8、HashMap 中的 key 我们可以使用任何类作为 key 吗?

平时可能大家使用的最多的就是使用 String 作为 HashMap 的 key,但是现在我们想使用某个自定义类作为 HashMap 的 key,那就需要注意以下几点

  • 如果类重写了 equals 方法,它也应该重写 hashCode 方法
  • 类的所有实例需要遵循与 equals 和 hashCode 相关的规则。
  • 如果一个类没有使用 equals,你不应该在 hashCode 中使用它。
  • 咱们自定义 key 类的最佳实践是使之为不可变的,这样,hashCode 值可以被缓存起来,拥有
    更好的性能。不可变的类也可以确保 hashCode 和 equals 在未来不会改变,这样就会解决与
    可变相关的问题了。

9、HashMap 的长度为什么是 2 的 N 次方呢?

为了能让 HashMap 存数据和取数据的效率高,尽可能地减少 hash 值的碰撞,也就是说尽量把数据能均匀的分配,每个链表或者红黑树长度尽量相等。
我们首先可能会想到 % 取模的操作来实现。
下面是回答的重点哟:

取余(%)操作中如果除数是 2 的幂次,则等价于与其除数减一的与(&)操作(也就是说
hash % length == hash &(length - 1) 的前提是 length 是 2 的 n 次方)。并且,采用二进
制位操作 & ,相对于 % 能够提高运算效率。

这就是为什么 HashMap 的长度需要 2 的 N 次方了。

 篇幅限制下面就只能给大家展示小册部分内容了,这边整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

 需要全套面试笔记的【点击此处即可】免费获取

10、HashMap 与 ConcurrentHashMap 的异同

  1. 都是 key-value 形式的存储数据;
  2. HashMap 是线程不安全的,ConcurrentHashMap 是 JUC 下的线程安全的;
  3. HashMap 底层数据结构是数组 + 链表(JDK 1.8 之前)。JDK 1.8 之后是数组 + 链表 + 红黑树。当链表中元素个数达到 8 的时候,链表的查询速度不如红黑树快,链表会转为红黑树,红黑树查询速度快;
  4. HashMap 初始数组大小为 16(默认),当出现扩容的时候,以 0.75 * 数组大小的方式进行扩容;
  5. ConcurrentHashMap 在 JDK 1.8 之前是采用分段锁来现实的 Segment + HashEntry,Segment 数组大小默认是 16,2 的 n 次方;JDK 1.8 之后,采用 Node + CAS + Synchronized来保证并发安全进行实现。

11、红黑树有哪几个特征?

紧接上个问题,面试官很有可能会问红黑树,下面把红黑树的几个特征列出来:

在这里插入图片描述

12、说说你平时是怎么处理 Java 异常的

try-catch-finally

  • try 块负责监控可能出现异常的代码
  • catch 块负责捕获可能出现的异常,并进行处理
  • finally 块负责清理各种资源,不管是否出现异常都会执行
  • 其中 try 块是必须的,catch 和 finally 至少存在一个标准异常处理流程
    在这里插入图片描述

抛出异常→捕获异常→捕获成功(当 catch 的异常类型与抛出的异常类型匹配时,捕获成功)
→异常被处理,程序继续运行 抛出异常→捕获异常→捕获失败(当 catch 的异常类型与抛出异
常类型不匹配时,捕获失败)→异常未被处理,程序中断运行

在开发过程中会使用到自定义异常,在通常情况下,程序很少会自己抛出异常,因为异常的类名通常也包含了该异常的有用信息,所以在选择抛出异常的时候,应该选择合适的异常类,从而可以明确地描述该异常情况,所以这时候往往都是自定义异常。

自定义异常通常是通过继承 java.lang.Exception 类,如果想自定义 Runtime 异常的话,可以继承java.lang.RuntimeException 类,实现一个无参构造和一个带字符串参数的有参构造方法。

在业务代码里,可以针对性的使用自定义异常。比如说:该用户不具备某某权限、余额不足等。

13、说说深拷贝和浅拷贝?

浅拷贝(shallowCopy)只是增加了一个指针指向已存在的内存地址,

深拷贝(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存,

使用深拷贝的情况下,释放内存的时候不会因为出现浅拷贝时释放同一个内存的错误。

最好是结合克隆已经原型模式联系在一起哈,记得复习的时候,把这几个联系起来的。

 篇幅限制下面就只能给大家展示小册部分内容了,这边整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

 需要全套面试笔记的【点击此处即可】免费获取

  • 22
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值