【模拟面试|豆包模拟面试-1 Java基础】
还没针对性 去背八股 纯靠之前记得多少 效果不是很理想 基础题答的都很乱
Java 基础面试完整笔记(问题 + 我的回答 + 标准答案 + 指导)
1. 问题:Java 中基本数据类型都有哪些,它们各自的特点是什么?
我的回答
有整数类型、浮点类型,还有引用类型。整数类型分为 int、long,然后还有浮点类型 float 跟 BYTE,然后还有字符型、布尔值。❌
标准答案
Java 的基本数据类型共 8 种,无引用类型(引用类型是类、接口、数组等,不属于基本类型),具体分类及特点如下:
| 类型 | 字节数 | 取值范围 / 特点 |
|---|---|---|
| 整数型 | ||
| byte | 1 | -128~127,最小整数类型 |
| short | 2 | -32768~32767 |
| int | 4 | -2³¹~2³¹-1(最常用整数类型) |
| long | 8 | -2⁶³~2⁶³-1,需加后缀 L(如 10L) |
| 浮点型 | ||
| float | 4 | 单精度,需加后缀 F(如 3.14F),精度有限 |
| double | 8 | 双精度,默认浮点类型(如 3.14),精度更高 |
| 字符型 | ||
| char | 2 | 存储 Unicode 字符(如 'a'、' 中 '),取值 0~65535 |
| 布尔型 | ||
| boolean | 未明确 | 仅 true/false,虚拟机实现为 1 位或 1 字节 |
指导
- 易错点:混淆 “基本类型” 与 “引用类型”,漏记 byte、short;将 byte 误归为字符串类型。
- 改进:用 “分类记忆法”(整数、浮点、字符、布尔)记全 8 种,重点记住字节数和关键后缀(long 加 L、float 加 F)。
2. 问题:int 类型在内存中占几个字节,取值范围是多少?
我的回答
占 4 个字节,取值范围是 - 128 到 17。❌
标准答案
- 字节数:4 个字节(32 位)
- 取值范围:-2³¹(-2147483648)~ 2³¹-1(2147483647)
指导
- 易错点:取值范围完全记混(-128 是 byte 的最小值)。
- 改进:关联 “字节数→位数→取值范围” 逻辑:4 字节 = 32 位,1 位符号位,31 位数值位,故最小值为 - 2³¹,最大值为 2³¹-1,通过公式辅助记忆。
3. 问题:Java 中的字符串类型有哪些,它们之间有什么区别?
我的回答
Java 中的字符串类型有 byte 跟 string,byte 的话是字节型字符串类型,string 是一个对象。❌
标准答案
Java 中核心字符串相关类型为 3 种(byte 是基本数据类型,非字符串类型):
| 类型 | 不可变性 | 线程安全 | 效率 | 核心场景 |
|---|---|---|---|---|
| String | 不可变 | 安全 | 低 | 字符串常量、少量拼接 |
| StringBuilder | 可变 | 不安全 | 高 | 单线程频繁字符串拼接 |
| StringBuffer | 可变 | 安全(synchronized 修饰) | 中 | 多线程频繁字符串拼接 |
指导
- 易错点:将 byte 误判为字符串类型;混淆 StringBuffer 与 StringBuilder 的线程安全和效率顺序。
- 改进:先明确 “byte≠字符串类型”,再用 “不可变性→线程安全→效率” 的逻辑链记忆,重点记住:String 不可变(拼接创建新对象)、StringBuffer 安全但慢、StringBuilder 不安全但快。
4. 问题:什么是封装,在 Java 中如何体现?
我的回答
封装就是面向对象三个重要特点之一,封装需要把...(未说完)❌
标准答案
- 定义:将对象的属性(数据)和操作属性的方法封装在一起,隐藏内部实现细节,仅对外暴露必要的访问接口(getter/setter),保护数据安全性。
- Java 中的体现:
- 成员变量用
private修饰(禁止外部直接访问); - 提供
public的 getter 方法(获取属性值)和 setter 方法(修改属性值,可添加逻辑校验); - 示例:
java
运行
class User {private String name; // 私有属性,隐藏细节public String getName() { return name; } // 暴露获取接口public void setName(String name) {if (name != null && !name.isEmpty()) { // 逻辑校验this.name = name;}} }
- 成员变量用
指导
- 不足:仅知道是面向对象特性,未说清 “隐藏细节 + 暴露接口” 的核心,也未说明实现方式。
- 改进:记住封装的 “目的(保护数据)+ 实现步骤(private 修饰属性 + public getter/setter)”,结合示例理解。
5. 问题:继承在 Java 中又是怎样的呢?
我的回答
继承就是实现它的父类,然后用一个 override 的方法实现它,如果父类是抽象类的话,就必须要实现它的所有方法。
标准答案
- 定义:子类通过
extends关键字继承父类的非私有属性和方法,实现代码复用,建立类的层级关系(如 Animal→Dog)。 - 核心规则:
- Java 单继承:一个子类只能继承一个父类(但可多层继承,如 Dog→Animal→Object);
- 重写(@Override):子类可重写父类的非静态方法,方法名、参数列表、返回值类型需完全一致(子类返回值可是父类的子类型);
- 抽象类继承:子类继承抽象类(
abstract class),必须实现所有抽象方法(除非子类也是抽象类)。
指导
- 不足:未提及
extends关键字、单继承规则;对重写的语法规则描述不完整。 - 改进:重点记忆 “单继承 + 重写三要素 + 抽象类实现要求”,结合 “父类 Animal,子类 Dog 重写 eat () 方法” 的示例。
6. 问题:多态又该怎么理解呢?
我的回答
多态就是父类可以引用子类的方法,子类也可以变成父类,就是上下兼容,向下转型的话要注意。
标准答案
- 定义:同一行为在不同对象上有不同表现形式,核心是 “父类引用指向子类对象”。
- 实现条件:
- 存在继承关系;
- 子类重写父类方法;
- 父类引用指向子类对象(向上转型)。
- 核心规则:
- 向上转型(自动):
Parent p = new Child();,只能调用父类声明的方法(实际执行子类重写的实现); - 向下转型(强制):需用
instanceof判断类型,避免ClassCastException,如:java
运行
if (p instanceof Child) {Child c = (Child) p; // 安全转型c.childMethod(); // 调用子类特有方法 }
- 向上转型(自动):
指导
- 不足:未说明多态的实现条件(继承、重写、父类引用);对向下转型的
instanceof判断未提及。 - 改进:用 “Animal p = new Dog (); p.eat ();” 示例理解向上转型,记住 “向下转型必须先判断类型” 的核心要求。
7. 问题:public、private、protected 和默认修饰符(不写)有什么区别?
我的回答
Public 就是公共类,所有都能访问到。private 就是只有当前类才能访问到。protected 是只有当前包可以访问到,类访问不到。 不写的话默认是 default,跟 public 一样。❌
标准答案
| 修饰符 | 本类 | 同包 | 子类(不同包) | 其他包 |
|---|---|---|---|---|
| public | √ | √ | √ | √ |
| protected | √ | √ | √ | × |
| default | √ | √ | × | × |
| private | √ | × | × | × |
指导
- 易错点:protected 允许不同包子类访问;default 仅同包可访问,与 public 完全不同。
- 改进:用 “表格记忆法”,重点标注 protected 和 default 的差异,结合 “同包非子类、不同包子类” 的场景测试。
8. 问题:Java 中的异常处理机制是怎样的?
我的回答
Java 中的异常处理机制大部分都是抛那个 throw。Try 就是尝试这个方法是否能成功,然后 catch 捕捉异常,然后 finally 不管怎么样,它最终都会执行。
标准答案
- 核心机制:通过
try-catch-finally-throw-throws实现异常的捕获、处理和抛出,避免程序崩溃。 - 关键组件:
- try:包裹可能抛出异常的代码块;
- catch:捕获指定类型的异常并处理(可多个 catch,按异常子类→父类顺序);
- finally:无论是否抛异常,都会执行(常用于释放资源,如关闭流、连接);
- throw:手动抛出异常(如
throw new NullPointerException("空指针")); - throws:声明方法可能抛出的异常(编译时异常必须声明)。
- 异常分类:
- 编译时异常(Checked):编译时必须处理(捕获或声明),如
IOException、SQLException; - 运行时异常(Unchecked):编译时不强制处理,运行时触发,如
NullPointerException、ArrayIndexOutOfBoundsException。
- 编译时异常(Checked):编译时必须处理(捕获或声明),如
指导
- 不足:未提及
throw(手动抛异常)、throws(声明异常);对异常分类及示例不熟悉。 - 改进:结合 “读取文件” 的示例(try-catch-finally 关闭流),记忆异常处理流程;记 3 个常见编译时异常和 3 个运行时异常。
9. 问题:常见的异常类型,比如运行时异常和编译时异常的区别?
我的回答
运行时异常是要运行了才能异常,编译时异常是说在 IDE 上会提示的异常。
标准答案
| 维度 | 编译时异常(Checked) | 运行时异常(Unchecked) |
|---|---|---|
| 编译阶段 | 强制处理(不处理报错) | 不强制处理 |
| 继承关系 | 继承Exception(非RuntimeException) | 继承RuntimeException |
| 常见示例 | IOException、SQLException、ClassNotFoundException | NullPointerException、ArrayIndexOutOfBoundsException、ArithmeticException(除数为 0) |
| 产生原因 | 外部环境问题(如文件不存在、数据库连接失败) | 代码逻辑错误(如空指针、数组越界) |
指导
- 不足:仅描述现象,未说明继承关系、产生原因及具体示例。
- 改进:通过 “文件不存在→IOException(编译时)” 和 “调用 null 对象方法→NullPointerException(运行时)” 的场景,记忆两者差异。
10. 问题:Java 中的接口和抽象类有什么区别?
我的回答
抽象类跟接口,抽象类其实就是有普通方法和成员变量,接口是抽象方法和常量。如果是继承抽象类的话,要实现它的所有方法,如果是实现接口...(未说完)
标准答案
| 维度 | 抽象类(abstract class) | 接口(interface) |
|---|---|---|
| 定义关键字 | abstract class | interface |
| 构造方法 | 有(不能实例化,供子类调用) | 无 |
| 方法类型 | 可包含普通方法、抽象方法(abstract 修饰) | JDK8 前:仅抽象方法;JDK8 后:可加默认方法(default)、静态方法 |
| 成员变量 | 可包含任意访问修饰符的变量 | 仅 public static final 常量(可省略修饰符) |
| 继承 / 实现 | 子类用 extends 继承,单继承 | 类用 implements 实现,多实现 |
| 核心作用 | 代码复用(封装共性方法 / 属性) | 定义行为规范(强制实现类遵守) |
指导
- 不足:未提及构造方法差异、继承 / 实现数量限制(单继承 vs 多实现)、JDK8 接口的默认方法;对核心作用理解不深。
- 改进:用 “抽象类 Animal(含普通方法 eat ())、接口 Flyable(含抽象方法 fly ())” 示例,理解 “复用” vs “规范” 的核心差异。
11. 问题:Java 中 final 关键字的作用?
我的回答
Final 关键字就是不能继承,假如用在常量上面就不可更改,用在方法上面,就是最终方法也不能改,也不能重写。如果用在类上面,它就是不能被继承。总之 final 是最终方法。
标准答案
- 修饰类:类不能被继承(如
String类),子类无法 extends; - 修饰方法:方法不能被重写,可避免子类修改核心逻辑;
- 修饰变量:
- 基本类型:值不可变(如
final int a=10; a=20;报错); - 引用类型:引用地址不可变(对象内容可改,如
final User u=new User(); u.setName("张三");合法,u=new User();报错);
- 基本类型:值不可变(如
- 关键特性:可重入性(修饰方法时,同一线程可多次调用,不会死锁)。
指导
- 不足:未区分 “基本类型变量” 和 “引用类型变量” 的 final 差异;未提及 final 的可重入性。
- 改进:重点记忆 “final 修饰引用→地址不变” 的核心,用 “final User u=new User ();” 示例理解对象内容可修改的特性。
12. 问题:Java 里 == 和 equals 的区别?
我的回答
等于(==)是比较它的对象地址,equals 的话是可以重写,比较它的值。
标准答案
- ==:
- 基本数据类型:比较值是否相等(如
int a=10; int b=10; a==b→true); - 引用数据类型:比较对象的内存地址是否相等(是否为同一对象);
- 基本数据类型:比较值是否相等(如
- equals:
- 默认实现(Object 类):
return this==obj;,本质是比较地址; - 重写后(如 String、Integer):比较对象的内容是否相等(如
String s1="abc"; String s2=new String("abc"); s1.equals(s2)→true,s1==s2→false);
- 默认实现(Object 类):
- 示例:
java
运行
Integer a=127; Integer b=127; a==b→true(缓存池); Integer c=128; Integer d=128; a==b→false(新对象); c.equals(d)→true(比较值)。
指导
- 不足:未说明 == 对基本类型和引用类型的差异;未提及 equals 的默认实现。
- 改进:用 “String 的 == 和 equals 对比”“Integer 缓存池示例” 强化记忆,核心是 “== 看地址,equals 看重写逻辑”。
13. 问题:Java 里的异常体系,包括 Checked 异常和 Unchecked 异常的区别,还有常见的异常类举例?
我的回答
受检异常跟非受检异常的区别就是编译时会检查,如果编译异常的话,编译期就会报错。运行异常只有运行时期才会报错,异常通常用 try catch finally 来处理。try 是用来尝试方法,catch 捕捉异常,finally 不管最终怎么样,它都会执行。
标准答案
- 异常体系顶层:
Throwable,分为Error(错误,如OutOfMemoryError,程序无法处理)和Exception(异常,可处理); - Checked 异常(受检):
- 继承
Exception,非RuntimeException; - 编译时强制处理(捕获或声明
throws); - 常见示例:
IOException(文件读写)、SQLException(数据库操作)、ClassNotFoundException(类未找到);
- 继承
- Unchecked 异常(非受检):
- 继承
RuntimeException; - 编译时不强制处理,运行时触发;
- 常见示例:
NullPointerException(空指针)、ArrayIndexOutOfBoundsException(数组越界)、ArithmeticException(除数为 0)、IllegalArgumentException(参数非法);
- 继承
- 处理原则:Checked 异常必须处理,Unchecked 异常需通过代码逻辑避免(如判空避免空指针)。
指导
- 不足:未区分
Error和Exception;常见异常类举例匮乏;未说明 Unchecked 异常的避免方式。 - 改进:记 “Error 是致命错误,Exception 是可处理异常”;默写 3 个 Checked 和 4 个 Unchecked 异常的类名及场景。
14. 问题:Java 中 String、StringBuffer 和 StringBuilder 的区别?
我的回答
他们都是字符串,String 是最原始的字面值,是不可更改的。通常从常量池里面取,然后 StringBuffer 和 StringBuilder 都是有缓冲,按效率来说,String 的效率最低,其次是 StringBuilder,StringBuffer 效率最高。❌
标准答案
| 类型 | 不可变性 | 线程安全 | 底层实现 | 效率 | 核心场景 |
|---|---|---|---|---|---|
| String | 不可变 | 安全 | 字符数组(final char []) | 低 | 字符串常量、少量拼接 |
| StringBuffer | 可变 | 安全(synchronized 修饰方法) | 字符数组 | 中 | 多线程频繁拼接(如日志输出) |
| StringBuilder | 可变 | 不安全 | 字符数组 | 高 | 单线程频繁拼接(如业务逻辑处理) |
- 关键:String 不可变→拼接时创建新对象(如
a+b等价于new StringBuilder(a).append(b).toString()),效率低;后两者可变,直接扩容字符数组,效率高。
指导
- 易错点:记反 StringBuffer 和 StringBuilder 的效率(StringBuffer 因同步锁效率低于 StringBuilder)。
- 改进:用 “多线程用 StringBuffer,单线程用 StringBuilder,常量用 String” 的口诀记忆,结合拼接 1000 次字符串的效率对比示例。
15. 问题:Java 里的线程池,说说它的核心参数有哪些,都是干嘛的?
我的回答
Java 里线程池作用基本上是为了资源复用,避免频繁连接关闭造成的性能浪费。然后它的参数主要有最大允许的连接量,释放量什么之类的。Java 的线程池可以用在 JDBC 之类的多线程连接中。
标准答案
- 核心作用:控制并发线程数,减少线程创建 / 销毁的开销,避免资源耗尽;
- 核心参数(ThreadPoolExecutor 的构造方法):
- corePoolSize:核心线程数,长期存活(除非设置 allowCoreThreadTimeOut);
- maximumPoolSize:最大线程数,线程池能创建的最多线程;
- keepAliveTime:非核心线程空闲存活时间,超时销毁;
- TimeUnit:keepAliveTime 的单位(如 TimeUnit.SECONDS);
- BlockingQueue:任务队列,存储等待执行的任务(如 ArrayBlockingQueue、LinkedBlockingQueue);
- ThreadFactory:创建线程的工厂(可自定义线程名称);
- RejectedExecutionHandler:拒绝策略(任务满时如何处理,如 AbortPolicy 直接抛异常);
- 常见场景:Web 服务器处理 HTTP 请求、批量数据导入导出、定时任务(Quartz)。
指导
- 不足:核心参数记忆不全(仅想到最大连接量);未说明参数之间的配合逻辑(如核心线程满→队列满→创建非核心线程→达最大线程→拒绝)。
- 改进:画 “任务提交流程图” 辅助记忆参数作用,重点记住前 5 个核心参数,结合 “Tomcat 的线程池处理请求” 的场景理解。
16. 问题:JVM 的内存模型,说说栈、堆、方法区都存啥?
我的回答
栈里面放的方法,堆里面放的对象,方法区里面放的方法。 ❌
标准答案
- 栈(线程私有):
- 每个线程对应一个栈,每个方法调用对应一个栈帧;
- 存储:局部变量(基本类型、引用类型的地址)、操作数栈、方法返回地址;
- 特点:方法执行完栈帧销毁,内存自动释放;
- 堆(线程共享):
- 存储:所有 new 创建的对象(包括成员变量)、数组;
- 特点:垃圾回收(GC)的主要区域,内存动态分配;
- 方法区(线程共享,JDK8 后为元空间):
- 存储:类的元数据(类结构、方法字节码)、常量池(字符串常量、静态常量)、静态变量(static 修饰)、运行时常量池;
指导
- 不足:描述过于笼统,未明确栈存储 “局部变量”、方法区存储 “类元数据 + 静态变量 + 常量池” 的核心;
- 改进:用 “User u=new User (); u.setName ("张三");” 示例:u 是栈中的引用,User 对象(含 name 成员变量)在堆中,User 类的字节码、static 变量在方法区。
17. 问题:Java 里的基本数据类型有哪些,各自占多少字节?
我的回答
整数型 int 4 个字节,long 8 个字节,浮点型 float 4 个字节,double 8 个字节,字符型 1 个字节,布尔值没有明确的字节规定。
标准答案
| 基本类型 | 字节数 | 备注 |
|---|---|---|
| byte | 1 | 整数型最小范围 |
| short | 2 | 中短整数 |
| int | 4 | 最常用整数类型 |
| long | 8 | 需加后缀 L |
| float | 4 | 需加后缀 F |
| double | 8 | 默认浮点类型 |
| char | 2 | 存储 Unicode 字符,如 'a'、' 中' |
| boolean | 未明确 | 虚拟机实现为 1 位或 1 字节,仅 true/false |
指导
- 易错点:漏记 byte(1 字节)、short(2 字节);char 的字节数记为 1(实际 2 字节)。
- 改进:制作 “类型 - 字节数” 对照表,每天花 1 分钟默写,重点关注 char 和 byte 的差异。
18. 问题:Java 里的反射机制是干嘛的,说说它的基本使用场景?
我的回答
Java 反射机制是用来反射方法,通过反射机制可以直接修改类的方法里面的数据,会破坏类的封装结构。反射机制一般是用在框架里面的。
标准答案
- 定义:运行时获取类的信息(构造器、方法、字段)并操作这些组件的能力(无需知道类的编译期结构);
- 核心作用:
- 动态创建对象(如
Class.forName("com.User").newInstance()); - 动态调用方法(如
Method m = clazz.getMethod("say"); m.invoke(obj)); - 访问私有成员(如
Field f = clazz.getDeclaredField("name"); f.setAccessible(true););
- 动态创建对象(如
- 基本步骤:
- 获取 Class 对象(3 种方式):
类名.class、对象.getClass()、Class.forName("全类名"); - 获取组件(构造器 / 方法 / 字段):
getConstructor()、getMethod()、getField(); - 操作组件:
newInstance()(创建对象)、invoke()(调用方法)、set()(修改字段);
- 获取 Class 对象(3 种方式):
- 使用场景:框架底层(Spring 依赖注入、MyBatis 映射 SQL、JUnit 反射调用测试方法);
- 注意:破坏封装性,性能略低(反射操作需解析字节码)。
指导
- 不足:未说明反射的核心步骤、动态创建对象 / 调用方法的能力;仅知道框架使用,但不明确具体场景(如 Spring DI)。
- 改进:结合 “Spring 通过反射创建 User 对象并注入属性” 的场景,记忆反射的 3 个步骤,重点理解 “运行时动态操作” 的核心。
19. 问题:Java 里的集合框架,说说 List 和 Set 的主要区别,还有常用的实现类?
我的回答
List 跟 Set 都是单列集合。List 是可重复的,Set 是不可重复的。List 采用的有 Redis,还有 link。Set 有 Redis set 跟 link set。它们共同的方法有增删改查。Set 其实是 map 的实现,hash code 改了什么,我也忘记了。
标准答案
- 核心区别:| 维度 | List | Set ||--------------|-------------------------------|-------------------------------|| 元素重复性 | 允许重复元素 | 不允许重复元素(依赖 equals 和 hashCode) || 元素顺序 | 有序(插入顺序 = 遍历顺序) | 无序(HashSet)/ 有序(LinkedHashSet 按插入顺序) || 索引访问 | 支持索引访问(get (int index)) | 不支持索引访问 || 核心接口方法 | add (int index, E e)、get (int index) | 无索引相关方法,add (E e) 返回 boolean(是否添加成功) |
- 常用实现类:
- List:
- ArrayList:动态数组,查询快(索引访问),增删慢(数组扩容 / 移位);
- LinkedList:双向链表,增删快(修改指针),查询慢(遍历);
- Set:
- HashSet:基于 HashMap 实现(元素存 HashMap 的 key),无序、去重,效率高;
- LinkedHashSet:继承 HashSet,底层加链表维护插入顺序,有序、去重;
- TreeSet:基于红黑树实现,自然排序(如 Integer 升序),去重;
- List:
指导
- 易错点:混淆 Redis(非 Java 集合)与 Java 集合实现类;不知道 Set 的去重依赖
equals()和hashCode(); - 改进:先明确 “List = 有序可重复 + 索引”“Set = 无序不可重复 + 无索引” 的核心,再记忆实现类的底层结构(数组 / 链表 / HashMap / 红黑树)及优缺点。
20. 问题:Java 中类的加载过程,分为哪几个阶段?
我的回答
不会。
标准答案
类加载过程是 JVM 将.class 文件加载到内存并初始化的过程,共 5 个阶段:
- 加载(Loading):
- 读取.class 文件(从磁盘、jar 包等),将字节流转换为方法区的运行时数据结构;
- 生成 Class 对象(存于堆中),作为访问类元数据的入口;
- 验证(Verification):
- 检查.class 文件的合法性(如字节码格式、语法规范),避免恶意代码;
- 准备(Preparation):
- 为类的静态变量(static)分配内存(在方法区),并赋默认值(如 static int a=10→默认值 0);
- 注意:不执行静态代码块,仅赋默认值;
- 解析(Resolution):
- 将符号引用(如类名、方法名)转换为直接引用(内存地址);
- 初始化(Initialization):
- 执行静态代码块(static {}),为静态变量赋最终初始值(如 a=10);
- 触发时机:首次主动使用类(如 new 对象、调用静态方法、访问静态变量);
指导
- 不足:完全不了解类加载的 5 个阶段,属于知识盲区;
- 改进:按 “加载→验证→准备→解析→初始化” 的顺序记忆,重点记住 “准备阶段赋默认值,初始化阶段赋最终值” 的核心差异,用 “static int a=10” 的示例辅助。
21. 问题:Java 中 HashMap 的底层实现原理,JDK7 和 JDK8 有啥区别?
我的回答
HashMap 就是用数组列表实现,JDK7 跟 JDK8 的区别是 JDK8 的列表达到一定长度会转化成红黑树。通过 hash code 分配内存,数组位置重复了就添加到列表后面,如果列表大于 8 以后就变成红黑树,小于 6 的话红黑树会变成链表,这是 JDK8 以后的新特性。
标准答案
- 核心原理:基于 “哈希表(数组 + 链表 / 红黑树)” 实现,通过 hash () 方法计算 key 的哈希值,确定元素在数组的位置(索引 = 哈希值 & 数组长度 - 1);
- JDK7 vs JDK8 区别:| 维度 | JDK7 | JDK8 ||--------------|-------------------------------|-------------------------------|| 底层结构 | 数组 + 链表(拉链法) | 数组 + 链表 / 红黑树(链表长度≥8 且数组长度≥64→红黑树;≤6→链表) || hash 计算 | 多次扰动(9 次异或 + 移位) | 简化扰动(1 次异或 + 移位),效率更高 || 存储结构 | Entry 数组(key、value、next、hash) | Node 数组(继承 Entry,优化结构) || 插入方式 | 头插法(新元素插链表头部) | 尾插法(新元素插链表尾部),避免死循环 || 扩容机制 | 数组长度达阈值(负载因子 0.75)→扩容为 2 倍,重新哈希 | 同 JDK7,但红黑树扩容时会拆分为链表 |
指导
- 不足:未提及负载因子(0.75)、数组长度为 2 的幂次、JDK7 头插法可能导致死循环、JDK8 的 hash 计算简化等细节;
- 改进:重点记忆 “结构升级(链表→红黑树)、插入方式(头插→尾插)、hash 计算简化” 三个核心差异,理解 “红黑树提升查询效率(O (logn) vs 链表 O (n))” 的优势。
22. 问题:Java 中 synchronized 关键字的作用和实现原理?
我的回答
关键字作用是同步,保证线程安全,让共享资源在多线程访问时不会乱套。
标准答案
- 核心作用:实现线程同步,保证同一时间只有一个线程访问被修饰的代码块 / 方法,解决多线程并发的数据一致性问题;
- 修饰范围:
- 实例方法:锁为当前对象(this);
- 静态方法:锁为当前类的 Class 对象;
- 代码块:锁为括号内的对象(如
synchronized (obj) {});
- 实现原理:
- 底层依赖 JVM 的 “监视器锁(Monitor)”,通过
ACC_SYNCHRONIZED标志(方法级)或monitorenter/monitorexit指令(代码块级)实现; - 锁的升级过程(从低到高):无锁→偏向锁→轻量级锁→重量级锁(减少锁竞争开销);
- 底层依赖 JVM 的 “监视器锁(Monitor)”,通过
- 关键特性:可重入性(同一线程可多次获取同一把锁,避免死锁,如递归调用 synchronized 方法);
指导
- 不足:未说明 synchronized 的修饰范围(实例方法 / 静态方法 / 代码块)、实现原理(Monitor 锁)、锁升级和可重入性;
- 改进:结合 “多线程自增计数器” 示例理解同步作用,记忆 “修饰范围→锁对象” 的对应关系,重点了解 “可重入性” 和 “锁升级” 的核心特性。
23. 问题:Integer 的缓存池机制,比如 Integer a = 127; Integer b = 127; 和 a=128;b=128; 时,== 的结果为啥不一样?
我的回答
不会。
标准答案
- 缓存池机制:Java 为节省内存,预先创建了
-128~127范围内的 Integer 对象,存于缓存池(IntegerCache类); - 自动装箱逻辑:
- 当
Integer a = 127;时,触发自动装箱,底层调用Integer.valueOf(127),直接从缓存池获取对象,故a==b→true; - 当
Integer a = 128;时,128 超出缓存范围,Integer.valueOf(128)会创建新的 Integer 对象,故a==b→false;
- 当
- 核心代码(Integer.valueOf ()):
java
运行
public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i); } - 注意:缓存范围可通过 JVM 参数
-XX:AutoBoxCacheMax=N调整上限(如调整为 255);
指导
- 不足:完全不了解缓存池范围和自动装箱的底层逻辑;
- 改进:记住 “-128~127” 的核心范围,通过 “== 比较地址,equals 比较值” 的逻辑,理解两次结果差异的原因,可手动运行代码验证。
24. 问题:Java 中接口的默认方法(default method)是怎么回事,解决了什么问题?
我的回答
Java default 就是 clone,解决什么问题不知道。
标准答案
- 定义:JDK8 及以后,接口中可定义带方法体的默认方法(用
default修饰),实现类可直接继承或重写; - 核心作用:解决接口演进的兼容性问题;
- 问题:JDK8 前,接口新增抽象方法后,所有实现类都必须重写该方法,否则编译报错,导致老代码无法兼容;
- 解决:新增默认方法时提供默认实现,实现类无需强制重写,直接使用默认逻辑,也可根据需求重写;
- 示例:
java
运行
interface MyInterface {default void say() {System.out.println("默认实现");} } class MyImpl implements MyInterface {// 可直接使用say(),也可重写 } - 注意:若实现多个接口,且接口有同名默认方法,实现类必须重写该方法(避免冲突);
指导
- 不足:完全误解默认方法的含义,不知道其 “解决接口演进兼容性” 的核心作用;
- 改进:重点记忆 “默认方法 = 带方法体 + 解决接口新增方法兼容问题”,结合 “集合框架 List 接口新增 sort () 默认方法” 的实际场景理解。
25. 问题:Java 中 IO 流的分类,比如字节流和字符流的区别,还有常用的类?
我的回答
字节流是一个字节一个字节的传数据,通常用的是 BYTE writer 什么的方法,用来传输二进制数据,视频音乐什么的。字符流只能用来传输文本数据,还要注意编码,编码错会乱码,方法是 write 和 read。
标准答案
- IO 流分类:
- 按处理数据类型:字节流(处理二进制数据)、字符流(处理文本数据,依赖编码);
- 按流向:输入流(读数据)、输出流(写数据);
- 字节流与字符流区别:| 维度 | 字节流 | 字符流 ||--------------|-------------------------------|-------------------------------|| 处理数据 | 二进制数据(图片、视频、文件) | 文本数据(.txt、日志) || 依赖编码 | 不依赖(直接操作字节) | 依赖(字节→字符需编码,如 UTF-8) || 核心父类 | 输入:InputStream;输出:OutputStream | 输入:Reader;输出:Writer || 常用类 | FileInputStream、FileOutputStream、BufferedInputStream | FileReader、FileWriter、BufferedReader || 核心方法 | read ()(读字节)、write (int b)(写字节) | read ()(读字符)、write (int c)(写字符) |
- 关键:字符流底层依赖字节流,缓冲流(BufferedXXX)通过缓存减少 IO 次数,提高效率;
指导
- 不足:常用类记忆不准确(如字节流的 FileInputStream);未说明字符流依赖字节流的底层关系;
- 改进:用 “表格 + 场景” 记忆:二进制文件(图片)用 FileInputStream,文本文件用 FileReader+BufferedReader(按行读取
readLine())。
总结指导
1. 整体改进方向
- 避免 “笼统回答”:每个问题尽量分点、结合示例,比如讲多态时必须提 “父类引用指向子类对象” 和
instanceof; - 攻克 “知识盲区”:重点补全类加载过程、Integer 缓存池、接口默认方法、反射步骤等完全不会的知识点;
- 强化 “细节记忆”:基本数据类型的字节数 / 取值范围、访问修饰符表格、StringBuffer 与 StringBuilder 的效率差异等,用 “表格 + 口诀” 辅助;
- 关联 “场景与原理”:每个知识点结合实际场景(如 HashMap 用在缓存、线程池用在 Web 请求),理解底层逻辑(如 HashMap 的红黑树提升查询效率)。
2. 复习方法建议
- 每日默写:每天花 30 分钟默写核心表格(基本数据类型、访问修饰符、List/Set 区别、JDK7/8 HashMap 区别);
- 代码验证:对模糊知识点(如 Integer 缓存池、== 和 equals 差异),手动写代码运行,直观感受结果;
- 场景联想:看到框架(如 Spring),联想底层技术(反射、依赖注入);看到多线程,联想 synchronized、线程池;
- 错题整理:将易错点(如 StringBuffer 和 StringBuilder 的效率)单独记录,反复强化。
通过以上针对性复习,可快速弥补细节漏洞和知识盲区,让 Java 基础回答更精准、全面,面试时更有竞争力。如果需要针对某个知识点深入讲解或补充更多面试题,可随时沟通!
压力🍐 还没开始背 回答的确实是一坨 大概知道怎么用 就是不知道怎么组织语言 和原理 定义啥的
