为什么不建议用 try catch?

2019-11-16 12:17:38 +08:00
 wysnylc

不问是不是,就问为什么。这个问题看来需要从头说起。

一句话解释: try catch 机制非常好。那些觉得 try catch 不行的人,是他们自己的水平有问题,无法理解这种机制。并且这群人写代码不遵守规则,喜欢偷懒,这才造成 try catch 不好的错觉。

详细解释: 1.程序要健壮,必须要设计报错机制。 最古老,也是最常见的,比如: bool CreateFile( ); //如果创建文件失败就返回 false,否则返回 true。 这种报错方式,显然不好。因为它没有给出产生错误的具体原因。

2.改进:一个函数或过程,会因为不同的原因产生错误,报错机制必须要把这些错误原因进行区分后,再汇报。 比如: int CreateFile(): //如果创建成功就返回 1. //如果是因为没有权限,导致失败,返回-1。 //如果是因为文件已经存在,导致失败,返回-2。 //如果是因为创建文件发生超时,导致失败,返回-3。 这样看上去,比 [ 1 ] 要好些,至少指出了比较具体的失败原因,但是,还不够。

3.很多情况下,函数需要把详细的原因,用字符串的方式,返回: class Result { ....int State;//同 [ 2 ] ....string ErrorMessage;//如果失败,这里将给出详细的信息,如果有可能,应该把建议也写上去。 }

Result CreateFile(); //如果创建成功,返回的 Result,State 为 1,ErrorMessage 为 null。 //如果是因为没有权限,导致失败,返回的 Result,State 为-1,ErrorMessage 为"用户 [ guest ] 没有权限在 [ C:] 这个目录下创建该文件。建议您向管理员申请权限,或者更换具有权限的用户。"。 //如果是因为文件已经存在,导致失败,返回的 Result,State 为-2,ErrorMessage 为"文件 [ C:\abc.txt ] 已经存在。如果需要覆盖,请添加参数:arg_overwrite = true"。 //如果是因为创建文件发生超时,导致失败,返回的 Result,State 为-3,ErrorMessage 为"在创建文件时超时,请使用 chkdsk 检查文件系统是否存在问题。"。

4.我个人推崇上面这种方式,完整,美观。但是这种流程,容易与正常的代码混在一起,不好区分开。因此,Java、C#等设计了 try catch 这一种特殊的方式: void CreateFile() //如果创建成功就不会抛出异常。 //如果是因为没有权限,导致失败,会抛出 AccessException,这个 Exception 的 Msg 属性为"用户 [ guest ] 没有权限在 [ C:] 这个目录下创建该文件。建议您向管理员申请权限,或者更换具有权限的用户。"。 //如果是因为文件已经存在,导致失败,会抛出 FileExistedException,这个 Exception 的 Msg 属性为"文件 [ C:\abc.txt ] 已经存在。如果需要覆盖,请添加参数:arg_overwrite = true"。 //如果是因为创建文件发生超时,导致失败,会抛出 TimeoutException,这个 Exception 的 Msg 属性为"在创建文件时超时,请使用 chkdsk 检查文件系统是否存在问题。"。

可见,上述机制,实际上是用不同的 Exception 代替了 [ 3 ] 的 State。

这种机制,在外层使用时: try { ....CreateFile( "C:\abc.txt" ); } catch( AccessException e ) { ....//代码进入这里说明发生 [没有权限错误] } catch( FileExistedException e ) { ....//代码进入这里说明发生 [文件已经存在错误] } catch( TimeoutException e ) { ....//代码进入这里说明发生 [超时错误] } 对比一下 [ 3 ] ,其实这与 [ 3 ] 本质相同,只是写法不同而已。

5.综上,我个人喜欢 [ 3 ] 这类面向过程的写法。但很多喜欢面向对象的朋友,估计更喜欢 [ 4 ] 的写法。然而 [ 3 ] 与 [ 4 ] 都一样。这两种机制都是优秀的错误处理机制。

6.理论说完了,回到正题,题注问:为什么不用 try catch ? 答:这是因为,很多菜鸟,以及新手,他们是这样写代码的: void CreateFile( ) //无论遇到什么错误,就抛一个 Exception,并且也不给出 Msg 信息。 这样的话,在外层只能使用: try { ....CreateFile( "C:\abc.txt" ); } catch( Exception e ) { ....//代码进入这里说明发生错误 } 当出错后,只知道它出错了,并不知道是什么原因导致错误。这同 [ 1 ] 。

以及,即使 CreateFile 是按 [ 4 ] 的规则设计的,但菜鸟在外层是这样使用的: try { ....CreateFile( "C:\abc.txt" ); } catch( Exception e ) { ....//代码进入这里说明发生错误 ....throw Exception( "发生错误" ) } 这种情况下,如果这位菜鸟的同事,调用了这段代码,或者用户看到这个错误信息,也只能知道发生了错误,但并不清楚错误的原因。这与 [ 1 ] 是相同的。

出于这些原因,菜鸟的同事,以及用户,并没有想到,造成这个问题是原因菜鸟的水平太差,写代码图简单省事。他们却以为是 try catch 机制不行。

因此,这就导致了二逼同事,以及傻比用户,不建议用 try catch。 原文地址:https://www.zhihu.com/question/29459586

13320 次点击
所在节点    Java
78 条回复
ipwx
2019-11-16 17:59:18 +08:00
那是框架设计的烂吧
WenhaoWu
2019-11-16 18:03:42 +08:00
偏爱 RxJava 的是什么流派? eg.
Flowable.error("demo").subscriber(
res -> {},
err -> { //handle error }
)
dany813
2019-11-16 18:05:26 +08:00
友军
hehheh
2019-11-16 18:13:40 +08:00
我不太清楚 Java,就 python c++而言,try catch 有时候偷懒不挺好用的么?如果底层扔错的代码包的太深,需要检测逻辑的部分又在最外层,一层一层返回错误也太繁琐了。不如直接在外面 try catch。。。
wly19960911
2019-11-16 18:23:33 +08:00
@WenhaoWu rxJava 的 catch 和 trycatch 没什么区别,讨论的核心还是要不要 catch。是我 do/tap/map 里面判断掉然后 filter 还是用 throw Error,来进行流程控制。

我感觉会 Rx Java 就没必要参与这个讨论了…Rx Java 本来就是一套流程控制库,充分使用自然知道会使用自己认为更好的策略
zengming00
2019-11-16 18:34:49 +08:00
该用就得用,没什么好不好
wysnylc
2019-11-16 18:39:17 +08:00
@hehheh #44 所以我认为坚持不使用 try-catch 的人要么是新手没写过复杂的多层的逻辑或者根本没有分工合作的经验,肯定没写过自定义异常和尝试设计框架
wysnylc
2019-11-16 18:40:36 +08:00
@wly19960911 #45 可以放弃 rxjava 了,java8 有 completablefuture,java9 有 flow 已经完全可以代替 rxjava
xiangyuecn
2019-11-16 18:50:25 +08:00
沙雕一样的 Runtime Exception,有没有有同感的😏😏😏

入门时以为二进制世界里面非常单纯的不是 True 就是 False,但存在 Optional 这坨,你不去观察一下始终不可能知道它处在什么状态下,不上全局 try catch 就等着某条线程崩溃连累整个应用 crash 吧😂😂
wly19960911
2019-11-16 19:01:39 +08:00
@wysnylc 但是我是 rxjs (跑

不过到时候看看 flow 是什么情况,毕竟我也用 Java
wysnylc
2019-11-16 19:01:47 +08:00
@xiangyuecn #49 二进制只是构成世界的基本结构,但是从人类的角度看一个事务的结果不可能只有 true 和 false 的
所以单纯的用 0 和 1 去理解世界就是自讨苦吃,刚入门的我也苦恼过好久
wysnylc
2019-11-16 19:03:37 +08:00
@wly19960911 #50 当初学 scala,结果 java 把 stream 和 lambda 偷学了
你现在学 rxjava,你看 java 不偷(狗头
如果 java 不偷,那说明不好用,哈哈啊哈
bumz
2019-11-16 20:53:13 +08:00
@kidlj 搜不到这篇文章,能麻烦提供地址吗
anonymous256
2019-11-16 21:07:54 +08:00
@lcdtyph #33 你说的是没错,非预期的是不应该被捕获。
但是如果一个程序的构成很大,由多个子模块构成,又不能 100%保证每个组件都不发生没有预料的错误。尤其公司的开发水平不齐,难保有的人写得组件会发生崩溃。

但是你又希望:如果当某个组件崩溃,而程序不会发生整体性崩溃, 只是单个组件崩溃。这时候需要在组件的外层捕捉全部的异常,并且忽略掉非预期的错误,写入到日志。python 的话, try-except 就可以了; golang 的话, 要用 recover, 都能在外层捕捉到全部; java 我就不清楚了。 比较拗口, 可能不太明白我在说什么。
kidlj
2019-11-16 21:20:49 +08:00
liufish
2019-11-16 22:06:28 +08:00
给楼主手动点赞
mondeo
2019-11-16 22:36:48 +08:00
err != nil
Varobjs
2019-11-16 22:51:21 +08:00
catch 没问题,就怕有人 catch 住了瞎搞
newtype0092
2019-11-16 23:02:45 +08:00
看了楼上关于 golang 方式和 java exception 方式的纠结,建议直接融合:
```
try(result) = getResult();
...

catch(result has someException) {
return null;
}
```
wysnylc
2019-11-17 00:24:40 +08:00
@newtype0092 #59 不建议 return null,会有空指针引用问题.检查空指针也不应当使用捕获异常的方式检查

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/620152

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX