java复习 18

1 无论是否抛出异常、无论从哪开始return、finally总会在 return前执行

是的,在 Java 中,finally 块的执行具有 “强保障性”,核心规则就是:无论 try 块中是否抛出异常、无论 try 或 catch 中如何 returnfinally 块一定会在 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 异常处理里的强规则,记住这个就能应对各种场景啦~

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/notifyLockSemaphore 等工具做线程协作。理解其原理,主要是应对多线程调度相关的知识考察与底层分析 。

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 时:

  • 先检查第一个 catchFileNotFoundException):IOException 不是 FileNotFoundException 的实例(是父类),不匹配。
  • 检查第二个 catchIOException):精准匹配,执行 System.out.print("IOException!")
  • 后续 catchException)不会再执行(已匹配到更具体的异常)。

3. 和 switch-case 的区别

switch-case 是值匹配,若不加 break 会穿透执行;但 Java 异常捕获是类型匹配,一旦匹配到第一个符合条件的 catch,就终止后续 catch 检查天然不会穿透

比如错误认为会选 B(IOException!Exception!),是混淆了两种不同的流程规则。

4. 关键结论

Java 异常捕获流程:

  1. 按 catch 顺序从上到下匹配。
  2. 匹配到第一个类型兼容的 catch(异常是 catch 类型的实例或子类),执行该 catch
  3. 执行后,跳出整个 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 都没有)必须声明返回类型(如 intvoid
关联对象与创建对象的过程绑定不依赖对象,属于类
能否使用 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 是 0String 是 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 或迭代器 Iterator
  • Map 遍历键值对:map.entrySet() 效率最高
  • 速记:List 有序可重复,Set 无序去重堆;Map 键值来配对,遍历 entrySet 最会

五、多线程(理解核心)

1. 创建线程的两种方式
  • 实现 Runnable 接口(推荐,解耦):new Thread(new MyRunnable()).start()
  • 继承 Thread 类(耦合高):new MyThread().start()
2. 线程状态 & 方法
  • 启动线程用 start(),别直接调 run()(普通方法调用,不会开新线程)
  • sleep()(静态,休眠,不释放锁)、wait()(Object 方法,释放锁,需配合 synchronized
  • 速记:线程启动用 startrun 调用没新腔;sleep 休眠不放手,wait 释放锁等帮忙

六、快速刷题技巧(针对选择题)

  1. 看关键词

    • 问 String 拼接选 StringBuilder/StringBuffer
    • 问线程安全集合选 Vector/Hashtable/ConcurrentHashMap(但 Vector 已过时,优先记 ConcurrentHashMap
    • 异常匹配选 “子类在前,父类在后”
  2. 排除法

    • 看到 String 可变操作直接排除(String 天生不可变)
    • 线程启动用 start(),选项里 run() 直接排除(除非问普通方法调用)

最后 3 小时急救步骤

  1. 背高频考点:把上面的口诀和核心点快速过 2 遍,用手写加深记忆
  2. 看错题 / 典型题:把之前练过的题(尤其是老师画的重点)快速扫一遍,记答案逻辑
  3. 写关键代码:默写构造方法、StringBuilder 拼接、try-catch-finally、线程创建这几个核心代码片段

心态稳住!Java 考点虽然多,但高频点就那些,突击记住就能拿分。加油,明天考试一定能超常发挥✨

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值