Java 基础语法与核心数据类型 专属复习笔记

目录

[模块一:8 大基本数据类型与包装类](#模块一:8 大基本数据类型与包装类)

[1. 8 大基本数据类型基础](#1. 8 大基本数据类型基础)

核心定义

个人理解

项目实际使用场景

面试考点标注

[2. 自动装箱 / 拆箱与包装类缓存池](#2. 自动装箱 / 拆箱与包装类缓存池)

核心定义

个人理解

项目实际使用场景

面试考点标注

[3. 基本类型与包装类的使用场景区别](#3. 基本类型与包装类的使用场景区别)

核心定义

个人理解

项目实际使用场景

面试考点标注

模块二:核心语法坑点

[1. 权限修饰符](#1. 权限修饰符)

核心定义

个人理解

项目实际使用场景

面试考点标注

[2. 运算符核心区别](#2. 运算符核心区别)

核心定义

个人理解

项目实际使用场景

面试考点标注

[3. 流程控制核心坑点](#3. 流程控制核心坑点)

核心定义

个人理解

项目实际使用场景

面试考点标注

[模块三:String 类核心(面试 100% 必问)](#模块三:String 类核心(面试 100% 必问))

[1. String 不可变的底层实现与好处](#1. String 不可变的底层实现与好处)

核心定义

个人理解

项目实际使用场景

面试考点标注

[2. StringTable(字符串常量池)与字符串拼接](#2. StringTable(字符串常量池)与字符串拼接)

核心定义

个人理解

项目实际使用场景

面试考点标注

[3. String、StringBuilder、StringBuffer 的区别](#3. String、StringBuilder、StringBuffer 的区别)

核心定义

个人理解

项目实际使用场景

面试考点标注

[Day1 当日验收清单](#Day1 当日验收清单)

面试模拟题(实习面试专属,附标准答案)

[一、基础必考题(8 道,中小厂实习 100% 覆盖)](#一、基础必考题(8 道,中小厂实习 100% 覆盖))

[1. 请列出 Java 的 8 大基本数据类型、对应字节数和包装类](#1. 请列出 Java 的 8 大基本数据类型、对应字节数和包装类)

[2. 以下代码的输出结果是什么?为什么?](#2. 以下代码的输出结果是什么?为什么?)

[3. 请详细说明==和equals()的核心区别](#3. 请详细说明==和equals()的核心区别)

[4. String 为什么设计成不可变的?有什么核心好处?](#4. String 为什么设计成不可变的?有什么核心好处?)

[5. String、StringBuilder、StringBuffer 的核心区别是什么?](#5. String、StringBuilder、StringBuffer 的核心区别是什么?)

[6. 4 种权限修饰符的访问范围区别是什么?](#6. 4 种权限修饰符的访问范围区别是什么?)

[7. 自动装箱和拆箱的底层原理是什么?](#7. 自动装箱和拆箱的底层原理是什么?)

[8. switch 语句支持哪些数据类型?不支持哪些?](#8. switch 语句支持哪些数据类型?不支持哪些?)

[二、场景坑点题(6 道,大厂实习高频代码题 / 找错题)](#二、场景坑点题(6 道,大厂实习高频代码题 / 找错题))

[1. 以下是校园二手平台商品价格计算的代码,运行会报什么错?为什么?怎么修复?](#1. 以下是校园二手平台商品价格计算的代码,运行会报什么错?为什么?怎么修复?)

[2. 以下是订单状态处理的代码,输出结果是什么?为什么?](#2. 以下是订单状态处理的代码,输出结果是什么?为什么?)

[3. 以下是下架二手平台过期商品的代码,运行会报什么错?为什么?怎么正确删除?](#3. 以下是下架二手平台过期商品的代码,运行会报什么错?为什么?怎么正确删除?)

[4. 以下代码的输出结果是什么?为什么?](#4. 以下代码的输出结果是什么?为什么?)

[5. 以下用户判空代码运行会报什么错?为什么?](#5. 以下用户判空代码运行会报什么错?为什么?)

[6. 以下代码输出结果是什么?为什么?](#6. 以下代码输出结果是什么?为什么?)

[三、进阶原理题(4 道,中大厂实习拔高题)](#三、进阶原理题(4 道,中大厂实习拔高题))

[1. new String("abc")创建了几个对象?详细说明过程](#1. new String("abc")创建了几个对象?详细说明过程)

[2. JDK7 为什么要把 StringTable 从方法区(永久代)移到堆内存?](#2. JDK7 为什么要把 StringTable 从方法区(永久代)移到堆内存?)

[3. JDK7 开始 switch 支持 String 类型,底层是怎么实现的?](#3. JDK7 开始 switch 支持 String 类型,底层是怎么实现的?)

[4. 为什么 HashMap 的 key 推荐用 String、Integer 这类不可变类?](#4. 为什么 HashMap 的 key 推荐用 String、Integer 这类不可变类?)

实习面试答题加分技巧(针对你的校园二手平台项目)


模块一:8 大基本数据类型与包装类

1. 8 大基本数据类型基础

核心定义

Java 提供 8 种原生基本数据类型,直接存储值在栈内存,无对象实例化开销,每种都有固定内存大小、取值范围和对应的引用类型包装类:

数据类型 字节数 取值范围 对应包装类
byte 1 [-128, 127] Byte
short 2 [-32768, 32767] Short
int 4 [-2^31, 2^31-1] Integer
long 8 [-2^63, 2^63-1] Long
float 4 单精度浮点数 Float
double 8 双精度浮点数 Double
char 2 [0, 65535](Unicode 字符) Character
boolean 1(编译后实现) true/false Boolean
个人理解

基本数据类型是 Java 中最底层的数据单元,核心优势是性能高、内存占用小;包装类是对基本类型的对象化封装,让基本类型具备面向对象的特性(可存入集合、可赋值为 null、自带工具方法),是 Java"一切皆对象" 设计的补充。

项目实际使用场景

结合校园二手平台的开发实践:

  1. 业务数值字段(非空)用基本类型:商品库存、浏览量、订单编号、支付状态码,这类字段不会为 null,且频繁参与计算,用 int/long/boolean 基本类型,避免对象的内存和性能开销;
  2. 实体类可空字段用包装类:对应 MySQL 中允许为 NULL 的字段(如商品折扣价、用户备注长度),必须用 Integer/Double 包装类,避免基本类型默认值(如 int 默认 0)带来的业务逻辑错误(比如误判折扣价为 0 元);
  3. 小范围数值用低字节类型:性别编码、商品状态码(0 - 上架 / 1 - 下架)这类取值范围极小的字段,用 byte/short,节省堆内存。
面试考点标注

✅ 高频必背:熟记每种类型的字节数,尤其是 int (4)、long (8)、char (2);

✅ 坑点:boolean 类型在 JVM 中无明确大小规定,编译后默认用 int 实现,boolean 数组用 byte 数组实现;

✅ 场景题:实体类字段选基本类型还是包装类的判断标准。


2. 自动装箱 / 拆箱与包装类缓存池

核心定义
  • 自动装箱 :编译器自动将基本类型转换为包装类,底层调用包装类.valueOf(xxx)方法;
  • 自动拆箱 :编译器自动将包装类转换为基本类型,底层调用xxxValue()方法(如Integer.intValue());
  • 包装类缓存池 :为了复用常用对象,JDK 默认对部分包装类做了对象缓存:
    • Byte/Short/Integer/Long:缓存范围 [-128, 127]
    • Character:缓存范围 [0, 127]
    • Boolean:缓存true/false两个固定对象
    • Float/Double:无缓存(浮点数取值无固定常用范围)
个人理解

自动装箱拆箱是 Java 的语法糖,简化了代码书写,但本质还是编译器帮我们做了对象转换;缓存池是 JVM 的性能优化,对高频使用的小数值对象做复用,避免频繁创建销毁对象带来的内存浪费。

项目实际使用场景
  1. 订单状态码命中缓存 :订单状态(0 - 待支付 / 1 - 已支付 / 2 - 已完成)取值都在[-128,127]内,自动装箱时会直接复用缓存池对象,不会创建新对象;
  2. 禁止手动 new 包装类 :项目中绝对禁止写Integer status = new Integer(1),这种写法会跳过缓存池,直接在堆中创建新对象,造成不必要的内存浪费;
  3. 拆箱空指针避坑 :实体类中包装类字段拆箱前必须判空,比如Integer discount = goods.getDiscount(); int dis = discount;,如果 discount 为 null,会直接抛出NullPointerException,这是项目中高频出现的线上 bug。
面试考点标注

✅ 必问:Integer a = 127; Integer b = 127; a == b 结果为 true,128 则为 false 的原因;

* **`Integer` 有缓存池:`-128 ~ 127`** * **自动装箱时,这个范围以内复用同一个对象** * **超出范围会 new 新对象** * **`==` 比较对象地址,不是比较值** * **包装类比较值永远用 `equals()`**

✅ 扩展:Integer 缓存池的上限可以通过 JVM 参数-XX:AutoBoxCacheMax修改;

✅ 坑点:包装类和基本类型混合运算时,包装类会自动拆箱,null 值会触发 NPE。


3. 基本类型与包装类的使用场景区别

核心定义
对比维度 基本类型 包装类
存储位置 栈内存 堆内存(对象头 + 数据)
null 值 不支持,有默认值 支持赋值为 null
性能 极高,无对象开销 较低,有 GC 开销
泛型 / 集合 不支持 支持
对象方法 自带工具方法(如 Integer.parseInt ())
个人理解

核心判断标准只有两个:字段是否允许为 null是否需要对象特性。业务开发中优先用基本类型,只有需要对象特性或允许为 null 时才用包装类。

项目实际使用场景
  1. 局部变量 / 计算变量用基本类型:循环计数器、价格计算中间值、方法内临时变量,用基本类型性能最高;
  2. 泛型集合必须用包装类List<Integer> goodsIdListMap<String, Long> userMap,Java 泛型不支持基本类型;
  3. 可选参数用包装类:商品搜索接口的价格区间参数(minPrice/maxPrice),允许用户不传(为 null),必须用 Double 包装类。
面试考点标注

✅ 场景题:开发中如何选择基本类型和包装类;

【常规计算用基本,可空集合用包装;数据库空选包装,泛型只能包装上。】

✅ 坑点:包装类的 equals 比较必须先判空,避免 NPE。


模块二:核心语法坑点

1. 权限修饰符

核心定义

4 种权限修饰符控制类、方法、变量的可见范围,是 Java 封装特性的核心实现,访问权限从大到小:public > protected > default(包私有,不写修饰符)> private

修饰符 同一类内 同一包内 不同包的子类 不同包的非子类
public
protected
default
private
个人理解

权限修饰符的核心作用是控制代码暴露范围,降低耦合,不该对外暴露的方法 / 变量绝对不要暴露,减少外部调用的误用风险。

项目实际使用场景
  1. Service 层业务方法用 public:供 Controller 层跨包调用;
  2. 类内工具方法用 private:比如订单 Service 内部的计算运费方法,只在当前类使用,禁止外部调用;
  3. 同包基础类用 default:同包下的 BaseController、BaseService,只允许包内的 Controller/Service 继承,不对外暴露;
  4. 父类扩展方法用 protected:BaseService 中的初始化方法,只允许子类重写,不对外暴露。
面试考点标注

✅ 必背:4 种修饰符的访问边界,尤其是 protected 和 default 的区别;

✅ 细节:接口的成员变量默认是public static final,方法默认是public abstract

✅ 限制:外部类只能用 public 或 default 修饰,内部类可以用全部 4 种修饰符。


2. 运算符核心区别

核心定义
  1. == 与 equals ()
    • ==:基本类型比较值,引用类型比较对象内存地址;
    • equals ():Object 类默认和 == 一致,常用类(String、Integer)都重写了该方法,用于比较对象内容。
  2. 短路与非短路逻辑符
    • &&/||:短路运算符,前面的条件能确定结果时,后面的条件不执行;
    • &/|:非短路运算符,所有条件都会执行。
  3. 位移运算符<<左移(等价于乘 2^n)、>>带符号右移(等价于除 2^n)、>>>无符号右移。
个人理解

运算符的坑点是业务 bug 的高频来源,尤其是字符串比较和空指针判断,开发中必须严格遵守规范。

项目实际使用场景
  1. 字符串比较必须用 equals ():用户登录时判断用户名、前端传的商品分类,绝对不能用 ==,因为前端传的字符串是 new 出来的对象,和常量池的字符串地址不同;
  2. 空指针判断用短路 &&if (user != null && user.getId() != null),user 为 null 时后面的条件不会执行,避免 NPE;如果用 &,user 为 null 时仍会执行后面的代码,直接报错;
  3. 数值计算用位移优化 :商品库存翻倍计算,用stock << 1代替stock * 2,性能更高。
面试考点标注

✅ 必问:== 和 equals 的区别,分基本类型、引用类型、String 三种场景回答;

  1. 基本类型
  • == :比较实际数值

  • 无 equals 方法 ,基本类型只能用==

  1. 普通引用类型(Integer、自定义对象等)
  • == :比较内存地址

  • equals :默认也是比地址,重写后比对象内容

  1. String 字符串
  • == :比较字符串地址

  • equals :重写过,比较字符内容

✅ 场景题:if (a == null & a.getId()) 会抛出 NPE,换成 && 则不会;

✅ 细节:重写 equals () 必须同时重写 hashCode (),否则 HashMap 中会出现 key 重复的问题。


3. 流程控制核心坑点

核心定义
  1. switch 语句
    • 支持类型:byte/short/int/char、枚举、String(JDK7+),不支持 long/float/double;
    • String 支持底层:将 String 转为 hashCode,本质还是 int 类型的 switch;
    • case 穿透:case 分支不加 break 时,会继续执行后续所有 case 的代码。
  2. foreach 循环 :底层基于 Iterator 迭代器实现,遍历过程中直接调用集合的 add/remove 方法,会触发ConcurrentModificationException(并发修改异常)。
个人理解

switch 的 case 穿透是高频业务 bug 来源,foreach 的并发修改异常是集合操作的经典坑点。

项目实际使用场景
  1. 订单状态流转加 break 防穿透:用 switch 处理订单状态时,每个 case 后必须加 break,否则待支付订单处理完后会继续执行已支付的逻辑,造成业务错误;
  2. 遍历删除集合用 Iterator :遍历商品列表删除下架商品时,不能直接在 foreach 里调用list.remove(),必须用iterator.remove(),避免并发修改异常;
  3. 字符串类型的业务判断用 switch:根据订单类型(sale/rent/exchange)走不同逻辑,用 switch 比 if-else 可读性更高。
面试考点标注

✅ 坑点:switch 的 case 穿透场景和解决方法;

case 匹配成功后,没有 break,会继续执行后续所有 case 代码,不会再判断条件。

解决穿透问题

  1. 每个 case 末尾加 break(最常用)匹配结束直接跳出 switch,阻止向下穿透

  2. return 直接返回方法,终止执行

  3. JDK14+ 使用箭头 case,天然无穿透】

✅ 原理:foreach 循环的底层实现,以及为什么会触发并发修改异常(fail-fast 机制);

【foreach 不是新语法,只是语法糖!

  • foreach 底层 = Iterator 迭代器

  • fail-fast 是集合的并发修改检测机制

  • 遍历过程中用集合方法 add/remove → modCount 改变 → 抛出异常

  • 想边遍历边删除,必须用迭代器的 remove ()

✅ 限制:switch 不支持 long、float、double 的原因。

  • 底层判定逻辑: switch 依靠哈希等值匹配判断分支,要求数据能精准确定相等值。

  • 浮点型缺陷: float、double 存在精度丢失,数值近似不等同实际相等,无法精准匹配 case 常量,判定结果不可靠。

  • long 类型缺陷: JDK 早期 switch 设计仅适配int 类型,byte、char、short 会自动转 int;long 占用 64 位,无法向上兼容转为 int,语法层面不兼容。

  • **补充支持类型:**仅支持:byte、short、char、int、枚举、String


模块三:String 类核心(面试 100% 必问)

1. String 不可变的底层实现与好处

核心定义
  • 不可变定义:String 对象一旦创建,其字符内容无法修改,所有看似修改 String 的方法(substring、concat)都是返回新的 String 对象;
  • 底层实现
    • JDK8 及之前:private final char[] value 存储字符,数组引用被 final 修饰,无法指向新数组;
    • JDK9 及之后:优化为private final byte[] value + 编码标记,节省内存;
    • String 类本身被 final 修饰,无法被继承,没有子类可以修改其不可变逻辑。
个人理解

String 不可变是 Java 的核心设计,本质是用 final 和私有封装,完全禁止外部修改 String 的内部数据,是 Java 安全性和性能优化的基础。

项目实际使用场景
  1. 敏感信息安全:用户的手机号、token、密码用 String 存储,不可变保证了这些信息不会被代码意外修改,避免安全漏洞;
  2. HashMap 的 key 首选 :商品分类缓存用HashMap<String, GoodsCategory>,String 不可变保证 hashCode 不会变化,不需要重新计算哈希,也不会出现 key 找不到的问题;
  3. 多线程安全:多线程环境下读取商品标题、用户昵称时,不可变的 String 不需要加锁,天然线程安全。
面试考点标注

✅ 必问:String 为什么设计成不可变?

✅ 必答:1. 线程安全;2. 可以复用字符串常量池,节省内存;3. 作为 HashMap 的 key 哈希值稳定;4. 敏感信息安全,避免被篡改。


2. StringTable(字符串常量池)与字符串拼接

核心定义
  • StringTable:存储字符串常量的哈希表,JDK7 及之后移到堆内存(之前在方法区),作用是复用相同内容的字符串,避免重复创建对象;
  • 字符串拼接优化
    • 常量拼接("a"+"b"):编译期直接优化为"ab",存入常量池;
    • 动态拼接(变量 + 字符串):编译期优化为StringBuilder.append(),最后调用toString()生成新 String 对象。
个人理解

字符串常量池是 JVM 对 String 的核心性能优化,不同的创建方式会产生不同的对象位置,是面试的核心考点。

项目实际使用场景
  1. 商品搜索接口拼接用 StringBuilder:动态拼接搜索 SQL 条件、商品描述时,绝对不能用循环 +"+" 拼接,因为每次循环都会创建新的 StringBuilder 和 String 对象,性能极差;必须手动创建 StringBuilder,循环调用 append ();
  2. 优先用字面量创建 StringString key = "goods:search:" 直接从常量池取对象,比new String("goods:search:")节省内存,后者会额外在堆中创建新对象。
面试考点标注

✅ 必问:String s = new String("abc")创建了几个对象?

✅ 答:2 个(常量池的 "abc" + 堆中的 new String 对象);如果常量池已经存在 "abc",则只创建 1 个堆对象;

✅ 细节:JDK7 把 StringTable 移到堆的原因:方法区内存小、GC 频率低,堆的 GC 更灵活,适合回收不常用的字符串。


3. String、StringBuilder、StringBuffer 的区别

核心定义
对比维度 String StringBuilder StringBuffer
可变性 不可变 可变 可变
线程安全 安全(不可变) 不安全 安全(方法加 synchronized)
性能 最低(每次修改都生成新对象) 最高 较低(有锁开销)
适用场景 少量字符串操作、常量定义 单线程大量拼接 多线程大量拼接
个人理解

99% 的业务开发场景都是单线程字符串拼接,所以优先用 StringBuilder,性能最高;只有明确的多线程拼接场景才用 StringBuffer。

项目实际使用场景
  1. 单线程拼接用 StringBuilder:Controller 层返回的 JSON 拼接、动态 SQL 拼接、商品列表的 HTML 描述拼接,都是单线程环境,用 StringBuilder 性能最优;
  2. 多线程日志拼接用 StringBuffer:全局日志拦截器中,多个线程同时拼接请求日志,用 StringBuffer 保证线程安全。
面试考点标注

✅ 必问:三者的核心区别,从可变性、线程安全、性能、使用场景四个维度回答;

✅ 细节:StringBuilder 和 StringBuffer 都继承自 AbstractStringBuilder,核心实现一致,只是 StringBuffer 的方法加了 synchronized 修饰。


Day1 当日验收清单

  1. 不看资料口述:8 大基本类型的包装类缓存规则、== 和 equals 的 3 种场景差异、String 不可变的 3 个核心好处;
  2. 结合项目口述:商品搜索接口为什么用 StringBuilder 拼接、实体类字段什么时候用包装类;
  3. 避坑确认:自动拆箱的 NPE 场景、foreach 修改集合的异常、switch 的 case 穿透。

面试模拟题(实习面试专属,附标准答案)

所有题目均来自一二线互联网公司 Java 实习面试高频题库,100% 覆盖 Day1 复习知识点,分为 3 个难度梯度,适配不同层次的实习面试要求。


一、基础必考题(8 道,中小厂实习 100% 覆盖)

1. 请列出 Java 的 8 大基本数据类型、对应字节数和包装类

【标准答案】

数据类型 字节数 对应包装类
byte 1 Byte
short 2 Short
int 4 Integer
long 8 Long
float 4 Float
double 8 Double
char 2 Character
boolean 1(编译后实现) Boolean

【考点对应】模块一:8 大基本数据类型基础


2. 以下代码的输出结果是什么?为什么?

java 复制代码
Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;
System.out.println(a == b);
System.out.println(c == d);

【标准答案】输出结果:truefalse原因:Integer 默认缓存[-128, 127]范围的对象,127 命中缓存,a 和 b 指向同一个缓存对象;128 超出缓存范围,会创建两个新的堆对象,地址不同。

【考点对应】模块一:包装类缓存池机制


3. 请详细说明==equals()的核心区别

【标准答案】分 3 种场景区分:

  1. 基本类型==只比较值,基本类型没有equals()方法;
  2. 普通引用类型==比较对象的内存地址,equals()默认和==一致(Object 类的实现),未重写时也是比较地址;
  3. 重写了 equals 的类(String、Integer 等)==仍比较地址,equals()比较对象的内容。

【考点对应】模块二:运算符核心区别


4. String 为什么设计成不可变的?有什么核心好处?

【标准答案】底层实现:String 类被 final 修饰无法继承,存储字符的数组被private final修饰,不对外暴露修改数组的方法。核心好处:

  1. 线程安全:不可变对象多线程下无需加锁,不会被修改;
  2. 节省内存:可以复用字符串常量池的对象,避免重复创建;
  3. 哈希稳定:作为 HashMap 的 key 时,hashCode 不会变化,无需重复计算,不会出现 key 找不到的问题;
  4. 安全:敏感信息(如密码、token)不会被意外篡改。

【考点对应】模块三:String 不可变特性


5. String、StringBuilder、StringBuffer 的核心区别是什么?

【标准答案】

对比维度 String StringBuilder StringBuffer
可变性 不可变,每次修改生成新对象 可变,直接修改内部数组 可变
线程安全 安全(不可变) 不安全 安全(方法加 synchronized 锁)
性能 最低 最高 较低(有锁开销)
适用场景 常量定义、少量字符串操作 单线程下大量字符串拼接 多线程下大量字符串拼接

【考点对应】模块三:String 类三者对比


6. 4 种权限修饰符的访问范围区别是什么?

【标准答案】权限从大到小:public > protected > default(包私有)> private

修饰符 同一类内 同一包内 不同包的子类 不同包的非子类
public
protected
default
private

【考点对应】模块二:权限修饰符


7. 自动装箱和拆箱的底层原理是什么?

【标准答案】

  • 自动装箱:编译器自动调用包装类.valueOf(基本类型)方法,将基本类型转为包装类对象;
  • 自动拆箱:编译器自动调用包装类.xxxValue()方法(如Integer.intValue()),将包装类转为基本类型;本质是 Java 的语法糖,编译阶段完成转换,运行期无额外优化。

【考点对应】模块一:自动装箱 / 拆箱


8. switch 语句支持哪些数据类型?不支持哪些?

【标准答案】支持的类型:byte、short、int、char、枚举、String(JDK7+)不支持的类型:long、float、double,因为这些类型的取值范围大,无法做精确的等值匹配。

【考点对应】模块二:流程控制坑点


二、场景坑点题(6 道,大厂实习高频代码题 / 找错题)

1. 以下是校园二手平台商品价格计算的代码,运行会报什么错?为什么?怎么修复?

java 复制代码
public class GoodsTest {
    public static void main(String[] args) {
        Integer discount = null; // 商品折扣,数据库查出来为null
        int originPrice = 100;
        int finalPrice = originPrice * discount / 10;
        System.out.println(finalPrice);
    }
}

【标准答案】报错:NullPointerException(空指针异常)原因:discount是 Integer 包装类,和基本类型运算时会自动拆箱,null 值拆箱会触发空指针。修复方案:拆箱前先判空,给默认值:

java 复制代码
int finalPrice = originPrice * (discount == null ? 10 : discount) / 10;

【考点对应】模块一:自动拆箱空指针坑点


2. 以下是订单状态处理的代码,输出结果是什么?为什么?

java 复制代码
public class SwitchTest {
    public static void main(String[] args) {
        int orderStatus = 1; // 1-已支付
        switch (orderStatus) {
            case 0:
                System.out.println("待支付");
            case 1:
                System.out.println("已支付");
            case 2:
                System.out.println("已完成");
            default:
                System.out.println("状态异常");
        }
    }
}

【标准答案】输出结果:

java 复制代码
已支付
已完成
状态异常

原因:case 穿透,case 1 分支没有加break,执行完后会继续执行后续所有 case 和 default 的代码。修复:每个 case 分支结束后加break

【考点对应】模块二:switch case 穿透坑点


3. 以下是下架二手平台过期商品的代码,运行会报什么错?为什么?怎么正确删除?

java 复制代码
List<Goods> goodsList = new ArrayList<>();
goodsList.add(new Goods(1, "手机", true));
goodsList.add(new Goods(2, "电脑", false));
goodsList.add(new Goods(3, "平板", false));

// 遍历删除下架商品
for (Goods goods : goodsList) {
    if (!goods.isOnSale()) {
        goodsList.remove(goods);
    }
}

【标准答案】报错:ConcurrentModificationException(并发修改异常)原因:foreach 循环底层是 Iterator 迭代器,直接调用list.remove()会修改集合的 modCount 计数,迭代器检测到计数变化会触发 fail-fast 机制抛出异常。正确删除方式:使用 Iterator 的 remove () 方法

java 复制代码
Iterator<Goods> iterator = goodsList.iterator();
while (iterator.hasNext()) {
    Goods goods = iterator.next();
    if (!goods.isOnSale()) {
        iterator.remove();
    }
}

【考点对应】模块二:foreach 循环坑点


4. 以下代码的输出结果是什么?为什么?

java 复制代码
String s1 = "abc";
String s2 = new String("abc");
String s3 = "a" + "b" + "c";
String s4 = new String("abc").intern();

System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s1 == s4);

【标准答案】输出结果:falsetruetrue原因:

  1. s1 是常量池的对象,s2 是堆中的新对象,地址不同;
  2. s3 是常量拼接,编译期直接优化为常量池的 "abc",和 s1 指向同一个对象;
  3. intern()方法会把堆中的 String 对象加入常量池,返回常量池的引用,和 s1 指向同一个对象。

【考点对应】模块三:字符串常量池与拼接优化


5. 以下用户判空代码运行会报什么错?为什么?

java 复制代码
User user = null;
if (user != null & user.getId() == 1) {
    System.out.println("用户ID为1");
}

【标准答案】报错:NullPointerException原因:&是非短路逻辑运算符,无论前面条件是否成立,后面的条件都会执行;user 为 null 时,仍会执行user.getId()触发空指针。修复:换成短路运算符&&,前面条件为 false 时后面不会执行。

【考点对应】模块二:短路与非短路运算符区别


6. 以下代码输出结果是什么?为什么?

java 复制代码
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1.equals(s2));
System.out.println(s1 == s2);

【标准答案】输出结果:truefalse原因:

  1. String 重写了 equals () 方法,比较的是字符数组的内容,两个对象内容都是 "abc",所以为 true;
  2. ==比较的是对象的内存地址,两个 new 出来的对象在堆中是不同的地址,所以为 false。

【考点对应】模块二:== 与 equals 的场景区别


三、进阶原理题(4 道,中大厂实习拔高题)

1. new String("abc")创建了几个对象?详细说明过程

【标准答案】分两种情况:

  1. 常量池没有 "abc" :创建 2 个对象
    • 第一个:在字符串常量池中创建 "abc" 对象;
    • 第二个:在堆内存中创建一个 new String 对象,内部 value 数组指向常量池的 "abc" 的字符数组。
  2. 常量池已经有 "abc" :创建 1 个对象
    • 只在堆中创建 new String 对象,复用常量池已有的 "abc"。

【考点对应】模块三:String 对象创建原理


2. JDK7 为什么要把 StringTable 从方法区(永久代)移到堆内存?

【标准答案】核心原因有两个:

  1. 内存限制问题:永久代内存很小,默认只有几十 M,字符串是高频创建的对象,很容易触发永久代 OOM;堆内存空间大,可灵活调整;
  2. GC 效率问题:永久代的 GC 频率极低,大量不用的字符串无法被及时回收,造成内存浪费;堆的 GC 频率高,能及时回收不再使用的字符串常量,释放内存。

【考点对应】模块三:StringTable 内存位置


3. JDK7 开始 switch 支持 String 类型,底层是怎么实现的?

【标准答案】本质是语法糖,底层还是 int 类型的 switch,编译阶段会做两层转换:

  1. 先调用 String 的hashCode()方法,把字符串转为 int 值,用 switch 做第一层 int 匹配;
  2. 匹配到 hashCode 后,再调用equals()方法做二次校验,避免 hash 冲突。所以 switch 支持 String 的本质,还是对 int 类型 switch 的封装。

【考点对应】模块二:switch 底层原理


4. 为什么 HashMap 的 key 推荐用 String、Integer 这类不可变类?

【标准答案】核心原因有两个:

  1. 哈希值稳定:不可变类的内容不会变,hashCode 不会变化,存入 HashMap 后不需要重新计算哈希值,性能更高;如果用可变对象做 key,key 修改后 hashCode 会变化,会出现存入 HashMap 后找不到 key 的问题;
  2. 线程安全:不可变类多线程下不会被修改,不会出现并发场景下哈希值错乱的问题。

【考点对应】模块三:String 不可变的设计意义


实习面试答题加分技巧(针对你的校园二手平台项目)

  1. 所有知识点绑定项目:回答问题时主动结合自己的项目,比如问 StringBuilder,就说「我做校园二手平台的商品搜索接口时,动态拼接 SQL 条件就是用的 StringBuilder,因为循环用 + 拼接会每次创建新对象,性能很差」,比单纯背知识点加分很多;
  2. 主动说踩坑经验:遇到坑点题,主动提自己项目中踩过的对应坑,比如自动拆箱 NPE,就说「我之前做订单模块的时候,数据库的折扣字段允许为 null,用 int 接收自动拆箱踩过 NPE 的坑,后来都改成包装类 + 判空」;
  3. 答题逻辑固定为「结论→原理→项目场景」:先给明确答案,再讲底层原理,最后举自己的项目例子,逻辑清晰,面试官认可度极高。
相关推荐
转型AI的宏达9 小时前
解除autoclaw白名单审批机制
java·服务器·前端
ch.ju9 小时前
Java程序设计(第3版)第四章——方法的重载
java·开发语言
ch.ju9 小时前
Java Programming Chapter 4——Overloading of method
java·开发语言
dulu~dulu9 小时前
大模型---工具调用
java·服务器·前端
Teable任意门互动9 小时前
拆解 Teable 背后研发主体,开源多维表格平台实力与落地案例
开发语言·开源·excel·飞书·开源软件
吃好睡好便好9 小时前
用直接输入的方式创建矩阵
开发语言·人工智能·学习·线性代数·算法·matlab·矩阵
过期动态9 小时前
【RabbitMQ高级篇】生产者可靠性、MQ可靠性、消费者可靠性以及延迟队列的实现
java·数据结构·分布式·算法·rabbitmq·ruby
NiKick9 小时前
理解C++中的构造函数如何影响对象初始化
开发语言·c++
海上彼尚9 小时前
Nodejs也能写Agent - 9.Mastra篇 - Mastra客户端
开发语言·前端·javascript·人工智能·node.js