YCBlogs 技术笔记:Java 异常与错误分类体系(Exception/Error)深度解析

发布时间:2026/10/10 5:15:14

YCBlogs 技术笔记:Java 异常与错误分类体系(Exception/Error)深度解析 教程技术博客文档【免费下载链接】YCBlogs技术博客笔记大汇总包括Java基础线程并发数据结构Android技术博客等等常用设计模式常见的算法网络协议知识点部分flutter笔记还包括平时开发中遇到的bug汇总当然也在工作之余收集了大量的面试题长期更新维护并且修正持续完善……开源的文件是markdown格式的转载请注明出处谢谢项目地址https://gitcode.com/gh_mirrors/yc/YCBlogs点击查看免费下载本文是 YCBlogs 仓库中 bug/00.常见的异常.md 的深度展开。该文档系统梳理了 Java 标准库中 25 个常见异常Exception与 22 个常见错误Error的触发场景是排查运行时崩溃、阅读崩溃日志的速查手册。读完本文你将能够从崩溃堆栈第一行快速判断异常/错误归属类别并定位根因理解 Throwable 异常体系与检查/非检查异常的划分逻辑掌握 try-catch-finally、throw/throws 的正确用法并从字节码层面理解 JVM 如何通过异常表完成异常捕获以及 try-catch 对性能的真实影响。一、异常体系总览先理解 Throwable、Exception、Error在逐条解读常见异常与错误之前必须先建立整体框架。Java 中所有的异常与错误都继承自Throwable其直接子类只有两个分支Error程序无法处理的严重问题如内存溢出、栈溢出。大多数 Error 与代码编写者执行的操作无关而表示代码运行时 JVMJava 虚拟机出现的问题。这些错误发生时JVM 一般会选择终止线程。不应该也不适合由应用程序捕获处理。Exception程序本身可以处理的异常是日常开发中主要面对的对象又分为运行时异常RuntimeException 及其子类和编译期非运行时异常。从是否需要强制处理的角度看Java 异常又分为两类详见 Java异常问题分类范围是否强制处理受检查异常checkedException 中除 RuntimeException 及其子类以外的所有异常如 IOException、SQLException必须显式处理要么 try-catch 捕获要么在方法签名上用 throws 声明否则无法通过编译不受检查异常uncheckedError 全部、RuntimeException 及其子类如 NullPointerException、ClassCastException编译器不检查可以不捕获不声明由 JVM 在运行时自动抛出在 Throwable异常体系 中有更完整的架构说明Exception 可以是可被控制checked或不可控制的unchecked表示由程序员导致的错误应在应用程序级被处理Error 总是不可控制的经常用于表示系统错误或底层资源错误如有可能应在系统级被捕捉。一个有效使用的异常应当清晰回答三个问题what / where / why异常类型告诉什么被抛出异常堆栈跟踪告诉在哪里抛出异常信息告诉为什么抛出。例如仓库中的一条典型 Android 崩溃信息java.lang.NullPointerException: Attempt to invoke virtual method void android.app.Activity.finish() on a null object reference at com.com.yc.toollib.crash.CrashTestActivity.onClick(CrashTestActivity.java:48) at android.view.View.performClick(View.java:7187) ... at android.os.Looper.loop(Looper.java:230) at android.app.ActivityThread.main(ActivityThread.java:7742)其中异常类型是NullPointerException异常信息说明是在一个 null 对象上调用了finish()方法堆栈轨迹则展示了从ActivityThread.main到onClick的完整调用链帮助定位到第 48 行的具体代码。二、Exception 分类详解25 种常见异常及触发场景以下为 bug/00.常见的异常.md 中完整收录的 25 种常见异常按触发场景分组解读并补充了触发原因与规避思路。2.1 算术与数字类异常类触发场景java.lang.ArithmeticException算术条件异常如整数除零等。典型例子int a 520 / 0;会抛出该异常java.lang.NumberFormatException数字格式异常。试图将 String 转换为指定的数字类型而该字符串不满足数字类型要求的格式时抛出如Integer.parseInt(abc)java.lang.NegativeArraySizeException数组大小为负值异常。使用负数大小值创建数组时抛出如new int[-3]2.2 数组与索引类异常类触发场景java.lang.ArrayIndexOutOfBoundsException数组索引越界异常。对数组的索引值为负数或大于等于数组大小时抛出java.lang.ArrayStoreException数组存储异常。向数组中存放非数组声明类型对象时抛出如向Object[]中放入与其运行时类型不兼容的对象java.lang.IndexOutOfBoundsException索引越界异常。访问某个序列的索引值小于 0 或大于等于序列大小时抛出是数组/字符串越界异常的基类java.lang.StringIndexOutOfBoundsException字符串索引越界异常。使用索引值访问字符串中的字符而索引值小于 0 或大于等于字符串长度时抛出2.3 类型转换与反射类异常类触发场景java.lang.ClassCastException强制类型转换异常。类 A 和 B 互不是父类或子类O 是 A 的实例强制将 O 构造为类 B 的实例时抛出即常说的强制类型转换异常java.lang.ClassNotFoundException找不到类异常。应用根据字符串形式的类名构造类如Class.forName在遍历 CLASSPATH 之后找不到对应名称的 class 文件时抛出java.lang.IllegalAccessException违法的访问异常。通过反射方式创建类的实例、访问类属性、调用类方法而当时无法访问类、属性、方法或构造方法的定义时抛出java.lang.InstantiationException实例化异常。试图通过newInstance()方法创建某个类的实例而该类是一个抽象类或接口时抛出java.lang.TypeNotPresentException类型不存在异常。以类型名称的字符串表达方式访问该类型但根据给定名称找不到该类型时抛出。与 ClassNotFoundException 的区别在于该异常是 unchecked不被检查异常而 ClassNotFoundException 是 checked被检查异常2.4 状态与线程类异常类触发场景java.lang.IllegalStateException违法的状态异常。在 Java 环境和应用尚未处于某个方法的合法调用状态而调用了该方法时抛出。在 Android 开发中非常常见如 崩溃bug日志总结1 中记录的资源未初始化就使用、Cant compress a recycled bitmap等java.lang.IllegalMonitorStateException违法的监控状态异常。线程试图等待一个自己并不拥有的对象O的监控器或者通知其他线程等待该对象O的监控器时抛出。典型场景是在synchronized块之外调用wait()/notify()java.lang.IllegalThreadStateException违法的线程状态异常。线程尚未处于某个方法的合法调用状态而调用了该方法时抛出如在已启动的线程上再次调用start()java.lang.InterruptedException被中止异常。线程处于长时间的等待、休眠或其他暂停状态此时其他线程通过Thread.interrupt()方法终止该线程时抛出2.5 空值与属性方法类异常类触发场景java.lang.NullPointerException空指针异常。在要求使用对象的地方使用了 null 时抛出如调用 null 对象的实例方法、访问 null 对象的属性、计算 null 对象的长度、使用 throw 语句抛出 null 等。这是 Java 异常中最常见也最令人头疼的异常java.lang.NoSuchFieldException属性不存在异常。访问某个类不存在的属性时抛出java.lang.NoSuchMethodException方法不存在异常。访问某个类不存在的方法时抛出2.6 其他专用异常异常类触发场景java.lang.CloneNotSupportedException不支持克隆异常。没有实现 Cloneable 接口或对象不支持克隆方法时调用其clone()方法抛出java.lang.EnumConstantNotPresentException枚举常量不存在异常。通过名称和枚举类型访问枚举对象但该枚举对象并不包含该常量时抛出java.lang.SecurityException安全异常。由安全管理器抛出用于指示违反安全情况java.lang.UnsupportedOperationException不支持的方法异常。指明请求的方法不被支持的情况。它是 RuntimeException 的子类常见于未实现的方法体如throw new UnsupportedOperationException(Not implemented)2.7 两个根异常异常类说明java.lang.Exception根异常。用以描述应用程序希望捕获的情况是所有受检查异常与运行时异常的公共父类java.lang.RuntimeException运行时异常。是所有 Java 虚拟机正常操作期间可以被抛出的异常的父类。它及其子类属于不受检查异常程序可以选择捕获处理也可以不处理但一旦发生会导致程序中断执行因此开发时最好仍用 try-catch 处理以保证程序健壮性关于运行时异常与编译期异常的划分在 Throwable异常体系 中有详细说明编译时异常Java 程序必须显式处理否则程序会发生错误无法通过编译运行时异常无需显式处理也可以和编译时异常一样处理。运行时异常一般由程序逻辑错误引起应从逻辑角度尽可能避免这类异常的发生被检查异常Exception类本身及其子类中除运行时异常之外的其它子类编译器会检查它。例如CloneNotSupportedException通过clone()接口克隆一个未实现Cloneable接口的对象时抛出必须显式处理才能编译通过。被检查异常通常都是可以恢复的。三、Error 分类详解22 种常见错误及触发场景Error 是程序无法处理的严重问题大多数与代码编写者执行的操作无关而表示代码运行时 JVM 出现的问题。这些错误是不可查的因为它们在应用程序的控制和处理能力之外。以下为原文档完整收录的 22 种常见错误按触发场景分组。3.1 虚拟机与资源类错误类触发场景java.lang.VirtualMachineError虚拟机错误。指示虚拟机被破坏或者继续执行操作所需的资源不足的情况java.lang.InternalError内部错误。指示 Java 虚拟机发生了内部错误java.lang.UnknownError未知错误。指示 Java 虚拟机发生了未知严重错误的情况java.lang.OutOfMemoryError内存不足错误。可用内存不足以让 Java 虚拟机分配给一个对象时抛出。在 Java异常问题 中专门讨论了 OOM 能否被 try-catch 的问题详见后文java.lang.StackOverflowError堆栈溢出错误。应用递归调用的层次太深而导致堆栈溢出时抛出java.lang.ThreadDeath线程结束。调用 Thread 类的 stop 方法时抛出用于指示线程结束。注Thread.stop()已被废弃不应在业务代码中使用3.2 类加载与字节码类错误类触发场景java.lang.ClassFormatError类格式错误。Java 虚拟机试图从一个文件中读取 Java 类而检测到文件内容不符合类的有效格式时抛出java.lang.ClassCircularityError类循环依赖错误。初始化一个类时检测到类之间循环依赖则抛出java.lang.NoClassDefFoundError未找到类定义错误。Java 虚拟机或类装载器试图实例化某个类而找不到该类的定义时抛出。与ClassNotFoundException的区别在于前者是 Error编译期存在、运行期找不到后者是 checked 异常java.lang.VerifyError验证错误。验证器检测到某个类文件中存在内部不兼容或者安全问题时抛出java.lang.UnsupportedClassVersionError不支持的类版本错误。Java 虚拟机试图读取某个类文件但发现该文件的主、次版本号不被当前 Java 虚拟机支持时抛出。即常说的类由更高版本 JDK 编译导致版本不兼容3.3 链接与依赖类错误类触发场景java.lang.LinkageError链接错误。该错误及其所有子类指示某个类依赖于另外一些类在该类编译之后被依赖的类改变了其类定义而没有重新编译所有类进而引发错误的情况java.lang.IncompatibleClassChangeError不兼容的类变化错误。正在执行的方法所依赖的类定义发生了不兼容的改变时抛出。一般在修改了应用中的某些类的声明定义而没有对整个应用重新编译而直接运行时容易引发java.lang.AbstractMethodError抽象方法错误。应用试图调用抽象方法时抛出java.lang.IllegalAccessError违法访问错误。应用试图访问、修改某个类的域Field或调用其方法但又违反域或方法的可见性声明时抛出java.lang.NoSuchFieldError域不存在错误。应用试图访问或修改某类的某个域而该类的定义中没有该域的定义时抛出java.lang.NoSuchMethodError方法不存在错误。应用试图调用某类的某个方法而该类的定义中没有该方法的定义时抛出java.lang.UnsatisfiedLinkError未满足的链接错误。Java 虚拟机未找到某个类的声明为 native 方法的本机语言定义时抛出。在 Android 上典型表现为 so 库加载失败见 崩溃bug日志总结1 中的libijkffmpeg.so崩溃案例3.4 初始化与断言类错误类触发场景java.lang.ExceptionInInitializerError初始化程序错误。执行一个类的静态初始化程序的过程中发生异常时抛出。静态初始化程序指直接包含于类中的 static 语句段java.lang.InstantiationError实例化错误。应用试图通过 Java 的 new 操作符构造一个抽象类或接口时抛出java.lang.AssertionError断言错误。用来指示一个断言失败的情况。注意Error本身是所有错误的基类用于标识严重的程序运行问题这些问题通常描述一些不应被应用程序捕获的反常情况四、异常处理机制try-catch-finally 与 throw/throws理解了异常分类之后需要掌握如何正确处理它们。完整的机制梳理见 异常处理的流程机制。4.1 五个关键字关键字作用try用于监听。将要被监听的代码可能抛出异常的代码放在 try 语句块之内当 try 块内发生异常时异常就被抛出catch用于捕获异常。catch 用来捕获 try 语句块中发生的异常finally无论是否捕获或处理异常finally 块里的语句都会被执行。主要用于回收在 try 块里打开的物理资源数据库连接、网络连接、磁盘文件。只有 finally 块执行完成之后才会回来执行 try 或 catch 块中的 return 或 throw 语句如果 finally 中使用了 return 或 throw 等终止方法的语句则不会跳回执行直接停止throw用于在方法体内抛出异常对象throws用在方法签名中用于声明该方法可能抛出的异常两种处理方式的使用原则如果该功能内部可以将问题处理用 try如果处理不了交由调用者处理则用 throws。区别在于后续程序需要继续运行就用 try后续程序不需要继续运行就用 throws。4.2 try-catch-finally 的执行顺序完整的执行顺序分析如下try 没有捕获到异常时try 语句块中的语句逐一被执行程序跳过 catch 语句块执行 finally 语句块和其后的语句try 捕获到异常但 catch 中没有处理此异常的语句此异常将抛给 JVM 处理。finally 语句块仍会被执行但 finally 之后的语句不会被执行try 捕获到异常且 catch 中有处理此异常的语句程序跳到 catch 语句块逐一匹配找到对应的处理程序其它 catch 语句块不会被执行try 中异常之后的语句也不会被执行catch 执行完后执行 finally最后执行 finally 之后的语句。4.3 throw 与 throws 的区别throws用在方法声明后面跟的是异常类名可以跟多个异常类名用逗号隔开。表示抛出异常由该方法的调用者来处理throws 表示出现异常的一种可能性并不一定会发生这些异常。throw用在方法体内跟的是异常对象名只能抛出一个异常对象自己 new 出来的。表示抛出异常由方法体内的语句处理。throw 语句之后的执行流程会立即停止所有后续语句都不会执行然后依次检查所有 catch 语句是否与异常类型匹配如果没有匹配的 catch默认的异常处理程序会终止程序并输出堆栈踪迹。一个典型用法对比方法声明static void pop() throws NegativeArraySizeException表示 pop 方法可能抛出该异常由调用者如 main处理而在方法内部throw new NullPointerException(NullPointer)则是主动抛出一个新的异常对象由上层 try-catch 捕获或交由 JVM 默认处理程序。4.4 捕获异常时的关键注意事项catch 中要注意异常层级关系异常子类必须位于异常超类之前因为使用了某个超类的 catch 语句会捕获这个超类及其所有子类的异常。如果子类位于超类之后永远也不会到达子类不可到达的代码会被编译器提示错误。例如先catch (Exception e)再catch (RuntimeException e)会报 exception java.lang.RuntimeException has already been caught 的编译错误因为 Exception 是 RuntimeException 的超类所有 RuntimeException 都会被第一个 catch 块捕获。多 catch 子句的匹配抛出异常时异常处理系统按代码书写顺序找出最近的处理程序找到匹配的处理程序后认为异常已得到处理不再继续查找。查找时并不要求抛出的异常和处理程序声明的异常完全匹配派生类的对象也可以匹配其基类的处理程序。也可以用catch (ArithmeticException | ArrayIndexOutOfBoundsException e)的方式在同一个 catch 子句中捕获多种异常每个多重捕获参数被隐式声明为 final 类型。覆盖方法的异常声明限制覆盖一个方法时不能声明与覆盖方法不同的异常声明的任何异常必须是被覆盖方法所声明异常的同类或子类。例如父类方法throws IOException子类覆盖时只能抛 IOException 或其子类不能抛其超类 Exception。finally 不一定会执行在 4 种特殊情况下 finally 块不会被执行——finally 语句块中发生异常、前面的代码用了System.exit()退出程序、程序所在的线程死亡、关闭 CPU。另外如果 try 语句本身没有被执行到如在 try 之前就 returnfinally 也不会执行。五、finally 与 return 的微妙关系finally处理逻辑介绍 用多个可运行案例验证了 finally 与 return 的执行顺序结论如下finally 在 return 语句执行之后、return 返回之前执行try 中的return b 80先执行此时 b 变为 100但并没有直接返回而是等 finally 执行完之后再返回结果 100finally 中的 return 会覆盖 try 中的 return如果 finally 里也有 return 语句会直接返回 finally 中的值try 中是否还有 return 语句都不再起作用此时 try 之后的 return 会变成不可到达语句需注释掉否则编译器报错finally 中修改基本类型变量不影响返回值finally里b 150并不会改变 try 中 return 的值返回 100因为 return 已经确定了返回值但如果 return 的是一个引用类型如 HashMapfinally 中修改该对象的内容如map.put(...)会生效而map null这类重新赋值不生效——这正是 Java 只有值传递的体现try 中的 return 在异常情况下不会执行若在 return 之前发生除零异常则转而执行 catch 和 finally此时它们对变量的修改都会影响最终返回的return b的值。六、JVM 层面的异常处理异常表、字节码与性能真相要真正理解异常需要下沉到 JVM 与字节码层面。这部分是 JVM处理异常解析 与 异常try-catch性能 的核心内容。6.1 异常实例的构造十分昂贵在构造异常实例时JVM 需要生成该异常的栈轨迹stack trace。该操作会逐一访问当前线程的 Java 栈帧并记录下各种调试信息包括栈帧所指向的方法的名字、方法所在的类名、文件名以及在代码中的第几行触发该异常。在生成栈轨迹时JVM 会忽略掉异常构造器以及填充栈帧的 Java 方法Throwable.fillInStackTrace直接从新建异常位置开始算起。由此引出一个实践问题既然异常构造昂贵能否缓存异常实例在需要时直接抛出语法上允许但该异常对应的栈轨迹并非 throw 语句的位置而是新建异常的位置这会误导开发人员定位到错误位置。因此实践中仍应抛出新建异常实例。6.2 虚拟机如何捕获异常异常表每个编译后的方法都附带一个异常表Exception table。异常表中的每一个条目都代表一个异常处理器由 from、to、target 指针及其异常类型构成这些指针的值是字节码索引bytecode index, bci。其中 from 和 to 标示了该异常处理器所监控的范围即 try 代码覆盖范围target 指向异常处理器的起始位置即 catch 起始位置。例如对如下代码编译后查看字节码javap -c0: invokestatic mayThrowException:()V 3: goto 11 6: astore_1 7: aload_1 8: invokevirtual java.lang.Exception.printStackTrace 11: return Exception table: from to target type 0 3 6 Class java/lang/Exception该条目的 from 指针和 to 指针分别为 0 和 3代表监控范围从索引 0 的字节码开始到索引 3 的字节码结束不包括 3target 是 6代表异常处理器从索引 6 的字节码开始最后一列说明捕获的异常类型是 Exception。运行时的匹配过程当程序触发异常时JVM 从上至下遍历异常表中的所有条目。如果触发异常的字节码索引值落在某个条目监控范围内JVM 判断所抛出异常与该条目要捕获的异常是否匹配匹配则将控制流转移至 target 指向的字节码。如果遍历完所有条目仍未匹配到异常处理器JVM 会弹出当前方法对应的 Java 栈帧在调用者caller中重复上述操作。最坏情况下JVM 需要遍历当前线程 Java 栈上所有方法的异常表。6.3 finally 代码块如何编译finally 代码块的编译是一个复制策略当前 JVM 的做法是复制 finally 代码块的内容分别放在 try-catch 代码块所有正常执行路径以及异常执行路径的出口中。针对异常执行路径Java 编译器会生成一个或多个异常表条目监控整个 try-catch 代码块并捕获所有种类的异常javap 中以 any 指代这些条目的 target 指向另一份复制的 finally 代码块并且在这个 finally 代码块的最后重新抛出所捕获的异常。以try-catch-finally的经典案例编译后查看可以发现有三份 finally 代码块前两份分别位于 try 代码块和 catch 代码块的正常执行路径出口最后一份作为异常处理器监控 try 块和 catch 块捕获 try 块触发的、未被 catch 捕获的异常以及 catch 块触发的异常。由此引出一个调试陷阱如果 catch 块捕获了异常并且触发了另一个异常finally 捕获并重抛的是后者catch 中触发的异常原本的异常便会被忽略掉这对代码调试十分不利。为此 Java 7 引入了Suppressed 异常允许将一个异常附于另一个异常之上抛出的异常可以附带多个异常信息并配套提供了try-with-resources语法糖在字节码层面自动使用 Suppressed 异常同时极大精简了资源打开关闭的用法——程序可以在 try 关键字后声明并实例化实现了AutoCloseable接口的类编译器将自动添加对应的 close() 操作。运行该类程序可以看到输出中的Suppressed: java.lang.RuntimeException: Foo2 / Foo1 / Foo0正是多资源关闭异常被正确保留的证据。6.4 try-catch 影响性能吗用实验说话异常try-catch性能 通过字节码和耗时实验给出了明确结论在未抛出异常的情况下try-catch 几乎没有性能开销。原因在于类会跟随一张异常表每个 try-catch 都会在表里添加行记录try 开始地址、结束地址、异常处理起始位、异常类名称。代码在运行时抛出异常时首先拿着抛出位置到异常表中查找是否可以被 catch如果异常没发生也就不会去查表。try 的范围大小其实就是异常表中开始地址和结束地址两个值的差异而已也不影响性能。实验数据也印证了这一点同样一段循环代码不触发异常i0运行耗时约 1133纳秒级别计数触发一次除零异常i0后耗时飙升至约 44177——一旦程序进入 catch 分支是非常耗资源的因为需要构造异常实例并生成栈轨迹。同时无论把 try 放在 for 循环外面还是里面从运行时长和字节码指令角度看性能都基本一致并不存在for 循环里放 try 会资源消耗加倍的问题。真正昂贵的不是 try 块本身而是异常实例的构造与异常发生的频率。七、异常链、自定义异常与 OOM 能否被捕获7.1 异常链异常链是 Java 中非常流行的异常处理概念在进行一个异常处理时抛出了另外一个异常由此产生一个异常链条。该技术大多用于将受检查异常封装成为非受检查异常或 RuntimeException。如果因为异常决定抛出一个新的异常一定要包含原有的异常这样处理程序才可以通过getCause()和initCause()方法访问异常最终的根源详见 Java异常问题。7.2 自定义异常与空 catch 块自定义异常一般继承 Exception 类或其子类步骤大体为定义异常类继承合适的父类、提供无参/带参构造器、按需重写方法。开发中常见的自定义异常场景包括业务校验失败、参数校验失败等需要携带特定业务语义的错误。空 catch 块是最糟糕的编程例子异常被捕获后没有任何处理或提示将失去关于异常的全部信息成为调试的噩梦。catch 中至少要打印异常信息哪怕是一条输出语句不能将异常信息隐藏。7.3 OOM 能被 try-catch 吗原则上来讲catch 的是 Error 时触发 Error 的执行状态已经无法恢复需要终止线程甚至终止虚拟机这是不应该被应用层捕获的异常。但严格来说 OOM 在特定条件下是可以被 catch 住的需要两个先决条件触发 OOM 的代码是开发者可控的在 try 块中申明对象并申请了大段内存导致触发 OOM。只有满足这两个条件才对 OOM 有控制权。但通常不推荐主动 catch OOM——哪怕在此处 catch 住了App 当前状态也已经处于濒危状态如果不采取措施此时不崩换一个地方也会崩。如果确实 catch 住了 OOM应主动释放一些可控内存、做好内存管理避免后续操作立即再次触发 OOM。八、实战从崩溃日志速查常见异常与错误8.1 Android 上的典型崩溃案例在 Android 开发中异常与错误直接表现为应用崩溃。仓库的 崩溃bug日志总结1 收录了大量真实崩溃日志可与上文分类一一对应java.lang.UnsatisfiedLinkError对应上文 3.3 节找不到 so 库导致崩溃例如播放视频时找不到libijkffmpeg.so。排查思路检查 so 在安装过程中是否丢失、loadLibrary是否使用了正确的 so 文件名并捕获处理、so 架构是否与设备架构一致如在 64-bit 架构下调用 32-bit 的 so。可通过abiFilters armeabi-v7a等配置控制打包的 CPU 类型。java.lang.IllegalStateException对应上文 2.4 节非法状态异常如Cant compress a recycled bitmap——对已经 recycle 的 Bitmap 执行压缩操作。android.content.res.Resources$NotFoundException资源找不到异常。java.lang.IllegalArgumentException参数不匹配异常。java.lang.NullPointerException对应上文 2.5 节空指针异常Android 崩溃占比最高的异常类型。android.view.WindowManager$BadTokenException窗口 token 非法导致的弹窗崩溃。java.lang.ClassCastException对应上文 2.3 节类转化异常。8.2 ANR 不是异常也不是错误值得注意的是ANRApplication Not Responding本身不属于 Error 或 Exception没有异常日志在第三方崩溃日志中也不会有记录。ANR 的排查需要从 ANR 文件、进程堆栈、CPU/IO 使用情况等维度入手具体分析见 ANR治理优化实践。8.3 崩溃日志排查方法论结合 Throwable异常体系 中关于ThrowableAPI 的介绍排查崩溃时通常使用三个核心方法方法作用getCause()返回抛出异常的原因如果 cause 不存在或未知则返回 nullgetMessage()返回异常的消息信息错误性质printStackTrace()将对象的堆栈跟踪输出至错误输出流System.errprintStackTrace()所提供的信息也可以通过getStackTrace()直接访问它返回由栈轨迹中元素构成的数组每个元素表示栈中的一帧元素 0 是栈顶元素是调用序列中的最后一个方法调用即这个 Throwable 被创建和抛出之处数组中的最后一个元素和栈底是调用序列中的第一个方法调用。每个StackTraceElement可以获取类名、源文件名、行号、方法名以及是否为 native 方法等信息。九、异常处理的最佳实践综合 异常处理的流程机制 与 Java异常问题整理出以下实践要点捕获特定异常而非泛化异常尽量不要捕获类似 Exception 这样的通用异常应该捕获特定异常。泛泛的 Exception 会隐藏代码意图还可能捕获到不希望捕获的 RuntimeException。除非深思熟虑否则不要捕获 Throwable 或 Error很难保证能正确处理 OutOfMemoryError。不要生吞异常生吞异常捕获后既不抛出也不记录日志往往基于这段代码可能不会发生的假设但千万不要在产品代码中做这种假设。否则程序可能在后续代码以不可控的方式结束没人能判断究竟哪里抛出了异常。catch 中的代码不要太多printStackTrace()输出到标准错误流STDERR在复杂生产系统中这不是合适的输出选项很难判断输出到哪里去了。不要用异常处理机制代替判断本来应该判 null 的结果使用了异常处理机制来代替这是本末倒置。不要打印堆栈后再抛出异常打印异常后重新抛出调用者可能也打印了异常重复的打印信息会增添排查问题的难度。调用方法返回布尔值代替返回 null避免 NullPointerException。在 finally 块中关闭资源数据库连接、查询、流处理或使用 try-with-resources。恰当使用异常在知道该如何处理的情况下才捕获异常让类库和程序更安全既是为调试做短期投资也是为程序健壮性做长期投资。不要在 for 循环内用异常控制流程利用异常控制代码流程远比条件语句if/else、switch低效因为异常实例的构造需要对栈做快照。结语从 bug/00.常见的异常.md 的速查清单出发本文将其中的 25 种常见异常与 22 种常见错误逐一展开并串联了仓库中 Throwable异常体系、异常处理的流程机制、JVM处理异常解析、finally处理逻辑介绍、异常try-catch性能 等系列笔记以及 崩溃bug日志总结1、崩溃bug日志总结2、崩溃bug日志总结3 中的真实案例。当你在日志中看到某个异常类名时可按本文分类定位其归属异常还是错误、检查还是非检查、触发场景是什么再结合堆栈与异常信息快速锁定代码位置当编写异常处理代码时则可对照最佳实践检查自己的 try-catch 是否得当。异常机制的价值在于把正常做的事儿与出现问题怎么办完全隔离开来让代码读写更加井井有条——前提是你真正理解它。赞分享教程技术博客文档【免费下载链接】YCBlogs技术博客笔记大汇总包括Java基础线程并发数据结构Android技术博客等等常用设计模式常见的算法网络协议知识点部分flutter笔记还包括平时开发中遇到的bug汇总当然也在工作之余收集了大量的面试题长期更新维护并且修正持续完善……开源的文件是markdown格式的转载请注明出处谢谢项目地址https://gitcode.com/gh_mirrors/yc/YCBlogs点击查看免费下载相关推荐openJiuwen DeepSearch 错误码与异常体系深度解析StatusCode 与 Custom*Exception 的工程实践openJiuwen DeepSearch 错误码与异常体系深度解析StatusCode 与 Custom Exception 的工程实践 openJiuwe人工智能大模型AI AgentRAG深度研究搜索引擎后端代码智能体LanguageExt 错误处理核心深入解析 Error 抽象记录类型与 Expected / Exceptional / ManyErrors 错误分类体系LanguageExt 错误处理核心深入解析 Error 抽象记录类型与 Expected / Exceptional / ManyErrors 错误分类体系后端Sass JS API 异常体系深度解析Exception 类的设计与实践指南Sass JS API 异常体系深度解析Exception 类的设计与实践指南 本篇技术指南以 Sass 官方规范仓库gh_mirrors/sa/sass前端上一篇解析医学影像空间谜题dcm2niix 3D裁剪功能的坐标转换陷阱与解决方案下一篇突破宏基因组定量瓶颈CoverM中TPM计算的深度解析与实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/10/10 5:15:14

基于预训练技术的BIM与IoT数据融合及偏差预警算法实战

简介:这份文档面向建筑施工管理、BIM工程与智能建造方向的技术人员及研究者,围绕施工进度管控中数据维度单一、偏差预警滞后等痛点,给出基于DeepSeek预训练技术的BIM与IoT数据融合及偏差预警算法方案。全文共196页、50个大章节,从…

2026/10/10 5:15:14

AI Measurement Science:让AI真正参与物理测量的底层重构

1. 项目概述:当AI真正开始“读数”——这不是算法秀技,而是测量科学的底层重构“AI Measurement Science”这个标题乍看像两个术语的简单拼接,实则藏着一场静默却深刻的范式迁移。我接触过太多团队,把AI当成万能滤镜——图像加个超…

2026/10/10 7:15:20

Codex CLI接入OpenAI兼容接口:config.toml配置与排错

如果你手头有 Codex CLI,又不想只接固定的云上模型,今天这篇文章值得你花五分钟看完。我会把config.toml逐行拆开讲,覆盖接入 OpenAI 兼容接口时的常见报错和排查思路,也算是我这半年反复折腾下来的一份笔记。文章面向两类人&…

2026/10/10 7:15:20

Cursor高效配置四步法:用规则驱动代码减量

1. 项目概述:这不是在教你怎么点开设置,而是在重建你和代码的协作关系“Cursor怎么配置才好用?这套规则让我少写一半代码”——这句话我第一次看到时,手停在键盘上三秒。不是因为夸张,而是太真实。过去两年&#xff0c…

2026/10/10 7:15:20

Claude作为创业决策协作者的实战闭环构建

1. 项目概述:这不是“AI创业课”,而是一份面向真实创业者的协作增强方案“Claude 创业计划扩展至更多创始人”——这个标题乍看像一则新闻通稿,但作为连续三年深度参与多个早期技术型创业项目孵化的从业者,我第一反应是&#xff1…

2026/10/10 7:15:20

用PINN做多变量回归预测:Matlab实现与调参全攻略

用PINN做多变量回归预测,还是多输入单输出,Matlab代码该怎么落地?这个问题我刚接触的时候绕了不少弯路。物理信息神经网络(PINN)这两年讨论度很高,核心其实一句话:让神经网络的预测结果不仅拟合…

2026/10/10 7:15:20

HED边缘检测实战:从VGG16到多任务学习的深度学习流水线

简介:HED_edgeDetect 是一份面向计算机视觉初学者与深度学习实践者的边缘检测资源包,围绕 HED(Hypercolumns for Edge Detection)这一基于卷积神经网络的端到端边缘检测方法展开,可用于理解多尺度特征融合、预训练与微…

2026/10/10 7:10:19

Python+Pillow制作艺术签名生成器:字体渲染与透明PNG实战

我一直在用 Python 做一些小工具,最近琢磨着一个挺有意思的需求——给自己设计一个优雅的艺术签名。平时签文件、写卡片、做水印,总觉得自己手写签名不够好看,或者干脆没有手写习惯,一笔一画下去自己都嫌弃。于是我用 Pillow 做了…

2026/10/8 10:03:18

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/9 20:15:56

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/8 6:05:44

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 0:04:53

从逻辑门到计算机:数字电路核心原理与全加器搭建实战

如果你拆过一台旧电脑的主板,盯着那些黑乎乎的小芯片看上一会儿,可能会冒出同一个疑问:这堆引脚密集的元件,到底是怎么“变”出那么复杂的应用的?答案并不在某个神秘的部件里,而是在所有芯片内部都在反复使…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑