1 无论是否抛出异常、无论从哪开始return、finally总会在 return前执行
是的,在 Java 中,finally 块的执行具有 “强保障性”,核心规则就是:无论 try 块中是否抛出异常、无论 try 或 catch 中如何 return,finally 块一定会在 return 前执行。
关键场景验证
1. try 正常执行,return 前执行 finally
java
public static int test() {
try {
System.out.println("try 执行");
return 100; // 标记点 A
} finally {
System.out.println("finally 执行"); // 标记点 B
}
}
// 执行流程:try → finally(B)→ return(A 的返回值)
// 输出:try 执行 → finally 执行 → 返回 100
2. try 抛异常,catch return 前仍执行 finally
java
public static int test() {
try {
System.out.println("try 执行(抛异常)");
int x = 1 / 0; // 抛 ArithmeticException
return 100;
} catch (Exception e) {
System.out.println("catch 执行");
return 200; // 标记点 C
} finally {
System.out.println("finally 执行"); // 标记点 D
}
}
// 执行流程:try 抛异常 → catch → finally(D)→ return(C 的返回值)
// 输出:try 执行(抛异常)→ catch 执行 → finally 执行 → 返回 200
3. finally 里修改返回值?不影响!
java
public static int test() {
try {
return 100; // 实际返回值由这里决定(暂存)
} finally {
return 200; // 注意:这种写法会覆盖返回,但不推荐!
// 即使写 "return 200",也是先执行 finally,再真正 return
}
}
// 执行:try return 暂存 100 → finally return 200 → 最终返回 200
// (但这种写法破坏逻辑,实际开发禁止在 finally 里 return!)
4. 极端情况:try 里用 System.exit(0)
java
public static void test() {
try {
System.out.println("try 执行");
System.exit(0); // 强制终止 JVM,finally 不执行!
} finally {
System.out.println("finally 执行"); // 不会执行
}
}
// 唯一例外:JVM 被强制终止时,finally 不执行
底层原理
- 返回值暂存:
try或catch里的return会先 “暂存返回值”,然后必须等finally执行完,才会真正返回。 - 执行顺序担保:JVM 在字节码层面,会确保
finally的逻辑插入到return之前执行,除非遇到System.exit这类 “杀死 JVM” 的操作。
开发建议
- ✅ 利用
finally做 “收尾工作”(如关闭资源、释放锁),因为它一定执行。 - ❌ 禁止在
finally里写return!会覆盖正常返回值,还可能隐藏异常,导致 bug 难排查。
简单说:只要 JVM 没被强制终止,finally 必定在 return 前执行,这是 Java 异常处理里的强规则,记住这个就能应对各种场景啦~
2 Java 开发工具 javac 的核心功能
1. javac 的本质作用
-
!!!!核心功能:
javac是 Java 的编译器,作用是 将.java源文件编译为.class字节码文件 ,字节码是 Java 虚拟机(JVM)可执行的格式。 - 示例:
命令行中执行javac Hello.java,会把Hello.java源文件编译成Hello.class字节码文件,后续可通过java Hello(调用java命令,由 JVM 加载字节码执行 )运行程序。
2. 选项拆解与辨析
| 选项 | 描述 | 结论 | 关键区分 |
|---|---|---|---|
| A | 将源程序编译成字节码 | ✔️ 正确 | javac 负责编译 .java → .class(字节码 ) |
| B | 将字节码编译成源程序 | ❌ 错误 | 这是反编译(如借助 jad 等工具 ),javac 不做反向操作 |
| C | 解释执行 Java 字节码 | ❌ 错误 | java 命令(JVM 工具 )负责加载、解释 / 执行字节码,不是 javac |
| D | 调试 Java 代码 | ❌ 错误 | 调试代码用 jdb(Java 调试器 )或 IDE 自带调试功能,javac 仅负责编译 |
3. Java 开发流程关联
Java 代码从编写到运行的典型流程:
---------------------------------------------------------------------------------------------------------------------------------
编写 .java 源文件 → javac 编译 → 生成 .class 字节码 → java 命令启动 JVM 执行字节码
---------------------------------------------------------------------------------------------------------------------------------
javac 是流程中编译阶段的核心工具,决定代码能否正确转换为 JVM 可识别的字节码。
4. 易混淆工具对比
| 工具 | 功能 | 作用阶段 |
|---|---|---|
javac | 编译 .java → .class | 编译期 |
java | 启动 JVM,加载并执行 .class | 运行期 |
jdb | 调试 Java 程序(设置断点、单步执行等 ) | 调试期 |
javap | 反解析 .class 文件(查看字节码结构 ) | 分析期 |
总结:javac 唯一核心功能是编译 Java 源文件为字节码,记住它在开发流程中的 “编译角色”,就能快速区分这类工具题~
3 yield和sleep
在 Java 里,yield 是 Thread 类的静态方法,用于线程调度协作,核心要点如下:
1. 基本定义与作用
- 定义:
public static native void yield();,是静态 native 方法,需 JVM 依赖底层系统实现。 - 作用:线程调用
yield时,主动 “暗示” 调度器:自己愿让出 CPU 使用权,回到就绪状态,让其他同 / 更高优先级线程有执行机会 。但调度器可忽略该暗示,线程可能立即重新获取 CPU
2. 执行特性
- 线程状态:调用后从运行态(Running) 转为就绪态(Runnable) ,不进入阻塞 / 等待态,直接参与下一轮 CPU 竞争。
- 锁资源:让出 CPU 时不释放已持有的锁,若其他线程需该锁,仍得等待。
- 优先级影响:理论上,同优先级线程更易获执行权,实际因平台(系统、JVM)差异,调度结果难完全预测。
3. 代码示例
java
public class YieldDemo {
public static void main(String[] args) {
new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("线程 A 执行:" + i);
if (i == 2) {
Thread.yield(); // i=2 时主动让出 CPU
}
}
}).start();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("线程 B 执行:" + i);
}
}).start();
}
}
// 多次运行结果可能不同,比如线程 A 让出 CPU 后,线程 B 可能抢到执行权,也可能线程 A 继续执行
4. 与 sleep 对比!!!!!!!!
| 特性 | yield() | sleep() |
|---|---|---|
| 线程状态 | 就绪态(Runnable) | 阻塞态(Timed Waiting) |
| 锁释放 | 不释放已持有锁 | 不释放已持有锁 |
| 异常处理 | 无 checked 异常 | 需处理 InterruptedException |
| 调度优先级 | 主要影响同 / 更高优先级线程竞争 | 允许任意优先级线程竞争 CPU |
| 平台依赖性 | 调度逻辑依赖 JVM / 系统实现 | 行为相对统一(休眠固定时长) |
5. 应用场景与局限
- 适用场景:
- 调试 / 测试:模拟多线程竞争,验证调度逻辑。
- 资源优化:自旋等待(如并发工具类内部)时,减少 CPU 空转。
- 协作式调度:非关键任务主动让出资源,避免长时间独占 CPU 。
- 局限性:
- 不可控:无法保证其他线程一定执行,线程可能刚让出就重新获得 CPU 。
- 性能风险:频繁调用会增加线程切换开销,可能降低整体性能。
- 平台差异:不同环境(系统、JVM 版本)下,调度效果可能不同。
6. 典型面试考点
- 是否释放锁?
不释放!即便调用yield,线程仍持有锁,其他线程需锁时仍需等待。 - 和
sleep(0)区别?
类似但有差异:sleep(0)会触发系统级重新调度;yield是 JVM 层面 “建议”,且sleep需处理中断异常。 - 实际效果验证?
可在计算密集型任务里插入yield,对比执行时间,观察线程切换影响。
实际业务开发中,yield 应用较少,优先用 wait/notify、Lock、Semaphore 等工具做线程协作。理解其原理,主要是应对多线程调度相关的知识考察与底层分析 。
4 java异常捕获的核心原则
1. Java 异常捕获的核心规则
异常匹配是 “就近匹配”+“精准匹配优先”,一旦匹配到第一个符合条件的 catch,就执行该 catch 的逻辑,不会继续向下穿透(和 switch-case 加 break 类似,但 Java 异常捕获天然不会穿透)。
2. 题目代码拆解
java
try {
// 假设抛出 IOException
}
// 1. 第一个 catch:匹配 FileNotFoundException(IOException 的子类)
catch (FileNotFoundException ex) { ... }
// 2. 第二个 catch:匹配 IOException(精准匹配)
catch (IOException ex) {
System.out.print("IOException!"); // 执行这里
}
// 3. 第三个 catch:匹配 Exception(IOException 的父类)
catch (Exception ex) { ... }
当抛出 IOException 时:
- 先检查第一个
catch(FileNotFoundException):IOException不是FileNotFoundException的实例(是父类),不匹配。 - 检查第二个
catch(IOException):精准匹配,执行System.out.print("IOException!")。 - 后续
catch(Exception)不会再执行(已匹配到更具体的异常)。
3. 和 switch-case 的区别
switch-case 是值匹配,若不加 break 会穿透执行;但 Java 异常捕获是类型匹配,一旦匹配到第一个符合条件的 catch,就终止后续 catch 检查,天然不会穿透。
比如错误认为会选 B(IOException!Exception!),是混淆了两种不同的流程规则。
4. 关键结论
Java 异常捕获流程:
- 按
catch顺序从上到下匹配。 - 匹配到第一个类型兼容的
catch(异常是catch类型的实例或子类),执行该catch。 - 执行后,跳出整个
try-catch结构,不会继续匹配后续catch。
因此,本题抛出 IOException 时,只会执行第二个 catch,输出 IOException!,选 A。
5 构造方法和静态方法
1. 构造方法的特性
- 作用:创建对象并初始化其状态,不可直接调用。
- 语法:方法名与类名相同,无返回类型(连
void都没有)。 - 关键特性:
- 依赖对象实例:必须通过
new关键字调用,用于创建对象。 - 隐式传递
this:构造方法内部可使用this引用当前正在创建的对象。 - 不可被
static修饰:静态方法属于类,而构造方法属于对象创建过程。
- 依赖对象实例:必须通过
示例:
java
public class Person {
private String name;
// 构造方法(非静态)
public Person(String name) {
this.name = name; // this 指向当前创建的对象
}
}
// 创建对象时自动调用构造方法
Person p = new Person("Alice"); // 不能用 Person.Person("Alice")
2. 静态方法的特性
- 作用:属于类,不依赖对象实例,可直接通过类名调用。
- 语法:用
static修饰,可返回任意类型。 - 关键限制:
- 无
this引用:静态方法中不能使用this或super(因不关联对象)。 - 只能访问静态成员:可直接调用静态方法 / 字段,不能直接访问实例成员。
- 无
示例:
java
public class MathUtils {
// 静态方法
public static int add(int a, int b) {
return a + b; // 不依赖对象,可直接调用
}
}
// 直接通过类名调用静态方法
int result = MathUtils.add(1, 2); // 无需创建 MathUtils 对象
3. 构造方法 vs 静态方法对比表
| 特性 | 构造方法 | 静态方法 |
|---|---|---|
| 修饰符 | 不能用 static | 必须用 static |
| 调用方式 | 通过 new 关键字调用 | 直接通过类名调用 |
| 返回类型 | 无返回类型(连 void 都没有) | 必须声明返回类型(如 int、void) |
| 关联对象 | 与创建对象的过程绑定 | 不依赖对象,属于类 |
能否使用 this | 可以(指向当前创建的对象) | 不可以(无对象上下文) |
| 能否访问实例成员 | 可以(通过 this) | 不可以(只能访问静态成员) |
4. 常见混淆点澄清
(1)静态工厂方法 ≠ 构造方法
静态工厂方法是类的静态方法,用于返回对象实例,但本质不是构造方法:
java
public class Person {
private Person(String name) { ... } // 私有构造方法
// 静态工厂方法(替代构造方法)
public static Person create(String name) {
return new Person(name);
}
}
// 通过静态方法创建对象
Person p = Person.create("Bob");
(2)构造方法中不能使用静态初始化块的逻辑
静态初始化块属于类加载阶段,构造方法属于对象创建阶段,两者执行时机不同:
java
public class Example {
static {
System.out.println("静态初始化块"); // 类加载时执行
}
public Example() {
System.out.println("构造方法"); // 对象创建时执行
}
}
总结
构造方法绝对不是静态的,它是对象创建的核心机制,依赖 new 关键字和实例上下文;而静态方法属于类,可独立于对象调用。理解两者的区别是掌握 Java 面向对象编程的基础!
6 **考前突击清单**
一、基础语法(必背)
1. 数据类型
- 8 大基本类型:
byte(1), short(2), int(4), long(8), float(4), double(8), char(2), boolean(1) - 包装类:
Byte/Short/Integer/Long/Float/Double/Character/Boolean, - 注意
int转Integer是自动装箱
2. 字符串
String不可变!拼接用StringBuilder(单线程)/StringBuffer(多线程)- 比较内容用
equals(),比较地址用== - 速记:
String不可变,修改必新建;拼接用Builder,效率高老大
二、面向对象(核心)
1. 类与对象
- 类是模板,对象是实例;
new关键字创建对象 - 成员变量 vs 局部变量:成员变量有默认值(
int是0,String是null),局部变量必须显式赋值
2. 构造方法
- 与类名同名,无返回值;默认有无参构造,若写了有参构造,无参需显式定义
- 作用:初始化对象,
this()调本类构造,super()调父类构造(必须放第一行) - 速记:构造与类名同,无返回要记清;
this/super第一行,初始化它最行
三、异常处理(必考)
try-catch-finally流程:try执行逻辑,catch按顺序匹配异常(子类在前,父类在后!),finally必执行(除非System.exit(0))- 受检异常(
IOException等)必须处理(try-catch或throws抛出),运行时异常(NullPointerException)可不管 - 速记:异常匹配按顺序,子类先抓父类后;
finally必执行,资源关闭它善后
四、集合框架(高频)
1. List vs Set vs Map
List(有序可重复):ArrayList(数组,查询快)、LinkedList(链表,增删快)Set(无序不可重复):HashSet(哈希表,无序)、TreeSet(红黑树,有序)Map(键值对):HashMap(哈希表,线程不安全)、Hashtable(线程安全,已过时)、TreeMap(有序)
2. 遍历方式
List/Set用for-each或迭代器IteratorMap遍历键值对:map.entrySet()效率最高- 速记:
List有序可重复,Set无序去重堆;Map键值来配对,遍历entrySet最会
五、多线程(理解核心)
1. 创建线程的两种方式
- 实现
Runnable接口(推荐,解耦):new Thread(new MyRunnable()).start() - 继承
Thread类(耦合高):new MyThread().start()
2. 线程状态 & 方法
- 启动线程用
start(),别直接调run()(普通方法调用,不会开新线程) sleep()(静态,休眠,不释放锁)、wait()(Object 方法,释放锁,需配合synchronized)- 速记:线程启动用
start,run调用没新腔;sleep休眠不放手,wait释放锁等帮忙
六、快速刷题技巧(针对选择题)
-
看关键词:
- 问
String拼接选StringBuilder/StringBuffer - 问线程安全集合选
Vector/Hashtable/ConcurrentHashMap(但Vector已过时,优先记ConcurrentHashMap) - 异常匹配选 “子类在前,父类在后”
- 问
-
排除法:
- 看到
String可变操作直接排除(String天生不可变) - 线程启动用
start(),选项里run()直接排除(除非问普通方法调用)
- 看到
最后 3 小时急救步骤
- 背高频考点:把上面的口诀和核心点快速过 2 遍,用手写加深记忆
- 看错题 / 典型题:把之前练过的题(尤其是老师画的重点)快速扫一遍,记答案逻辑
- 写关键代码:默写构造方法、
StringBuilder拼接、try-catch-finally、线程创建这几个核心代码片段
心态稳住!Java 考点虽然多,但高频点就那些,突击记住就能拿分。加油,明天考试一定能超常发挥✨
1625

被折叠的 条评论
为什么被折叠?



