Android中的错误和异常

99 阅读2分钟
  • java.lang.Throwable是Java中所有异常(Exception)和错误(Error)的基类。
java.lang.Object
  └── java.lang.Throwable
        ├── java.lang.Error
        └── java.lang.Exception
  • JVM运行时仅会抛出Throwable及其子类。
  • 编译检查机制:
    • Checked Exception:
      • 除了RuntimeException和Error外的Throwable子类都是为受检异常(Checked exception)
      • 调用者必须显示声明/捕获,在方法中用throws声明或者使用try-catch捕获。例如:IOExceptionClassNotFoundExceptionSQLException
    • Unchecked Exception:
      • 编译阶段不强制检测和处理。包含RuntimeException及子类,Error及子类。
  • 在日志中搜索Crash,可以搜索:FATAL EXCEPTION

public void classDemo() {
    try {
        Class.forName("com.example.NonExistentClass");
    } catch (ClassNotFoundException e) {
        System.out.println("Class not found: " + e.getMessage());
    }
}

public void sqlDemo() {
    try (Connection conn = DriverManager.getConnection(url, user, password)) {
        System.out.println("Connected to the database.");
    } catch (SQLException e) {
        System.out.println("Database connection failed: " + e.getMessage());
    }
}

对于Kotlin

fun fileDemo() {
    try {
        BufferedReader(FileReader("file.txt")).use { br ->
            println(br.readLine())
        }
    } catch (e: IOException) {
        e.printStackTrace()
    }
}

fun sqlDemo() {
    val url = "jdbc:mysql://localhost:3306/testdb"
    val user = "root"
    val password = "password"

    try {
        // 不需要显式关闭连接conn.close()了
        DriverManager.getConnection(url, user, password).use { conn ->
            println("Connection established: $conn")
        }
    } catch (e: SQLException) {
        println("Database connection error: ${e.message}")
    }
}

最佳实践

  • 避免捕获Throwable,Error

捕获Error会导致掩盖OOM等严重故障,不利于问题定位。

  • 链式抛出

自定义异常的时候,将底层异常作为cause传入,上层捕获者能够获得完整上下文

  • 日志与上报

捕获后打印/上报 statckTrace, message, cause帮助定位,注意不要直接打印,而是用内部工具,避免数据泄漏。

  • 设计自定义异常

继承自RuntimeException或特定业务基类;提供多种构造器,以便支持message、cause、实际的业务数据

如果期望自定义异常为checked exception,则可继承自Exception类;如果期望自定义异常为uncheked exception,则可继承自RuntimeException类。通常业务中定义的异常都是unchecked excpetion,因为异常是根据特定的业务逻辑导致的,通常无法在编译期捕获处理。

// 自定义checked exception
class CustomCheckedException(message: String) : Exception(message) {
    // 可以添加更多的属性和方法
}

// 自定义unchecked exception
class CustomUncheckedException(message: String) : RuntimeException(message) {
    // 可以添加更多的属性和方法
}
  • 早抛出,晚捕获

    • Early Throw, Late catch原则:尽早抛出异常,并延迟捕获异常能够有效处理的地方。早抛出可以避免将错误传播到更复杂的地方。
      • 如果过早的捕获,而不做记录,抛出,则称为“错误吞噬”,可能导致重要问题被忽略。
      • 晚捕获可以收集足够的上下文环境信息。
      • 晚捕获可以在高层结构集中处理异常,避免多处捕获导致代码混乱。
  • 资源管理

始终使用try-with-resources管理可关闭资源。

java.lang.RuntimeException的各种构造方法定义 image.png