本文作者:一痕
基础知识
Java 异样
异样层次结构
在 Java 中,异样明确的分为两种:Checked Exception
和 Unchecked Exception
。下图中的红色局部示意 Unchecked Exception
异样,蓝色的示意 Checked Exception
。结构图如下:
Checked Exception
Checked Exception
必须被显式地捕捉或者传递, 否则在编译期就会显示的报错。
一般而言,Checked Exception
指的都是不受程序间接管制的谬误。它们通常都是因为与内部资源、网络交互而产生的,例如数据库问题、网络连接谬误、文件失落等问题。
Checked Exception
常是 Exception 类的子类。
Checked Exception
的例子如:ClassNotFoundException
、IOException
、SQLException
等。
Unchecked Exception
Unchecked Exception
即开发者不用显示的捕捉或传递而在编译期是不会报错的。
编译器不会强制要求应用方对 Unchecked Exception
进行显示的捕获。
Unchecked Exception
:
RuntimeException
的子类。eg:NullPointerException
、AritheticException
、ArrayStoreException
、ClassCastException
等。Error
的子类。eg:StackOverflowError
、OutOfMemoryError
等。
Kotlin 异样
Kotlin 的所有异样类都是 Throwable 类的子孙类,这点和 Java 相似,然而 Kotlin 中没有 Checked Exception
,所以 Kotlin 中所有的 Exception 都是 Unchecked Exception
,也就意味着编译器不会强制捕捉任何异样。
背景
- 问题一:在什么状况下适宜抛出异样?
平时大家在开发时,会有一些在执行逻辑或者参数不合乎预期时,会间接抛出一个异样,如下代码比拟常见:
public boolean open(xxx) {if (xxx) {xxxx} else {throw new IllegalArgumentException("xxxx");
}
return xxxx;
}
如果代码执行匹配上了异样逻辑,在运行时调用方没有捕获相应的异样,利用就会间接解体,对用户造成不敌对的体验。
- 问题二:捕获异样的代码应该如何写?
咱们平时开发时,对于须要捕获异样的场景,咱们又该如何标准的书写呢?比方上面的代码写的正当吗?
try {xxxx} catch (Throwable e) {e.printStackTrace();
}
-
问题三:
Kotlin
和Java
混合开发的问题Java
和Kotlin
在对异样的设计理念就有差别,所以在互调时应该怎么对齐两者的差别?最大的差别是Kotlin
没有Checked Exception
这个概念,这样在我的项目应用 Kotlin 和 Java 混合开发时就会存在一些争议性的问题:- 捕获异样争议
在 Kotlin 调用 Java 代码时,如果 Java 抛出了
Checked Exception
,Kotlin 应该被动捕获还是不被动捕获?咱们第一反馈是应该捕获,既然要捕获然而在开发阶段 ide 又不会给予显示的提醒,并且不捕获在编译期又不会报错。- 抛异样争议
Kotlin 在须要抛出异样的场景应该怎么写?只是把异样抛出来?抛出来,调用者又很难感知到异样,所以就存在代码命中相干异样解体的危险。
因为以上各种问题的存在,在认知层面所有开发者未达成统一的状况下,也就会存在 code review 时规范不统一,不标准的应用异样也会导致更多的线上解体,并且业内也没有一套比拟可行的规范能间接应用,所以咱们不得不针对这些问题制订一套卓有成效的规定和流程来解决这些问题。
解决办法
对于 Java 和 Kotlin 异样的不统一,咱们基于代码品质思考,抉择对齐 Java 的代码标准,所以 Kotlin 侧咱们就定义相似 `
Checked Exception` 概念,对于须要显示提醒进去的能力,借助 Lint 的能力实现(Kotlin 编译器不会强制捕捉任何异样)。
对于抛出异样,明确规定下层业务调用者不容许抛出异样,仅 API 提供方在不得不抛出异样的场景,才容许抛出异样,并且得抛出 Checked Exception
。
对于捕捉异样,原则上捕捉是为了解决它,应该加上必要的解决逻辑,在捕捉只是为了兜底的场景(可能会产生解体)提供对异样上报的工具类。其余应用规定对齐业内的规范。
最初对于所有制订的规定,提供 Lint 检测能力,在 MR 流程中进行卡点,保障代码的正确性。
接下来介绍下云音乐对 Java 与 Kotlin 异样应用标准。
标准
Java 标准
抛出异样(throw Exception)
-
最顶层的调用者防止抛出异样;
阐明:最顶层的调用者如果抛出异样,在逻辑命中的状况下,app 会间接解体;
- 对于须要抛出异样的场景,防止间接抛出
Unchecked Exception
(RuntimeException 子类,使用者无奈显示感知,未做捕捉解决,容易呈现解体),更不容许间接抛出 Exception 或者 Throwable;抛出的异样应继承 Exception,即Checked Exception
(使用者能显示的感知,即可对其进行解决),应应用有业务含意的自定义异样。举荐业界已定义 过的自定义异样,如:DAOException、ServiceException 等; -
抛出更具体的异样;
阐明:你抛出的异样越具体、越明确越好。使用者能够依据具体的异样进行不同的补救措施。因而,你须要确保提供尽可能多的信息,这会使得你的 API 更易于了解。
捕捉异样(try catch)
-
捕捉异样是为了解决它,不要捕捉了却什么都不解决而摈弃之,如果不想解决它,请
将该异样抛给它的调用者。最外层的业务使用者,必须解决异样,将其转化为用户能够了解的
内容。在一些兜底的捕捉逻辑里,须要将相干的异样信息上报。上报相干接口如下 (
Monitor
类):/** * 上报日志信息到异样监控平台 */ void logActiveReport(Throwable throwable);
eg:
try {xxxxs} catch (IOException e) { // 上报后可在异样平台上查问到相干信息 ServiceFacade.get(Monitor::class.java).logActiveReport(...) }
-
catch
时请分清稳固代码和非稳固代码,稳固代码指的是无论如何不会出错的代码。
对于非稳固代码的catch
尽可能进行辨别异样类型,再做对应的异样解决。阐明:对大段代码进行 try-catch,使程序无奈依据不同的异样做出正确的应激反应,也不利于定位问题,这是一种不负责任的体现。
正例:用户注册的场景中,如果用户输出非法字符,或用户名称已存在,或用户输出明码过于
简略,在程序上作出分门别类的判断,并提醒给用户。 -
不要在
finally
块中应用return
。阐明:finally 块中的 return 返回后办法完结执行,不会再返回 try 块中的 return 语句的后果,即返回值被 finally 的返回值笼罩;
-
try catch
只解决利用能解决的异样,不要捕捉Throwable
。阐明:
Throwable
是所有Exceptions
和Errors
的父类。如果你在catch
子句中应用了Throwable
,它将不仅捕捉所有异样,还会捕捉所有谬误。这些谬误是由JVM
抛出的,用来表明不打算由利用解决的严重错误。OutOfMemoryError
和StackOverflowError
就是典型的例子,这两种状况都是由一些超出利用管制范畴的状况导致的,无奈解决。谬误案例:
try {xxxxs} catch (Throwable e) {e.printStackTrace(); }
Kotlin 标准
抛出异样(throw Exception)
-
尽量避免在 kotlin 代码中抛出异样,特地是最顶层的调用者;
阐明:最顶层的调用者如果抛出异样,在逻辑命中的状况下,app 会间接解体;
-
对于须要抛出异样的场景,在代码中抛出异样的同时,须要在办法申明处显示的抛出来(相似
Checked Exception
做法)。举荐业界已定义过的自定义异样,如:DAOException、ServiceException 等;
正确做法:// 显示的抛出来 @Throws(IOException::class) fun xxxx() {throw IOException("xxxx") }
-
抛出更具体的异样;
阐明:你抛出的异样越具体、越明确越好。使用者能够依据具体的异样进行不同的补救措施。因而,你须要确保提供尽可能多的信息,这会使得你的 API 更易于了解。
捕捉异样(try catch)
- 对于代码显示抛出的异样(Java
Checked Exception
和 Kotlin 显示抛出的异样),应该进行相应的捕捉。 -
捕捉异样是为了解决它,不要捕捉了却什么都不解决而摈弃之,如果不想解决它,请
将该异样抛给它的调用者。最外层的业务使用者,必须解决异样,将其转化为用户能够了解的
内容。在一些兜底的捕捉逻辑里,须要将相干的异样信息上报。上报相干接口如下 (
Monitor
类):/** * 上报日志信息到异样平台 */ void logActiveReport(Throwable throwable);
-
catch
时请分清稳固代码和非稳固代码,稳固代码指的是无论如何不会出错的代码。
对于非稳固代码的catch
尽可能进行辨别异样类型,再做对应的异样解决。阐明:对大段代码进行 try-catch,使程序无奈依据不同的异样做出正确的应激反应,也不利于定位问题,这是一种不负责任的体现。
正例:用户注册的场景中,如果用户输出非法字符,或用户名称已存在,或用户输出明码过于
简略,在程序上作出分门别类的判断,并提醒给用户。 -
不要在
finally
块中应用return
。阐明:finally 块中的 return 返回后办法完结执行,不会再返回 try 块中的 return 语句的后果,即返回值被 finally 的返回值笼罩;
总结
- 比照业内对 Java 和 Kotlin 的相干的代码标准,咱们定义的会更加严格,业内的标准根本是对代码应用的一个领导意见,咱们定义的代码标准更多是从保障 app 品质角度登程的代码编写准则。
- 目前业内对
Checked Exception
的争执始终未平息,Kotlin 作者的观点是本就不该呈现Checked Exception
,然而Checked Exception
的确对于咱们的代码质量保证有肯定的价值。本文也未对这块的观点进行探讨,咱们抉择 Kotlin 对齐Checked Exception
也是出于对立 Java 和 Kotlin 的异样标准,晋升 app 的品质角度登程。 -
对于因为代码不标准的应用导致的解体问题,咱们的做法根本都是制订规范加上相应的 Lint 来解决相应的问题。Kotlin 相干的代码标准目前业内还没有一套比拟权威的标准,所以在大家应用 Kotlin 时须要更加的关注其潜在的一些代码问题导致的品质问题。
参考资料
- Checked or Unchecked Exceptions?
- Kotlin Exceptions
- 阿里巴巴 Java 开发手册
- Java’s checked exceptions were a mistake
- The Trouble with Checked Exceptions
本文公布自 网易云音乐大前端团队,文章未经受权禁止任何模式的转载。咱们长年招收前端、iOS、Android,如果你筹备换工作,又恰好喜爱云音乐,那就退出咱们 grp.music-fe(at)corp.netease.com!