Kotlin 异常处置惩罚

打印 上一主题 下一主题

主题 578|帖子 578|积分 1734

什么是异常

我们在运行程序时,如果代码出现了语法题目或逻辑题目,会导致程序编译失败或退出,称为异常。运行效果会给出一个一长串的赤色字,通常会给出异常信息(异常名、调用堆栈等)。
语法错误会直接导致编译失败,不能被代码捕获;而逻辑异常一样平常是在运行时抛出的,可以捕获。
语法错误:
  1. fun main() {
  2.     print("Hello Kotlin"
  3. }
复制代码
  1. // 异常信息(部分)
  2. e: file:///D:/Project/Kotlin/Normal/untitled/src/main/kotlin/Main.kt:2:25 Expecting ')'
  3. FAILURE: Build failed with an exception.
  4. * What went wrong:
  5. Execution failed for task ':compileKotlin'.
  6. > A failure occurred while executing org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction
  7.    > Compilation error. See log for more details
复制代码
逻辑错误:
  1. fun main() {
  2.     val list = listOf(1, 2, 3)
  3.     print(list[3])
  4. }
复制代码
  1. Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
  2.         at java.base/java.util.Arrays$ArrayList.get(Arrays.java:4266)
  3.         at MainKt.main(Main.kt:4)
  4.         at MainKt.main(Main.kt)
复制代码
每一个异常都是一个Throwable的对象,这是一个 Java 的类。
  1. public class Throwable implements Serializable {
  2.         ...
  3. }
复制代码
实在,Kotlin 中的异常来自于 Java 异常,而且使用typealias起了别名。
  1. // Typealias.kt
  2. package kotlin
  3. @SinceKotlin("1.1") public actual typealias Error = java.lang.Error
  4. @SinceKotlin("1.1") public actual typealias Exception = java.lang.Exception
  5. ...
复制代码
  Note:Kotlin 是支持跨平台的,而且有相应的分支,Kotlin/Jvm(桌面端、安卓Android)、Kotlin/Native(原生平台,如苹果 iOS)、Kotlin/JS(网页端 JavaScript)、Kotlin/Wasm(网页端 Assembly)。
Kotlin 跨平台项目有expect和actual关键字,分别用于声明全平台期望内容(expect)以及各个平台的实现内容(actual)。以上的代码前方都标志了actual,说明这是平台代码,各个平台之间代码会有所差别,Jvm 平台有 Java,而其他平台没有。
  从源码中可以发现,并没有Throwable的别名,Kotlin 创建了一个自己的Throwable对象。应该是为了跨平台兼容,其他平台没有 Java 也就没有java.lang.Throwable(其他平台应该是继承自kotlin.Throwable),在现实使用中我们用的是kotlin.Throwable。
  1. // Throwable.kt
  2. package kotlin
  3. public open class Throwable(open val message: String?, open val cause: Throwable?) {
  4.     constructor(message: String?) : this(message, null)
  5.     constructor(cause: Throwable?) : this(cause?.toString(), cause)
  6.     constructor() : this(null, null)
  7. }
复制代码
抛出异常

我们可以使用throw抛出一个Throwable(不能是java.lang.Throwable),我们可以实例化Throwable或其子类,使用throw将其抛出,后续代码将不再执行。
  1. fun main() {
  2.     throw Throwable("你好,异常")
  3.     print("Hello Kotlin")
  4. }
复制代码
  1. Exception in thread "main" java.lang.Throwable: 你好,异常
  2.         at MainKt.main(Main.kt:2)
  3.         at MainKt.main(Main.kt)
复制代码
通过异常信息办理异常

异常信息(赤色字)是我们办理异常的关键。我们可以从异常信息中找到异常的名称以及抛出位置。
通常情况下,异常信息会非常长,异常信息的格式实在是固定的,异常名称、信息message值和抛出位置会在开头处,我们可以看一下下方的异常信息。
首先在开头处我们便可以得知在main这一线程(thread)中出现了一个异常(Exception in thread "main"),而且在后边跟着它的名称java.lang.Throwable,冒号:背面是异常的message值,如果message是null,则不会有冒号及message的内容。
一样平常情况下,你只需要搜索第一行的信息,便会找到办理办法。固然,为了与Java 区分开,你可以在信息前加 Kotlin。
后边的就是调用堆栈,在这里可以找到异常抛出处,我们要留意那些蓝色字体(在 IDEA 中,Main.kt:2是蓝色的。Main.kt表示文件名;2表示行数,有时候会对不上),点击它光标会定位到该位置。越靠前的,就越靠近异常抛出位置。
有些时候,在中央位置也会出现类似Cause by ...的内容,也需要特别留意,可以像搜索第一行一样搜索它。
  1. fun main() {
  2.     throw Throwable("抛出异常")
  3. }
复制代码
  1. Exception in thread "main" java.lang.Throwable: 抛出异常
  2.         at MainKt.main(Main.kt:2)
  3.         at MainKt.main(Main.kt)
复制代码
捕获异常

有些情况下,我们并不希望异常直接抛出,比方,在安卓 Android 应用中,抛出异常将直接导致应用闪退,这黑白常不好的体验。我们可以使用try-catch-finall来捕获异常。
finall可以缺省,我们先从简单的try-catch讲起。我们在try的花括号{}里可以写一段要执行的可能发生异常的代码,在后边补上catch,小括号()中需要写一个变量名称: Throwable 或其子类,表示你要捕获的异常,接着在catch花括号{}里写上捕获异常后要做什么,这里我们打印异常的消息,这是一个String?范例。
  1. fun main() {
  2.     try {
  3.         // 这是一段要执行的代码
  4.         println("try 1")
  5.         throw Exception("异常消息")
  6.         print("try 2")
  7.     }catch (e: Exception) {
  8.         // 如果上方代码抛出了异常
  9.         // 则该地方可以尝试捕获异常
  10.         print(e.message)
  11.     }
  12. }
复制代码
  1. try 1
  2. 异常消息
复制代码
可以看到效果中没有异常了。如果我们去掉异常抛出语句,try中的内容会顺遂执行,而且不会执行catch的内容:
  1. fun main() {
  2.     try {
  3.         // 这是一段要执行的代码
  4.         println("try 1")
  5.         // throw Exception("异常消息")
  6.         print("try 2")
  7.     }catch (e: Exception) {
  8.         // 如果上方代码抛出了异常
  9.         // 则该地方可以尝试捕获异常
  10.         print(e.message)
  11.     }
  12. }
复制代码
  1. try 1
  2. try 2
复制代码
这里需要留意,如果抛出的异常与catch圆括号中的范例不是同一类(不一定要范例雷同,抛出异常也可以是圆括号中范例的子类),会捕获失败,抛出异常。这里举一个我不停以来都犯的错误,我会习惯性地把e的范例给成Exception,可是Throwable大抵是分为两大类的:Exception、Error。我的声明e: Exception只对Exception及其子类见效,如果某一次它抛出了Error,将会捕获失败。
  1. import java.awt.AWTError
  2. fun main() {
  3.     try {
  4.         // AWTError: Error
  5.         throw AWTError("AWT 错误
  6. ")
  7.     }catch (e: Exception) {
  8.         print(e.message)
  9.     }
  10. }
复制代码
  1. Exception in thread "main" java.awt.AWTError: AWT 错误
  2.         at MainKt.main(Main.kt:6)
  3.         at MainKt.main(Main.kt)
复制代码
所以发起大家,还是把它的范例写为Throwable:
  1. fun main() {
  2.     try {
  3.         // AWTError: Error
  4.         throw AWTError("AWT 错误
  5. ")
  6.     }catch (e: Throwable) {
  7.         print(e.message)
  8.     }
  9. }
复制代码
  1. AWT 错误
复制代码
最后是finally,它不管是否抛出异常,都会执行相应的代码:
  1. // 将 block 函数交给 catching 函数调用
  2. inline fun catching(block: () -> Unit) {
  3.     try {
  4.         // 调用 block 函数
  5.         block()
  6.     } catch (_: Throwable) {
  7.         // 空语句需要将 e 命名为 _
  8.     } finally {
  9.         println("Finally")
  10.     }
  11. }
  12. fun main() {
  13.     // 抛出异常
  14.     catching { throw Throwable() }
  15.     // 不抛出异常
  16.     catching {  }
  17. }
复制代码
  1. Finally
  2. Finally
复制代码
try-catch-finally还能用于赋值,这与if 或when类似:
  1. fun main() {
  2.     val msg = try {
  3.         throw Throwable("抛出异常")
  4.         "顺利执行"
  5.     }catch (e: Throwable) {
  6.         "出现异常
  7. "
  8.     }
  9.     print(msg)
  10. }
复制代码
  1. 出现异常
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

勿忘初心做自己

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表