finally
代码块- 自动资源管理
有时在 try-catch
语句中会占用一些非 Java 虚拟机资源,如打开文件、网络连接、打开数据库连接 和 使用数据结果集等,这些资源并非 Kotlin 资源,不能通过 Java 虚拟机的垃圾收集器回收,需要程序员释放。为了确保这些资源能够被释放,可以使用 finally
代码块 或 自动资源管理
技术。
一、finally 代码块
try-catch
语句后面还可以跟一个 finally
代码块,try-catch-finally
语句语法如下:
try {
// 可能会发生异常的代码
} catch (e1: Throwable) {
// 捕获到异常的处理
} catch (e2: Throwable) {
// 捕获到异常的处理
} catch (en: Throwable) {
// 捕获到异常的处理
} finally {
// 释放资源
}
无论 try
正常结束还是 catch
异常结束都会执行 finally
代码块。
fun main(args: Array<String>?) {
val date = readDate()
println("读取的日期 = $date")
}
private fun readDate(): Date? {
var fileIs: FileInputStream? = null
var isr: InputStreamReader? = null
var br: BufferedReader? = null
try {
fileIs = FileInputStream("readme.txt")
isr = InputStreamReader(fileIs)
br = BufferedReader(isr)
// 读取文件中的一行数据
val str = br.readLine() ?: return null
val df = SimpleDateFormat("yyyy-MM-dd")
return df.parse(str)
} catch (e: FileNotFoundException) {
println("处理 FileNotFoundException ...")
e.printStackTrace()
} catch (e: IOException) {
println("处理 IOException ...")
e.printStackTrace()
} catch (e: ParseException) {
println("处理 ParseException ...")
e.printStackTrace()
} finally {
try {
fileIs?.close()
} catch (e: IOException) {
e.printStackTrace()
}
try {
isr?.close()
} catch (e: IOException) {
e.printStackTrace()
}
try {
br?.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
return null
}
注意:为了使代码简洁,可能会有人将 finally
代码中的多个嵌套的 try-catch
语句合并,例如将上述代码改成如下形式,将三个有可能发生异常的 close
函数放到一个 try-catch
中。这种处理并不稳妥,因为每个 close
函数对应关闭一个资源,如果第一个 close
函数关闭时发生异常,那么后面的两个也不会关闭,因此如下的程序代码是有缺陷的。
try {
...
} catch (e: FileNotFoundException) {
...
} catch (e: IOException) {
...
} catch (e: ParseException) {
...
} finally {
try {
fileIs?.close()
isr?.close()
br?.close()
} catch(e: IOException) {
e.printStackTrace()
}
}
二、自动资源管理
使用 finally
代码块释放资源会导致程序代码大量增加,一个 finally
代码块往往比正常执行的程序还要多。在 Kotlin 中使用 Java7 之后提供的自动资源管理技术,可以替换 finally
代码块,优化代码结构,提高程序可读性。
private fun readDate(): Date? {
try {
FileInputStream("readme.txt").use {
InputStreamReader(it).use {
BufferedReader(it).use {
// 读取文件中的一行数据
val str = it.readLine() ?: return null
val df = SimpleDateFormat("yyyy-MM-dd")
return df.parse(str)
}
}
}
} catch (e: FileNotFoundException) {
println("处理 FileNotFoundException ...")
e.printStackTrace()
} catch (e: IOException) {
println("处理 IOException ...")
e.printStackTrace()
} catch (e: ParseException) {
println("处理 ParseException ...")
e.printStackTrace()
}
return null
}
调用输入流 use
函数进行嵌套,这就是自动资源管理技术。采用了自动资源管理后不再需要 finally
代码块,不需要自己关闭这些资源,释放过程交给了 Java 虚拟机
注意:所有可以自动管理的资源需要实现 Java 中的AutoCloseable接口,上述代码中三个输入流 FileInputStream
、InputStreamReader
和 BufferedReader
都实现了 Java 中的 AutoCloseable
接口,这些资源对象都可以使用 use
函数管理资源。