---
title: Java面试题之Java基础篇,56道Java基础八股文(2.3万字68张手绘图),面渣逆袭必看👍
shortTitle: 面渣逆袭-Java SE
author: 三分恶&沉默王二
date: 2025-09-19
category:
- 面渣逆袭
tag:
- 面渣逆袭
description: 下载次数超 1 万次,2.3 万字 68 张手绘图,详解56道Java基础面试高频题(让天下没有难背的八股),面渣背会这些八股文,这次吊打面试官,我觉得稳了(手动 dog)。
head:
- - meta
- name: keywords
content: Java,Java SE,面试题,Java基础面试题,Java面试题,八股文,java,面试,java面试
---

## 前言
2.3 万字 68 张手绘图,详解 56 道 Java 基础面试高频题(让天下没有难背的八股),面渣背会这些八股文,这次吊打面试官,我觉得稳了(手动 dog)。整理:沉默王二,戳[转载链接](https://mp.weixin.qq.com/s/t7EYyF0VGEg1rAZut9dwSw),原作者:星球嘉宾三分恶,戳[原文链接](https://mp.weixin.qq.com/s/M-6RSRcRd3X93cR7VXpanw)。
亮白版本更适合拿出来打印,这也是很多学生党喜欢的方式,打印出来背诵的效率会更高。

2024 年 12 月 23 日开始着手第二版更新。
- 对于高频题,会标注在《[Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)》中出现的位置,哪家公司,原题是什么,并且会加🌟,目录一目了然;如果你想节省时间的话,可以优先背诵这些题目,尽快做到知彼知己,百战不殆。
- 区分八股精华回答版本和原理底层解释,让大家知其然知其所以然,同时又能做到面试时的高效回答。
- 结合项目([技术派](https://javabetter.cn/zhishixingqiu/paicoding.html)、[pmhub](https://javabetter.cn/zhishixingqiu/pmhub.html))来组织语言,让面试官最大程度感受到你的诚意,而不是机械化的背诵。
- 修复第一版中出现的问题,包括球友们的私信反馈,网站留言区的评论,以及 [GitHub 仓库](https://github.com/itwanger/toBeBetterJavaer/issues)中的 issue,让这份面试指南更加完善。
- 增加[二哥编程星球](https://javabetter.cn/zhishixingqiu/)的球友们拿到的一些 offer,对面渣逆袭的感谢,以及对简历修改的一些认可,以此来激励大家,给大家更多信心。
- 优化排版,增加手绘图,重新组织答案,使其更加口语化,从而更贴近面试官的预期。

由于 PDF 没办法自我更新,所以需要最新版的小伙伴,可以微信搜【**沉默王二**】,或者扫描/长按识别下面的二维码,关注二哥的公众号,回复【**222**】即可拉取最新版本。
":()V
13: aload_1
14: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
17: aload_2
18: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
24: astore_3
25: return
```
也就是说,上面的代码相当于:
```java
String a = "hello ";
String b = "world!";
StringBuilder sb = new StringBuilder();
sb.append(a);
sb.append(b);
String ab = sb.toString();
```
因此,如果笼统地讲,通过加号拼接字符串时会创建多个 String 对象是不准确的。因为加号拼接在编译期还会创建一个 StringBuilder 对象,最终调用 `toString()` 方法的时候再返回一个新的 String 对象。
```java
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
```
那除了使用 `+` 号来拼接字符串,还有 `StringBuilder.append()`、`String.join()` 等方式。
推荐阅读:[如何拼接字符串?](https://javabetter.cn/string/join.html)
#### 如何保证 String 不可变?
第一,String 类内部使用一个私有的字符数组来存储字符串数据。这个字符数组在创建字符串时被初始化,之后不允许被改变。
```java
private final char value[];
```
第二,String 类没有提供任何可以修改其内容的公共方法,像 concat 这些看似修改字符串的操作,实际上都是返回一个新创建的字符串对象,而原始字符串对象保持不变。
```java
public String concat(String str) {
if (str.isEmpty()) {
return this;
}
int len = value.length;
int otherLen = str.length();
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
```
第三,String 类本身被声明为 final,这意味着它不能被继承。这防止了子类可能通过添加修改方法来改变字符串内容的可能性。
```java
public final class String
```
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的小米春招同学 K 一面面试原题:String 是可变的吗,为什么要设计为不可变
> 2. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的美团同学 2 优选物流调度技术 2 面面试原题:String 不可变吗?为什么不可变?有什么好处?怎么保证不可变。
> 3. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的京东面经同学 8 面试原题:字符串拼接
### 37.intern 方法有什么作用?
JDK 源码里已经对这个方法进行了说明:
```java
*
* When the intern method is invoked, if the pool already contains a
* string equal to this {@code String} object as determined by
* the {@link #equals(Object)} method, then the string from the pool is
* returned. Otherwise, this {@code String} object is added to the
* pool and a reference to this {@code String} object is returned.
*
```
意思也很好懂:
- 如果当前字符串内容存在于字符串常量池(即 equals()方法为 true,也就是内容一样),直接返回字符串常量池中的字符串
- 否则,将此 String 对象添加到池中,并返回 String 对象的引用
## Integer
### 38.Integer a= 127,Integer b = 127;Integer c= 128,Integer d = 128;相等吗?
1. 推荐阅读:[IntegerCache](https://javabetter.cn/basic-extra-meal/int-cache.html)
2. 推荐阅读:[深入浅出 Java 拆箱与装箱](https://javabetter.cn/basic-extra-meal/box.html)
a 和 b 相等,c 和 d 不相等。
这个问题涉及到 Java 的自动装箱机制以及`Integer`类的缓存机制。
对于第一对:
```java
Integer a = 127;
Integer b = 127;
```
`a`和`b`是相等的。这是因为 Java 在自动装箱过程中,会使用`Integer.valueOf()`方法来创建`Integer`对象。
`Integer.valueOf()`方法会针对数值在-128 到 127 之间的`Integer`对象使用缓存。因此,`a`和`b`实际上引用了常量池中相同的`Integer`对象。
对于第二对:
```java
Integer c = 128;
Integer d = 128;
```
`c`和`d`不相等。这是因为 128 超出了`Integer`缓存的范围(-128 到 127)。
因此,自动装箱过程会为`c`和`d`创建两个不同的`Integer`对象,它们有不同的引用地址。
可以通过`==`运算符来检查它们是否相等:
```java
System.out.println(a == b); // 输出true
System.out.println(c == d); // 输出false
```
要比较`Integer`对象的数值是否相等,应该使用`equals`方法,而不是`==`运算符:
```java
System.out.println(a.equals(b)); // 输出true
System.out.println(c.equals(d)); // 输出true
```
使用`equals`方法时,`c`和`d`的比较结果为`true`,因为`equals`比较的是对象的数值,而不是引用地址。
#### 什么是 Integer 缓存?
就拿 Integer 的缓存吃来说吧。根据实践发现,大部分的数据操作都集中在值比较小的范围,因此 Integer 搞了个缓存池,默认范围是 -128 到 127。

当我们使用自动装箱来创建这个范围内的 Integer 对象时,Java 会直接从缓存中返回一个已存在的对象,而不是每次都创建一个新的对象。这意味着,对于这个值范围内的所有 Integer 对象,它们实际上是引用相同的对象实例。
Integer 缓存的主要目的是优化性能和内存使用。对于小整数的频繁操作,使用缓存可以显著减少对象创建的数量。
可以在运行的时候添加 `-Djava.lang.Integer.IntegerCache.high=1000` 来调整缓存池的最大值。

引用是 Integer 类型,= 右侧是 int 基本类型时,会进行自动装箱,调用的其实是 `Integer.valueOf()`方法,它会调用 IntegerCache。
```java
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
```
IntegerCache 是一个静态内部类,在静态代码块中会初始化好缓存的值。
```java
private static class IntegerCache {
……
static {
//创建Integer对象存储
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
……
}
}
```
#### new Integer(10) == new Integer(10) 相等吗
在 Java 中,使用`new Integer(10) == new Integer(10)`进行比较时,结果是 false。
这是因为 new 关键字会在堆(Heap)上为每个 Integer 对象分配新的内存空间,所以这里创建了两个不同的 Integer 对象,它们有不同的内存地址。
当使用==运算符比较这两个对象时,实际上比较的是它们的内存地址,而不是它们的值,因此即使两个对象代表相同的数值(10),结果也是 false。
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的小米春招同学 K 一面面试原题:new Integer(10) == new Integer(10) 相等吗 常量池
### 39.String 怎么转成 Integer 的?原理?
PS:这道题印象中在一些面经中出场过几次。
String 转成 Integer,主要有两个方法:
- Integer.parseInt(String s)
- Integer.valueOf(String s)
不管哪一种,最终还是会调用 Integer 类内中的`parseInt(String s, int radix)`方法。
抛去一些边界之类的看看核心代码:
```java
public static int parseInt(String s, int radix)
throws NumberFormatException
{
int result = 0;
//是否是负数
boolean negative = false;
//char字符数组下标和长度
int i = 0, len = s.length();
……
int digit;
//判断字符长度是否大于0,否则抛出异常
if (len > 0) {
……
while (i < len) {
// Accumulating negatively avoids surprises near MAX_VALUE
//返回指定基数中字符表示的数值。(此处是十进制数值)
digit = Character.digit(s.charAt(i++),radix);
//进制位乘以数值
result *= radix;
result -= digit;
}
}
//根据上面得到的是否负数,返回相应的值
return negative ? result : -result;
}
```
去掉枝枝蔓蔓(当然这些枝枝蔓蔓可以去看看,源码 cover 了很多情况),其实剩下的就是一个简单的字符串遍历计算,不过计算方式有点反常规,是用负的值累减。

## Object
### 40.Object 类的常见方法?
在 Java 中,经常提到一个词“万物皆对象”,其中的“万物”指的是 Java 中的所有类,而这些类都是 Object 类的子类。
Object 主要提供了 11 个方法,大致可以分为六类:

#### 对象比较:
①、`public native int hashCode()` :[native 方法](https://javabetter.cn/oo/native-method.html),用于返回对象的哈希码。
```java
public native int hashCode();
```
按照约定,相等的对象必须具有相等的哈希码。如果重写了 equals 方法,就应该重写 hashCode 方法。可以使用 [Objects.hash()](https://javabetter.cn/common-tool/Objects.html#%E8%8E%B7%E5%8F%96%E5%AF%B9%E8%B1%A1%E7%9A%84hashcode) 方法来生成哈希码。
```java
public int hashCode() {
return Objects.hash(name, age);
}
```
②、`public boolean equals(Object obj)`:用于比较 2 个对象的内存地址是否相等。
```java
public boolean equals(Object obj) {
return (this == obj);
}
```
如果比较的是两个对象的值是否相等,就要重写该方法,比如 [String 类](https://javabetter.cn/string/string-source.html)、Integer 类等都重写了该方法。举个例子,假如有一个 Person 类,我们认为只要年龄和名字相同,就是同一个人,那么就可以这样重写 equals 方法:
```java
class Person1 {
private String name;
private int age;
// 省略 gettter 和 setter 方法
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof Person1) {
Person1 p = (Person1) obj;
return this.name.equals(p.getName()) && this.age == p.getAge();
}
return false;
}
}
```
#### 对象拷贝:
`protected native Object clone() throws CloneNotSupportedException`:naitive 方法,返回此对象的一个副本。默认实现只做[浅拷贝](https://javabetter.cn/basic-extra-meal/deep-copy.html),且类必须实现 Cloneable 接口。
Object 本身没有实现 Cloneable 接口,所以在不重写 clone 方法的情况下直接直接调用该方法会发生 CloneNotSupportedException 异常。
#### 对象转字符串:
`public String toString()`:返回对象的字符串表示。默认实现返回类名@哈希码的十六进制表示,但通常会被重写以返回更有意义的信息。
```java
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
```
比如说一个 Person 类,我们可以重写 toString 方法,返回一个有意义的字符串:
```java
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
```
当然了,这项工作也可以直接交给 IDE,比如 IntelliJ IDEA,直接右键选择 Generate,然后选择 toString 方法,就会自动生成一个 toString 方法。
也可以交给 [Lombok](https://javabetter.cn/springboot/lombok.html),使用 @Data 注解,它会自动生成 toString 方法。
数组也是一个对象,所以通常我们打印数组的时候,会看到诸如 `[I@1b6d3586` 这样的字符串,这个就是 int 数组的哈希码。
#### 多线程调度:
每个对象都可以调用 Object 的 wait/notify 方法来实现等待/通知机制。我们来写一个例子:
```java
public class WaitNotifyDemo {
public static void main(String[] args) {
Object lock = new Object();
new Thread(() -> {
synchronized (lock) {
System.out.println("线程1:我要等待");
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1:我被唤醒了");
}
}).start();
new Thread(() -> {
synchronized (lock) {
System.out.println("线程2:我要唤醒");
lock.notify();
System.out.println("线程2:我已经唤醒了");
}
}).start();
}
}
```
解释一下:
- 线程 1 先执行,它调用了 `lock.wait()` 方法,然后进入了等待状态。
- 线程 2 后执行,它调用了 `lock.notify()` 方法,然后线程 1 被唤醒了。
①、`public final void wait() throws InterruptedException`:调用该方法会导致当前线程等待,直到另一个线程调用此对象的`notify()`方法或`notifyAll()`方法。
②、`public final native void notify()`:唤醒在此对象监视器上等待的单个线程。如果有多个线程等待,选择一个线程被唤醒。
③、`public final native void notifyAll()`:唤醒在此对象监视器上等待的所有线程。
④、`public final native void wait(long timeout) throws InterruptedException`:等待 timeout 毫秒,如果在 timeout 毫秒内没有被唤醒,会自动唤醒。
⑥、`public final void wait(long timeout, int nanos) throws InterruptedException`:更加精确了,等待 timeout 毫秒和 nanos 纳秒,如果在 timeout 毫秒和 nanos 纳秒内没有被唤醒,会自动唤醒。
#### 反射:
推荐阅读:[二哥的 Java 进阶之路:掌握 Java 反射](https://javabetter.cn/basic-extra-meal/fanshe.html)
`public final native Class> getClass()`:用于获取对象的类信息,如类名。比如说:
```java
public class GetClassDemo {
public static void main(String[] args) {
Person p = new Person();
Class extends Person> aClass = p.getClass();
System.out.println(aClass.getName());
}
}
```
输出结果:
```
com.itwanger.Person
```
#### 垃圾回收:
`protected void finalize() throws Throwable`:当垃圾回收器决定回收对象占用的内存时调用此方法。用于清理资源,但 Java 不推荐使用,因为它不可预测且容易导致问题,Java 9 开始已被弃用。

> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的用友金融一面原题:Object 有哪些常用的方法?
> 2. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的美团面经同学 3 Java 后端技术一面面试原题:object 有哪些方法 hashcode 和 equals 为什么需要一起重写 不重写会导致哪些问题 什么时候会用到重写 hashcode 的场景
## 异常处理
### 41.🌟Java 中异常处理体系?
推荐阅读:[一文彻底搞懂 Java 异常处理](https://javabetter.cn/exception/gailan.html)
Java 中的异常处理机制用于处理程序运行过程中可能发生的各种异常情况,通常通过 try-catch-finally 语句和 throw 关键字来实现。

`Throwable` 是 Java 语言中所有错误和异常的基类。它有两个主要的子类:Error 和 Exception,这两个类分别代表了 Java 异常处理体系中的两个分支。
Error 类代表那些严重的错误,这类错误通常是程序无法处理的。比如,OutOfMemoryError 表示内存不足,StackOverflowError 表示栈溢出。这些错误通常与 JVM 的运行状态有关,一旦发生,应用程序通常无法恢复。
Exception 类代表程序可以处理的异常。它分为两大类:编译时异常(Checked Exception)和运行时异常(Runtime Exception)。
①、编译时异常(Checked Exception):这类异常在编译时必须被显式处理(捕获或声明抛出)。
如果方法可能抛出某种编译时异常,但没有捕获它(try-catch)或没有在方法声明中用 throws 子句声明它,那么编译将不会通过。例如:IOException、SQLException 等。
②、运行时异常(Runtime Exception):这类异常在运行时抛出,它们都是 RuntimeException 的子类。对于运行时异常,Java 编译器不要求必须处理它们(即不需要捕获也不需要声明抛出)。
运行时异常通常是由程序逻辑错误导致的,如 NullPointerException、IndexOutOfBoundsException 等。
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的京东同学 10 后端实习一面的原题:Java 编译时异常和运行时异常的区别
> 2. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的字节跳动面经同学 1 Java 后端技术一面面试原题:异常有哪些分类?
> 3. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的字节跳动面经同学 1 Java 后端技术一面面试原题:Error 和 Exception 都是谁的子类?
> 4. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的微众银行同学 1 Java 后端一面的原题:对异常体系了解多少?
> 5. [二哥编程星球](https://javabetter.cn/zhishixingqiu/)球友[枕云眠美团 AI 面试原题](https://t.zsxq.com/BaHOh):什么是 java 中的异常处理,checked 异常和 unchecked 异常有什么区别
> 6. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的京东面经同学 8 面试原题:开发中遇到的一些异常,异常类型的父类,继承关系等,写程序中如何处理异常?
> 7. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的拼多多面经同学 4 技术一面面试原题:error与execption异同,抛出error程序是无法运行的吗
### 42.异常的处理方式?

①、遇到异常时可以不处理,直接通过throw 和 throws 抛出异常,交给上层调用者处理。
throws 关键字用于声明可能会抛出的异常,而 throw 关键字用于抛出异常。
```java
public void test() throws Exception {
throw new Exception("抛出异常");
}
```
②、使用 try-catch 捕获异常,处理异常。
```java
try {
//包含可能会出现异常的代码以及声明异常的方法
}catch(Exception e) {
//捕获异常并进行处理
}finally {
//可选,必执行的代码
}
```
#### catch和finally的异常可以同时抛出吗?
如果 catch 块抛出一个异常,而 finally 块中也抛出异常,那么最终抛出的将是 finally 块中的异常。catch 块中的异常会被丢弃,而 finally 块中的异常会覆盖并向上传递。
```java
public class Example {
public static void main(String[] args) {
try {
throw new Exception("Exception in try");
} catch (Exception e) {
throw new RuntimeException("Exception in catch");
} finally {
throw new IllegalArgumentException("Exception in finally");
}
}
}
```
- try 块首先抛出一个 Exception。
- 控制流进入 catch 块,catch 块中又抛出了一个 RuntimeException。
- 但是在 finally 块中,抛出了一个 IllegalArgumentException,最终程序抛出的异常是 finally 块中的 IllegalArgumentException。
虽然 catch 和 finally 中的异常不能同时抛出,但可以手动捕获 finally 块中的异常,并将 catch 块中的异常保留下来,避免被覆盖。常见的做法是使用一个变量临时存储 catch 中的异常,然后在 finally 中处理该异常:
```java
public class Example {
public static void main(String[] args) {
Exception catchException = null;
try {
throw new Exception("Exception in try");
} catch (Exception e) {
catchException = e;
throw new RuntimeException("Exception in catch");
} finally {
try {
throw new IllegalArgumentException("Exception in finally");
} catch (IllegalArgumentException e) {
if (catchException != null) {
System.out.println("Catch exception: " + catchException.getMessage());
}
System.out.println("Finally exception: " + e.getMessage());
}
}
}
}
```

> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的京东面经同学 8 面试原题:写程序中如何处理异常?
> 2. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的拼多多面经同学 4 技术一面面试原题:try-catch-finally抛出异常,catch和finally的异常可以同时抛出吗?
### 43.三道经典异常处理代码题
#### 题目 1
```java
public class TryDemo {
public static void main(String[] args) {
System.out.println(test());
}
public static int test() {
try {
return 1;
} catch (Exception e) {
return 2;
} finally {
System.out.print("3");
}
}
}
```
在`test()`方法中,首先有一个`try`块,接着是一个`catch`块(用于捕获异常),最后是一个`finally`块(无论是否捕获到异常,`finally`块总会执行)。
①、`try`块中包含一条`return 1;`语句。正常情况下,如果`try`块中的代码能够顺利执行,那么方法将返回数字`1`。在这个例子中,`try`块中没有任何可能抛出异常的操作,因此它会正常执行完毕,并准备返回`1`。
②、由于`try`块中没有异常发生,所以`catch`块中的代码不会执行。
③、无论前面的代码是否发生异常,`finally`块总是会执行。在这个例子中,`finally`块包含一条`System.out.print("3");`语句,意味着在方法结束前,会在控制台打印出`3`。
当执行`main`方法时,控制台的输出将会是:
```
31
```
这是因为`finally`块确保了它包含的`System.out.print("3");`会执行并打印`3`,随后`test()`方法返回`try`块中的值`1`,最终结果就是`31`。
#### 题目 2
```java
public class TryDemo {
public static void main(String[] args) {
System.out.println(test1());
}
public static int test1() {
try {
return 2;
} finally {
return 3;
}
}
}
```
执行结果:3。
try 返回前先执行 finally,结果 finally 里不按套路出牌,直接 return 了,自然也就走不到 try 里面的 return 了。
注意:finally 里面使用 return 仅存在于面试题中,实际开发这么写要挨吊的(😂)。
#### 题目 3
```java
public class TryDemo {
public static void main(String[] args) {
System.out.println(test1());
}
public static int test1() {
int i = 0;
try {
i = 2;
return i;
} finally {
i = 3;
}
}
}
```
执行结果:2。
大家可能会以为结果应该是 3,因为在 return 前会执行 finally,而 i 在 finally 中被修改为 3 了,那最终返回 i 不是应该为 3 吗?
但其实,在执行 finally 之前,JVM 会先将 i 的结果暂存起来,然后 finally 执行完毕后,会返回之前暂存的结果,而不是返回 i,所以即使 i 已经被修改为 3,最终返回的还是之前暂存起来的结果 2。
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的京东同学 10 后端实习一面的原题:return 先执行还是 finally 先执行
## I/O
### 44.Java 中 IO 流分为几种?
Java IO 流的划分可以根据多个维度进行,包括数据流的方向(输入或输出)、处理的数据单位(字节或字符)、流的功能以及流是否支持随机访问等。
#### 按照数据流方向如何划分?
- 输入流(Input Stream):从源(如文件、网络等)读取数据到程序。
- 输出流(Output Stream):将数据从程序写出到目的地(如文件、网络、控制台等)。
#### 按处理数据单位如何划分?
- 字节流(Byte Streams):以字节为单位读写数据,主要用于处理二进制数据,如音频、图像文件等。
- 字符流(Character Streams):以字符为单位读写数据,主要用于处理文本数据。

#### 按功能如何划分?
- 节点流(Node Streams):直接与数据源或目的地相连,如 FileInputStream、FileOutputStream。
- 处理流(Processing Streams):对一个已存在的流进行包装,如缓冲流 BufferedInputStream、BufferedOutputStream。
- 管道流(Piped Streams):用于线程之间的数据传输,如 PipedInputStream、PipedOutputStream。
#### IO 流用到了什么设计模式?
用到了——**装饰器模式**,装饰器模式的核心思想是在不改变原有对象结构的前提下,动态地给对象添加新的功能。

具体到 Java IO 中,InputStream 和 OutputStream 这些抽象类定义了基本的读写操作,然后通过各种装饰器类来增强功能。比如 BufferedInputStream 给基础的输入流增加了缓冲功能,DataInputStream 增加了读取基本数据类型的能力,它们都是对基础流的装饰和增强。
```java
InputStream input = new BufferedInputStream(
new DataInputStream(
new FileInputStream("data.txt")
)
);
```
这里 FileInputStream 提供基本的文件读取能力,DataInputStream 装饰它增加了数据类型转换功能,BufferedInputStream 再装饰它增加了缓冲功能。每一层装饰都在原有功能基础上增加新特性,而且可以灵活组合。
我对装饰器模式的理解是它很好地体现了“组合优于继承”的设计原则。优势在于运行时动态组合功能,而且遵循开闭原则,可以在不修改现有代码的情况下增加新功能。
memo:2025 年 9 月 19 日修改至此,今天[有球友在 VIP 群里](https://javabetter.cn/zhishixingqiu/)报喜说上岸美团了,恭喜他!9 月份正是一个收获的季节,大家都要加油哦。

#### Java 缓冲区溢出,如何预防
Java 缓冲区溢出主要是由于向缓冲区写入的数据超过其能够存储的数据量。可以采用这些措施来避免:
①、**合理设置缓冲区大小**:在创建缓冲区时,应根据实际需求合理设置缓冲区的大小,避免创建过大或过小的缓冲区。
②、**控制写入数据量**:在向缓冲区写入数据时,应该控制写入的数据量,确保不会超过缓冲区的容量。Java 的 ByteBuffer 类提供了`remaining()`方法,可以获取缓冲区中剩余的可写入数据量。
```java
import java.nio.ByteBuffer;
public class ByteBufferExample {
public static void main(String[] args) {
// 模拟接收到的数据
byte[] receivedData = {1, 2, 3, 4, 5};
int bufferSize = 1024; // 设置一个合理的缓冲区大小
// 创建ByteBuffer
ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
// 写入数据之前检查容量是否足够
if (buffer.remaining() >= receivedData.length) {
buffer.put(receivedData);
} else {
System.out.println("Not enough space in buffer to write data.");
}
// 准备读取数据:将limit设置为当前位置,position设回0
buffer.flip();
// 读取数据
while (buffer.hasRemaining()) {
byte data = buffer.get();
System.out.println("Read data: " + data);
}
// 清空缓冲区以便再次使用
buffer.clear();
}
}
```
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的美团面经同学 2 Java 后端技术一面面试原题:Java IO 流 如何划分?
> 2. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的华为面经同学 9 Java 通用软件开发一面面试原题:Java 缓冲区溢出,如何预防
### 45.既然有了字节流,为什么还要有字符流?
其实字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还比较耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。
所以, I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。如果音频文件、图片等媒体文件用字节流比较好,如果涉及到字符的话使用字符流比较好。
#### 文本存储是字节流还是字符流,视频文件呢?
在计算机中,文本和视频都是按照字节存储的,只是如果是文本文件的话,我们可以通过字符流的形式去读取,这样更方面的我们进行直接处理。
比如说我们需要在一个大文本文件中查找某个字符串,可以直接通过字符流来读取判断。
处理视频文件时,通常使用字节流(如 Java 中的`FileInputStream`、`FileOutputStream`)来读取或写入数据,并且会尽量使用缓冲流(如`BufferedInputStream`、`BufferedOutputStream`)来提高读写效率。
在[技术派实战项目](https://javabetter.cn/zhishixingqiu/paicoding.html)项目中,对于文本,比如说文章和教程内容,是直接存储在数据库中的,而对于视频和图片等大文件,是存储在 OSS 中的。
因此,无论是文本文件还是视频文件,它们在物理存储层面都是以字节流的形式存在。区别在于,我们如何通过 Java 代码来解释和处理这些字节流:作为编码后的字符还是作为二进制数据。
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的国企面试原题:文本存储是字节流还是字符流,视频文件呢?
### 46.🌟BIO、NIO、AIO 之间的区别?
推荐阅读:[Java NIO 比传统 IO 强在哪里?](https://javabetter.cn/nio/nio-better-io.html)
Java 常见的 IO 模型有三种:BIO、NIO 和 AIO。

BIO:采用阻塞式 I/O 模型,线程在执行 I/O 操作时被阻塞,无法处理其他任务,适用于连接数较少的场景。
NIO:采用非阻塞 I/O 模型,线程在等待 I/O 时可执行其他任务,通过 Selector 监控多个 Channel 上的事件,适用于连接数多但连接时间短的场景。
AIO:使用异步 I/O 模型,线程发起 I/O 请求后立即返回,当 I/O 操作完成时通过回调函数通知线程,适用于连接数多且连接时间长的场景。
#### 简单说一下 BIO?
BIO,也就是传统的 IO,基于字节流或字符流(如 FileInputStream、BufferedReader 等)进行文件读写,基于 Socket 和 ServerSocket 进行网络通信。
对于每个连接,都需要创建一个独立的线程来处理读写操作。

#### 简单说下 NIO?
NIO,JDK 1.4 时引入,放在 java.nio 包下,提供了 Channel、Buffer、Selector 等新的抽象,基于 RandomAccessFile、FileChannel、ByteBuffer 进行文件读写,基于 SocketChannel 和 ServerSocketChannel 进行网络通信。
实际上,“旧”的 I/O 包已经使用 NIO 重新实现过,所以在进行文件读写时,NIO 并无法体现出比 BIO 更可靠的性能。
NIO 的魅力主要体现在网络编程中,服务器可以用一个线程处理多个客户端连接,通过 Selector 监听多个 Channel 来实现多路复用,极大地提高了网络编程的性能。

缓冲区 Buffer 也能极大提升一次 IO 操作的效率。

#### 简单说下 AIO?
AIO 是 Java 7 引入的,放在 java.nio.channels 包下,提供了 AsynchronousFileChannel、AsynchronousSocketChannel 等异步 Channel。
它引入了异步通道的概念,使得 I/O 操作可以异步进行。这意味着线程发起一个读写操作后不必等待其完成,可以立即进行其他任务,并且当读写操作真正完成时,线程会被异步地通知。
```java
AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(Paths.get("test.txt"), StandardOpenOption.READ);
ByteBuffer buffer = ByteBuffer.allocate(1024);
Future result = fileChannel.read(buffer, 0);
while (!result.isDone()) {
// do something
}
```
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的比亚迪面经同学 3 Java 技术一面面试原题:BIO NIO 的区别
> 2. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的美团面经同学 2 Java 后端技术一面面试原题:BIO、NIO、AIO 的区别?
> 3. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的 360 面经同学 3 Java 后端技术一面面试原题:说一下阻塞非阻塞 IO -说了下 BIO 和 NIO
> 4. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的阿里云面经同学 22 面经:介绍NIO BIO AIO
## 序列化
### 47.什么是序列化?什么是反序列化?
序列化(Serialization)是指将对象转换为字节流的过程,以便能够将该对象保存到文件、数据库,或者进行网络传输。
反序列化(Deserialization)就是将字节流转换回对象的过程,以便构建原始对象。

#### Serializable 接口有什么用?
`Serializable`接口用于标记一个类可以被序列化。
```java
public class Person implements Serializable {
private String name;
private int age;
// 省略 getter 和 setter 方法
}
```
#### serialVersionUID 有什么用?
serialVersionUID 是 Java 序列化机制中用于标识类版本的唯一标识符。它的作用是确保在序列化和反序列化过程中,类的版本是兼容的。
```java
import java.io.Serializable;
public class MyClass implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
// getters and setters
}
```
serialVersionUID 被设置为 1L 是一种比较省事的做法,也可以使用 Intellij IDEA 进行自动生成。
但只要 serialVersionUID 在序列化和反序列化过程中保持一致,就不会出现问题。
如果不显式声明 serialVersionUID,Java 运行时会根据类的详细信息自动生成一个 serialVersionUID。那么当类的结构发生变化时,自动生成的 serialVersionUID 就会发生变化,导致反序列化失败。
#### Java 序列化不包含静态变量吗?
是的,序列化机制只会保存对象的状态,而静态变量属于类的状态,不属于对象的状态。
#### 如果有些变量不想序列化,怎么办?
可以使用`transient`关键字修饰不想序列化的变量。
```java
public class Person implements Serializable {
private String name;
private transient int age;
// 省略 getter 和 setter 方法
}
```
#### 能解释一下序列化的过程和作用吗?
序列化过程通常涉及到以下几个步骤:
第一步,实现 Serializable 接口。
```java
public class Person implements Serializable {
private String name;
private int age;
// 省略构造方法、getters和setters
}
```
第二步,使用 ObjectOutputStream 来将对象写入到输出流中。
```java
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.ser"));
```
第三步,调用 ObjectOutputStream 的 writeObject 方法,将对象序列化并写入到输出流中。
```java
Person person = new Person("沉默王二", 18);
out.writeObject(person);
```
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的京东面经同学 2 后端面试原题:用过序列化和反序列化吗?
> 2. [二哥编程星球](https://javabetter.cn/zhishixingqiu/)球友[枕云眠美团 AI 面试原题](https://t.zsxq.com/BaHOh):你了解 java 的序列化和反序列化吗,能解释一下序列化的过程和作用吗
> 3. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的vivo 面经同学 10 技术一面面试原题:什么是序列化,什么是反序列化
### 48.说说有几种序列化方式?
Java 序列化方式有很多,常见的有三种:

- Java 对象序列化 :Java 原生序列化方法即通过 Java 原生流(InputStream 和 OutputStream 之间的转化)的方式进行转化,一般是对象输出流 `ObjectOutputStream`和对象输入流`ObjectInputStream`。
- Json 序列化:这个可能是我们最常用的序列化方式,Json 序列化的选择很多,一般会使用 jackson 包,通过 ObjectMapper 类来进行一些操作,比如将对象转化为 byte 数组或者将 json 串转化为对象。
- ProtoBuff 序列化:ProtocolBuffer 是一种轻便高效的结构化数据存储格式,ProtoBuff 序列化对象可以很大程度上将其压缩,可以大大减少数据传输大小,提高系统性能。
## 网络编程
### 49.了解过Socket网络套接字吗?(补充)
>2024 年 11 月 28 日 增补
推荐阅读:[Java Socket:飞鸽传书的网络套接字](https://javabetter.cn/socket/socket.html)
Socket 是网络通信的基础,表示两台设备之间通信的一个端点。Socket 通常用于建立 TCP 或 UDP 连接,实现进程间的网络通信。

一个简单的 TCP 客户端:
```java
class TcpClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 8080); // 连接服务器
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
out.println("Hello, Server!"); // 发送消息
System.out.println("Server response: " + in.readLine()); // 接收服务器响应
socket.close();
}
}
```
TCP 服务端:
```java
class TcpServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8080); // 创建服务器端Socket
System.out.println("Server started, waiting for connection...");
Socket socket = serverSocket.accept(); // 等待客户端连接
System.out.println("Client connected: " + socket.getInetAddress());
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
String message;
while ((message = in.readLine()) != null) {
System.out.println("Received: " + message);
out.println("Echo: " + message); // 回送消息
}
socket.close();
serverSocket.close();
}
}
```
#### RPC框架了解吗?
RPC是一种协议,允许程序调用位于远程服务器上的方法,就像调用本地方法一样。RPC 通常基于 Socket 通信实现。
>RPC,Remote Procedure Call,远程过程调用
RPC 框架支持高效的序列化(如 Protocol Buffers)和通信协议(如 HTTP/2),屏蔽了底层网络通信的细节,开发者只需关注业务逻辑即可。

常见的 RPC 框架包括:
1. gRPC:基于 HTTP/2 和 Protocol Buffers。
2. Dubbo:阿里开源的分布式 RPC 框架,适合微服务场景。
3. Spring Cloud OpenFeign:基于 REST 的轻量级 RPC 框架。
4. Thrift:Apache 的跨语言 RPC 框架,支持多语言代码生成。
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的理想汽车面经同学 2 一面面试原题:线程内有哪些通信方式?线程之间有哪些通信方式?
## 泛型
### 50.Java 泛型了解么?
推荐阅读:[手写Java泛型,彻底掌握它](https://javabetter.cn/collection/generic.html)
泛型主要用于提高代码的类型安全,它允许在定义类、接口和方法时使用类型参数,这样可以在编译时检查类型一致性,避免不必要的类型转换和类型错误。
没有泛型的时候,像 List 这样的集合类存储的是 Object 类型,导致从集合中读取数据时,必须进行强制类型转换,否则会引发 ClassCastException。
```java
List list = new ArrayList();
list.add("hello");
String str = (String) list.get(0); // 必须强制类型转换
```
泛型一般有三种使用方式:**泛型类**、**泛型接口**、**泛型方法**。

**1.泛型类**:
```java
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic{
private T key;
public Generic(T key) {
this.key = key;
}
public T getKey(){
return key;
}
}
```
如何实例化泛型类:
```java
Generic genericInteger = new Generic(123456);
```
**2.泛型接口** :
```java
public interface Generator {
public T method();
}
```
实现泛型接口,指定类型:
```java
class GeneratorImpl implements Generator{
@Override
public String method() {
return "hello";
}
}
```
**3.泛型方法** :
```java
public static < E > void printArray( E[] inputArray )
{
for ( E element : inputArray ){
System.out.printf( "%s ", element );
}
System.out.println();
}
```
使用:
```java
// 创建不同类型数组: Integer, Double 和 Character
Integer[] intArray = { 1, 2, 3 };
String[] stringArray = { "Hello", "World" };
printArray( intArray );
printArray( stringArray );
```
#### 泛型常用的通配符有哪些?
**常用的通配符为: T,E,K,V,?**
- ? 表示不确定的 java 类型
- T (type) 表示具体的一个 java 类型
- K V (key value) 分别代表 java 键值中的 Key Value
- E (element) 代表 Element
#### 什么是泛型擦除?
所谓的泛型擦除,官方名叫“类型擦除”。
Java 的泛型是伪泛型,这是因为 Java 在编译期间,所有的类型信息都会被擦掉。
也就是说,在运行的时候是没有泛型的。
例如这段代码,往一群猫里放条狗:
```java
LinkedList cats = new LinkedList();
LinkedList list = cats; // 注意我在这里把范型去掉了,但是list和cats是同一个链表!
list.add(new Dog()); // 完全没问题!
```
因为 Java 的范型只存在于源码里,编译的时候给你静态地检查一下范型类型是否正确,而到了运行时就不检查了。上面这段代码在 JRE(Java**运行**环境)看来和下面这段没区别:
```java
LinkedList cats = new LinkedList(); // 注意:没有范型!
LinkedList list = cats;
list.add(new Dog());
```
#### 为什么要类型擦除呢?
主要是为了向下兼容,因为 JDK5 之前是没有泛型的,为了让 JVM 保持向下兼容,就出了类型擦除这个策略。
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的 OPPO 面经同学 1 面试原题:泛型的作用是什么?
## 注解
### 51.说一下你对注解的理解?
**Java 注解本质上是一个标记**,可以理解成生活中的一个人的一些小装扮,比如戴什么什么帽子,戴什么眼镜。

注解可以标记在类上、方法上、属性上等,标记自身也可以设置一些值,比如帽子颜色是绿色。
有了标记之后,我们就可以在编译或者运行阶段去识别这些标记,然后搞一些事情,这就是注解的用处。
例如我们常见的 AOP,使用注解作为切点就是运行期注解的应用;比如 lombok,就是注解在编译期的运行。
注解生命周期有三大类,分别是:
- RetentionPolicy.SOURCE:给编译器用的,不会写入 class 文件
- RetentionPolicy.CLASS:会写入 class 文件,在类加载阶段丢弃,也就是运行的时候就没这个信息了
- RetentionPolicy.RUNTIME:会写入 class 文件,永久保存,可以通过反射获取注解信息
所以我上文写的是解析的时候,没写具体是解析啥,因为不同的生命周期的解析动作是不同的。
像常见的:

就是给编译器用的,编译器编译的时候检查没问题就 over 了,class 文件里面不会有 Override 这个标记。
再比如 Spring 常见的 Autowired ,就是 RUNTIME 的,所以**在运行的时候可以通过反射得到注解的信息**,还能拿到标记的值 required 。

## 反射
### 52.🌟什么是反射?应用?原理?
反射允许 Java 在运行时检查和操作类的方法和字段。通过反射,可以动态地获取类的字段、方法、构造方法等信息,并在运行时调用方法或访问字段。
比如创建一个对象是通过 new 关键字来实现的:
```java
Person person = new Person();
```
Person 类的信息在编译时就确定了,那假如在编译期无法确定类的信息,但又想在运行时获取类的信息、创建类的实例、调用类的方法,这时候就要用到反射。
反射功能主要通过 `java.lang.Class` 类及 `java.lang.reflect` 包中的类如 Method, Field, Constructor 等来实现。

比如说我们可以动态加载类并创建对象:
```java
String className = "java.util.Date";
Class> cls = Class.forName(className);
Object obj = cls.newInstance();
System.out.println(obj.getClass().getName());
```
比如说我们可以这样来访问字段和方法:
```java
// 加载并实例化类
Class> cls = Class.forName("java.util.Date");
Object obj = cls.newInstance();
// 获取并调用方法
Method method = cls.getMethod("getTime");
Object result = method.invoke(obj);
System.out.println("Time: " + result);
// 访问字段
Field field = cls.getDeclaredField("fastTime");
field.setAccessible(true); // 对于私有字段需要这样做
System.out.println("fastTime: " + field.getLong(obj));
```
#### 反射有哪些应用场景?
①、Spring 框架就大量使用了反射来动态加载和管理 Bean。
```java
Class> clazz = Class.forName("com.example.MyClass");
Object instance = clazz.newInstance();
```
②、Java 的动态代理机制就使用了反射来创建代理类。代理类可以在运行时动态处理方法调用,这在实现 AOP 和拦截器时非常有用。
```java
InvocationHandler handler = new MyInvocationHandler();
MyInterface proxyInstance = (MyInterface) Proxy.newProxyInstance(
MyInterface.class.getClassLoader(),
new Class>[] { MyInterface.class },
handler
);
```
③、JUnit 和 TestNG 等测试框架使用反射机制来发现和执行测试方法。反射允许框架扫描类,查找带有特定注解(如 `@Test`)的方法,并在运行时调用它们。
```java
Method testMethod = testClass.getMethod("testSomething");
testMethod.invoke(testInstance);
```
④、最常见的是写通用的工具类,比如对象拷贝工具。比如说 BeanUtils、MapStruct 等等,能够自动拷贝两个对象之间的同名属性,就是通过反射来实现的。

#### 反射的原理是什么?
每个类在加载到 JVM 后,都会在方法区生成一个对应的 Class 对象,这个对象包含了类的所有元信息,比如字段、方法、构造器、注解等。
通过这个 Class 对象,我们就能在运行时动态地创建对象、调用方法、访问字段。
### 反射的优缺点是什么?
反射的优点还是很明显的。首先是能够在运行时动态操作类和对象。其次是能够编写通用的代码,一套代码可以处理不同类型的对象。还有就是能够突破访问限制,访问 private 字段和方法,这在反编译场景下很有用。
但反射的缺点也不少。最明显的是性能问题,反射操作比直接调用要慢很多,因为需要在运行时解析类信息、进行类型检查、权限验证等。
其次是反射能够绕过访问控制,访问和修改 private 成员,这会破坏类的封装。
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的美团面经同学 2 Java 后端技术一面面试原题:Java 反射用过吗?
> 2. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的美团面经同学 18 成都到家面试原题:反射及其应用场景
> 3. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的小米面经同学 F 面试原题:反射的介绍与使用场景
> 4. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的美团面经同学 3 Java 后端技术一面面试原题:java 的反射机制,反射的应用场景 AOP 的实现原理是什么,与动态代理和反射有什么区别
> 5. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的比亚迪面经同学 12 Java 技术面试原题:java的反射
memo:2025 年 9 月 27 日修改至此,今天[在帮球友修改简历](https://javabetter.cn/zhishixingqiu/)的时候,碰到这样一个反馈,很感动:两个月前在特别焦虑迷茫时找过一次二哥,你说了别担心先去做先去投之类的话那次沟通后让我清醒了很多,我开始停止设想坏的结果,照着简历学习八股,投简历,最后收到了面试通知,运气也很好背的八股也派上用场,虽然没有一下子变得特别厉害,但是实打实地感觉自己比以前成熟稳重点了。学习过程中发现自己蛮喜欢软开的,也能获得成就感,所以特别感谢二哥一直以来对大家的帮助。

## JDK1.8 新特性
JDK 已经出到 17 了,但是你迭代你的版本,我用我的 8。JDK1.8 的一些新特性,当然现在也不新了,其实在工作中已经很常用了。
### 53.JDK 1.8 都有哪些新特性?
JDK 1.8 新增了不少新的特性,如 Lambda 表达式、接口默认方法、Stream API、日期时间 API、Optional 类等。

①、Java 8 允许在接口中添加默认方法和静态方法。
```java
public interface MyInterface {
default void myDefaultMethod() {
System.out.println("My default method");
}
static void myStaticMethod() {
System.out.println("My static method");
}
}
```
②、Lambda 表达式描述了一个代码块(或者叫匿名方法),可以将其作为参数传递给构造方法或者普通方法以便后续执行。
```java
public class LamadaTest {
public static void main(String[] args) {
new Thread(() -> System.out.println("沉默王二")).start();
}
}
```
《Effective Java》的作者 Josh Bloch 建议使用 Lambda 表达式时,最好不要超过 3 行。否则代码可读性会变得很差。
③、Stream 是对 Java 集合框架的增强,它提供了一种高效且易于使用的数据处理方式。
```java
List list = new ArrayList<>();
list.add("中国加油");
list.add("世界加油");
list.add("世界加油");
long count = list.stream().distinct().count();
System.out.println(count);
```
④、Java 8 引入了一个全新的日期和时间 API,位于`java.time`包中。这个新的 API 纠正了旧版`java.util.Date`类中的许多缺陷。
```java
LocalDate today = LocalDate.now();
System.out.println("Today's Local date : " + today);
LocalTime time = LocalTime.now();
System.out.println("Local time : " + time);
LocalDateTime now = LocalDateTime.now();
System.out.println("Current DateTime : " + now);
```
⑤、引入 Optional 是为了减少空指针异常。
```java
Optional optional = Optional.of("沉默王二");
optional.isPresent(); // true
optional.get(); // "沉默王二"
optional.orElse("沉默王三"); // "bam"
optional.ifPresent((s) -> System.out.println(s.charAt(0))); // "沉"
```
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的招商银行面经同学 6 招银网络科技面试原题:JDK1.8 的特性?
> 2. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的联想面经同学 7 面试原题:Java 印象比较深的版本更新。
### 54.Lambda 表达式了解多少?
Lambda 表达式主要用于提供一种简洁的方式来表示匿名方法,使 Java 具备了函数式编程的特性。
比如说我们可以使用 Lambda 表达式来简化线程的创建:
```java
new Thread(() -> System.out.println("Hello World")).start();
```
这比以前的匿名内部类要简洁很多。
所谓的函数式编程,就是把函数作为参数传递给方法,或者作为方法的结果返回。比如说我们可以配合 Stream 流进行数据过滤:
```java
List numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
```
其中 `n -> n % 2 == 0` 就是一个 Lambda 表达式。表示传入一个参数 n,返回 `n % 2 == 0` 的结果。
#### Java8 有哪些内置函数式接口?
JDK 1.8 API 包含了很多内置的函数式接口。其中就包括我们在老版本中经常见到的 **Comparator** 和 **Runnable**,Java 8 为他们都添加了 @FunctionalInterface 注解,以用来支持 Lambda 表达式。
除了这两个之外,还有 Callable、Predicate、Function、Supplier、Consumer 等等。
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的招商银行面经同学 6 招银网络科技面试原题:Lamada 表达式的作用?
### 55.Optional 了解吗?
`Optional`是用于防范`NullPointerException`。
可以将 `Optional` 看做是包装对象(可能是 `null`, 也有可能非 `null`)的容器。当我们定义了 一个方法,这个方法返回的对象可能是空,也有可能非空的时候,我们就可以考虑用 `Optional` 来包装它,这也是在 Java 8 被推荐使用的做法。
```java
Optional optional = Optional.of("bam");
optional.isPresent(); // true
optional.get(); // "bam"
optional.orElse("fallback"); // "bam"
optional.ifPresent((s) -> System.out.println(s.charAt(0))); // "b"
```
### 56.Stream 流用过吗?
`Stream` 流,简单来说,使用 `java.util.Stream` 对一个包含一个或多个元素的集合做各种操作。这些操作可能是 _中间操作_ 亦或是 _终端操作_。 终端操作会返回一个结果,而中间操作会返回一个 `Stream` 流。
Stream 流一般用于集合,我们对一个集合做几个常见操作:
```java
List stringCollection = new ArrayList<>();
stringCollection.add("ddd2");
stringCollection.add("aaa2");
stringCollection.add("bbb1");
stringCollection.add("aaa1");
stringCollection.add("bbb3");
stringCollection.add("ccc");
stringCollection.add("bbb2");
stringCollection.add("ddd1");
```
- **Filter 过滤**
```java
stringCollection
.stream()
.filter((s) -> s.startsWith("a"))
.forEach(System.out::println);
// "aaa2", "aaa1"
```
- **Sorted 排序**
```java
stringCollection
.stream()
.sorted()
.filter((s) -> s.startsWith("a"))
.forEach(System.out::println);
// "aaa1", "aaa2"
```
- **Map 转换**
```java
stringCollection
.stream()
.map(String::toUpperCase)
.sorted((a, b) -> b.compareTo(a))
.forEach(System.out::println);
// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"
```
- **Match 匹配**
```java
// 验证 list 中 string 是否有以 a 开头的, 匹配到第一个,即返回 true
boolean anyStartsWithA =
stringCollection
.stream()
.anyMatch((s) -> s.startsWith("a"));
System.out.println(anyStartsWithA); // true
// 验证 list 中 string 是否都是以 a 开头的
boolean allStartsWithA =
stringCollection
.stream()
.allMatch((s) -> s.startsWith("a"));
System.out.println(allStartsWithA); // false
// 验证 list 中 string 是否都不是以 z 开头的,
boolean noneStartsWithZ =
stringCollection
.stream()
.noneMatch((s) -> s.startsWith("z"));
System.out.println(noneStartsWithZ); // true
```
- **Count 计数**
`count` 是一个终端操作,它能够统计 `stream` 流中的元素总数,返回值是 `long` 类型。
```java
// 先对 list 中字符串开头为 b 进行过滤,让后统计数量
long startsWithB =
stringCollection
.stream()
.filter((s) -> s.startsWith("b"))
.count();
System.out.println(startsWithB); // 3
```
- **Reduce**
`Reduce` 中文翻译为:_减少、缩小_。通过入参的 `Function`,我们能够将 `list` 归约成一个值。它的返回类型是 `Optional` 类型。
```java
Optional reduced =
stringCollection
.stream()
.sorted()
.reduce((s1, s2) -> s1 + "#" + s2);
reduced.ifPresent(System.out::println);
// "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"
```
以上是常见的几种流式操作,还有其它的一些流式操作,可以帮助我们更便捷地处理集合数据。

>2024 年 12 月 30 日第二版优化结束。
说一点心里话。
网上的八股其实不少,甚至还有付费的,大家都说自己的好。
我觉得面渣逆袭还不错,这是在星球嘉宾三分恶的初版基础上,加入了二哥自己的思考,加入了 1000 多份真实的面经之后的结果,从 24 届到 25 届,也帮助无数的小伙伴拿到了心仪的 offer,我想这就足够了。
自认为自己在面渣逆袭的制作上是花了心思的,也确实得到了大家的一些认可,我很欣慰。


很多时候,我觉得自己是一个佛系的人,不愿意和别人争个高低,也不愿意去刻意宣传自己的作品。
我喜欢静待花开。
如果你觉得面渣逆袭还不错,可以告诉学弟学妹们有这样一份免费的学习资料。
我还会继续优化,也不确定第三版什么时候会来,但我会尽力。
愿大家都有一个光明的未来。
这次仍然是三个版本,亮白、暗黑和 epub 版本。给大家展示其中一个 epub 版本吧,有些小伙伴很急需这个版本,所以也满足大家了。

由于 PDF 没办法自我更新,所以需要最新版的小伙伴,可以微信搜【**沉默王二**】,或者扫描/长按识别下面的二维码,关注二哥的公众号,回复【**222**】即可拉取最新版本。
当然了,请允许我的一点点私心,那就是星球的 PDF 版本会比公众号早一个月时间,毕竟星球用户都付费过了,我有必要让他们先享受到一点点福利。相信大家也都能理解,毕竟在线版是免费的,CDN、服务器、域名、OSS 等等都是需要成本的。
更别说我付出的时间和精力了,大家觉得有帮助还请给个口碑,让你身边的同事、同学都能受益到。

我把二哥的 Java 进阶之路、JVM 进阶之路、并发编程进阶之路,以及所有面渣逆袭的版本都放进来了,涵盖 Java基础、Java集合、Java并发、JVM、Spring、MyBatis、计算机网络、操作系统、MySQL、Redis、RocketMQ、分布式、微服务、设计模式、Linux 等 16 个大的主题,共有 40 多万字,2000+张手绘图,可以说是诚意满满。
---
图文详解 56 道 Java 基础面试高频题,这次吊打面试官,我觉得稳了(手动 dog)。整理:沉默王二,戳[转载链接](https://mp.weixin.qq.com/s/t7EYyF0VGEg1rAZut9dwSw),作者:三分恶,戳[原文链接](https://mp.weixin.qq.com/s/M-6RSRcRd3X93cR7VXpanw)。
_没有什么使我停留——除了目的,纵然岸旁有玫瑰、有绿荫、有宁静的港湾,我是不系之舟_。
**系列内容**:
- [面渣逆袭 Java SE 篇 👍](https://javabetter.cn/sidebar/sanfene/javase.html)
- [面渣逆袭 Java 集合框架篇 👍](https://javabetter.cn/sidebar/sanfene/javathread.html)
- [面渣逆袭 Java 并发编程篇 👍](https://javabetter.cn/sidebar/sanfene/collection.html)
- [面渣逆袭 JVM 篇 👍](https://javabetter.cn/sidebar/sanfene/jvm.html)
- [面渣逆袭 Spring 篇 👍](https://javabetter.cn/sidebar/sanfene/spring.html)
- [面渣逆袭 Redis 篇 👍](https://javabetter.cn/sidebar/sanfene/redis.html)
- [面渣逆袭 MyBatis 篇 👍](https://javabetter.cn/sidebar/sanfene/mybatis.html)
- [面渣逆袭 MySQL 篇 👍](https://javabetter.cn/sidebar/sanfene/mysql.html)
- [面渣逆袭操作系统篇 👍](https://javabetter.cn/sidebar/sanfene/os.html)
- [面渣逆袭计算机网络篇 👍](https://javabetter.cn/sidebar/sanfene/network.html)
- [面渣逆袭 RocketMQ 篇 👍](https://javabetter.cn/sidebar/sanfene/rocketmq.html)
- [面渣逆袭分布式篇 👍](https://javabetter.cn/sidebar/sanfene/fenbushi.html)
- [面渣逆袭微服务篇 👍](https://javabetter.cn/sidebar/sanfene/weifuwu.html)
- [面渣逆袭设计模式篇 👍](https://javabetter.cn/sidebar/sanfene/shejimoshi.html)
- [面渣逆袭 Linux 篇 👍](https://javabetter.cn/sidebar/sanfene/linux.html)