V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
wysnylc
V2EX  ›  Java

为什么不建议用 try catch?

  •  
  •   wysnylc · 2019-11-16 12:17:38 +08:00 · 13650 次点击
    这是一个创建于 1866 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    一句话解释: 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

    78 条回复    2023-12-21 11:03:18 +08:00
    NOspy
        1
    NOspy  
       2019-11-16 13:17:53 +08:00   ❤️ 10
    原来是友军。
    iwtbauh
        2
    iwtbauh  
       2019-11-16 14:22:07 +08:00 via Android
    3, 4 并不好

    因为这种设计将错误处理和本地化这两个无关的模块耦合在了一起。

    不应该直接返回人类可读的错误信息。
    guyeu
        3
    guyeu  
       2019-11-16 14:31:51 +08:00
    了解一下 go 语言
    result, error := func()
    if error != nil {
    ...
    }
    wysnylc
        4
    wysnylc  
    OP
       2019-11-16 15:05:55 +08:00
    @iwtbauh #2 不应该直接返回人类可读的错误信息? 应该返回外星人可读的错误信息??????满脑壳的疑问??????????????
    @guyeu #3 不同语言不予置评,文中已经说的很明白 try-catch 的作用和意义
    qdwang
        5
    qdwang  
       2019-11-16 15:09:23 +08:00 via iPhone
    try catch 没啥好的,真的好的是把错误放到类型里。
    wysnylc
        6
    wysnylc  
    OP
       2019-11-16 15:20:46 +08:00
    @qdwang #5 A 方法只需要返回一个 int 类型的数值,正常用 try-catch 设计不需要改变方法的结构入参返回值该怎么样怎么样.
    但是如果把错误放到返回值中,那么 int 肯定是必须改,又因为需要详细的错误 code 和 message 那么此时返回值会变成一个含有 code,message,result(正常结果)的对象或者 map
    请问你的项目里到处充斥着这种接口,可读性强吗?美观么?每一个错误都重新定义 code 吗?
    wysnylc
        7
    wysnylc  
    OP
       2019-11-16 15:21:36 +08:00
    @NOspy #1 真的有人把错误信息完完全全的塞在返回值里,没救
    zunceng
        8
    zunceng  
       2019-11-16 15:26:18 +08:00
    @guyeu go 也不完美 1.13 上 给出的 Wrap 和 Unwrap 用对了 才能解决题主说的后面几个点

    归根结底还是在人
    wysnylc
        9
    wysnylc  
    OP
       2019-11-16 15:28:11 +08:00
    @zunceng #8 go 的异常处理被多少人诟病了,都不是性能问题,是设计问题!
    HanMeiM
        10
    HanMeiM  
       2019-11-16 15:30:51 +08:00
    try catch 挺好的啊,不用把服务层的 false 一路传回应用层,也不用在返回结构体里面定义错误信息
    guyeu
        11
    guyeu  
       2019-11-16 15:37:46 +08:00   ❤️ 1
    @wysnylc #4
    @zunceng #8
    java 的异常设计优势是让程序的各种无法预期的错误可以不影响代码结构,这一点比楼主提的 2 和 3 都好。go 的设计也是为了这个目标,但是实现得很愚蠢,java 里可以很容易得把异常抛给上层去处理。
    Wrap 和 Unwrap 其实很大程度上是 java 的 Cause by 和 Supressed 的翻版。
    就现在而言,个人认为 java 的这个异常方案可以说是最接近最优解的了,然而无论在哪里,处理异常只要把握住以下两点就可以了:
    1. 不要丢失信息,包括异常树和异常堆栈;
    2. 简洁,不要侵犯正常逻辑。
    wysnylc
        12
    wysnylc  
    OP
       2019-11-16 15:42:05 +08:00
    @guyeu #11 我有点觉得这些不用 try-catch 是假 javaer,不用 exception 处理异常流程简直没法写代码,用状态码会污染正常的方法结构不用状态码又没法告知上一层我出异常了,他们怎么写代码的?天天只写 void 的吗?
    HongJay
        13
    HongJay  
       2019-11-16 15:47:57 +08:00
    try catch 导致潜在的问题不被暴露出来。我是觉得应该具体情况具体分析,应不应该用。

    同意知乎回答里面的一个答主。

    只有错误可预知时才用 try。所有不可预知的错误用 try 都是不负责任的写法。
    ClericPy
        14
    ClericPy  
       2019-11-16 15:48:49 +08:00
    一开始以为讲 LBYL 与 EAFP... 蒙着头看一大坨没有换行的代码...
    结果结尾一句

    因此,这就导致了二逼同事,以及傻比用户,不建议用 try catch。

    差点闪了我的腰啊... 这排版还不如把原文地址放第一行上
    kidlj
        15
    kidlj  
       2019-11-16 15:51:35 +08:00 via iPhone
    Go 社区经常引用的一篇文章:try-catch is elegant, but is wrong. 建议了解一下。
    wysnylc
        16
    wysnylc  
    OP
       2019-11-16 15:53:11 +08:00
    @HongJay #13 错误都可预知了还要用 try 干嘛?你没看这个回复的评论吗.
    @ClericPy #14 只是方便看原文而已,我放上面你样会跳出来杠"原文都有了你还复制一堆出来干嘛",所以你只是为了杠而已
    ClericPy
        17
    ClericPy  
       2019-11-16 15:54:54 +08:00
    @wysnylc #16 这不是害我看了几十行不换行代码的理由... 你觉得被杠了那我撤了
    wysnylc
        18
    wysnylc  
    OP
       2019-11-16 15:57:20 +08:00
    @kidlj #15 能不能直接发表你的看法?
    scriptB0y
        19
    scriptB0y  
       2019-11-16 16:00:29 +08:00   ❤️ 1
    同事喜欢函数体进来就是一个 try { } catch (Exception e) { return null; } ,函数内检查每一个变量是不是 null。还说这是程序的健壮性。。。真是说服不了他。

    有一次我写了个 throw Exception,把他吓得要命。
    sagaxu
        20
    sagaxu  
       2019-11-16 16:04:43 +08:00 via Android
    checked exception 和 unchecked exception 还能再打一战
    wysnylc
        21
    wysnylc  
    OP
       2019-11-16 16:06:17 +08:00
    @scriptB0y #19 try-catch 是不建议滥用的例如用 try 包裹整个方法中的代码,但是这不是不用 try-catch 的接口不能因噎废食
    这种 null 的检查 java8 中用 optional
    工具是给人使用的不是放在头顶当神明敬畏的
    kidlj
        22
    kidlj  
       2019-11-16 16:12:02 +08:00 via iPhone
    简单来说,Go 这种叫做 explicit error handling,try-catch 是 implicit error handling. 上面提到的那篇文章有对两种方式的利弊对比。当然,Go 当前提供的处理方式还有些简陋,但我认为方向是对的。这也是 Go 一贯的做事方式,再找到完整的解决方案之前,只提供最简单的设施,给设计者和社区足够的时间来找到最佳解决方案。error handling, go modules 以及范型都是这种处理方式。
    wly19960911
        23
    wly19960911  
       2019-11-16 16:13:24 +08:00
    3. 我并不喜欢。徒增难度而已,很多时候错误处理和 throw 的地方跨了好几个调用栈,你这意味着你每个调用栈都可以需要有处理来自 method 的 response。

    我有一个业务是有 http 调用和 sql 调用,还有调用结束之后的 业务报错。 具体是 A -> B -> HTTP, A -> B -> SQL, 这个时候不管是 HTTP 或者 SQL 还是 B 里面业务逻辑,都可能会异常,但是这个异常都必须交给 A 来响应,说实话如果我写了 method response 的话,本身就是给自己增加烦恼,我还要在 B 里面区分 response 然后交给 A,这个流程代码会写的很冗长。
    undeflife
        24
    undeflife  
       2019-11-16 16:15:26 +08:00
    try-catch 有没什么好争论的,使用返回值的问题是所有的引用都需要去判断返回值里是否含有错误信息,并一层层的向外返回,
    java 里的 CheckException 是很好的设计,接口可以明确的告诉调用者有哪些问 Exeption 你需要处理,调用者也选择不处理直接 throw,可惜的是被滥用,甚至再 jdk 自身也有滥用的情况,才导致被人一直被人诟病,一度有人提出去掉所有的 Check Exception 只保留 RuntimeException ..
    wysnylc
        25
    wysnylc  
    OP
       2019-11-16 16:15:38 +08:00
    @wly19960911 #23 同样不喜欢 3,会导致方法的返回值结构完全被污染而且多层之间的定义可能完全不同,同一个标识为 1 的异常在不同层面的含义又不一样需要分开处理和转换
    wysnylc
        26
    wysnylc  
    OP
       2019-11-16 16:16:23 +08:00
    @undeflife #24 Check Exception 在很多时候确实没屁用......
    wysnylc
        27
    wysnylc  
    OP
       2019-11-16 16:16:48 +08:00
    @kidlj #22 java 在这里等着 go 来革命,加油!
    DonaldY
        28
    DonaldY  
       2019-11-16 16:26:54 +08:00
    感觉你想的就是 go 的机制。
    lcdtyph
        29
    lcdtyph  
       2019-11-16 16:27:58 +08:00 via iPhone
    unhappy path 如果出现频率不可忽略的话,catch 段会耗费大量时间啊

    比如某个 api 是接受用户输入的,输入简单处理之后交给内部的库去 parse,如果这个库的 pasererror 是 throw 出来的,那么这个接口只需要很少的代价就可以 dos
    wysnylc
        30
    wysnylc  
    OP
       2019-11-16 16:36:34 +08:00
    @lcdtyph #29
    你想想看,假如没有 try catch,你每调用一次函数,都需要去判断执行结果,判断方式自然是 if else。
    当程序中这些会出错误的函数少还好,但是假设你一段代码中有大量的程序要做这做判断,而且一般都是相关的代码放在一起的。这就意味着后面执行的逻辑会依赖你前面语句的执行情况,也就意味着你每调用一个可能会出现错误的函数的时候,都要判断是否成功,然后再继续执行后面的语句。导致你的这段代码中充斥着大量的 if else。
    更极端一点,假设你的这段充满了 if else 判断的代码封装在某个函数里面,然后外层又有函数调用你这段函数,是否意味着外面这个函数也要去判断异常情况?你的错误可能会使用某个整数来作为错误代码,来表示不同的错误情况,可能会大大影响程序的可读性。而且每一层代码的错误处理都要和你的逻辑代码混在一起,写到最后你自己都会觉得恶心。

    异常机制( try catch )就是用来解决这个问题的。
    异常机制将所有的程序异常的情况和正常执行的代码分离开来,并提供统一的代码去处理不同的异常,而且针对不同类型的异常情况定义了不同的异常类,用于表示不同的异常情况,增加代码可读性。java 还提供了受检异常和非受检异常,受检异常会强制你去写 try catch 去处理异常情况,否则可能导致编译不通过,这对代码的健壮性很有帮助,避免人为的遗漏异常处理。
    lcdtyph
        31
    lcdtyph  
       2019-11-16 16:39:59 +08:00 via iPhone
    @wysnylc
    是的,这算是可读性与性能的 trade-off 吧,我只是想表达,在某些情况下 try catch 带来的优点可能没法掩盖它的问题,取决于需求
    anonymous256
        32
    anonymous256  
       2019-11-16 16:41:23 +08:00   ❤️ 2
    这是两种处理异常的不同思想,一种是使用错误状态码,一种是使用异常。

    前者叫 LBYL (Look Before You Leap) ,中文的说法"三思而后行",在 Golang 很典型,大量的 if == nil.
    后者叫 EAFP (Easier to Ask for Forgiveness than Permission),"宽恕比许可更容易?",在 Python 中最典型。

    虽然 EAFP 也有不好的问题,但是 LBYL 有太多缺陷了:
    1. 降低代码的可读性和清晰度,参考 golang.
    2. 在操作本身,还需要冗余的检查工作。
    3. 程序员可能会省略了必要的检查,导致隐藏的 bug。比如 golang 的 nil,slice out of index 等,会产生代码 bug。
    4. 在执行检查和尝试操作之间,情况可能会发生变化.

    LBYL 这种设计思想, 意味着你总是预见到了所有的错误, 并且在它发生前时, 已经做好了解决方案. 如果你没有预见到,
    那就代码就会 bug 了。但是 EAFP,你也捕捉到非预期的错误,来避免程序整体性的崩溃。

    如果你感兴趣, 看看这篇: https://eli.thegreenplace.net/2008/08/21/robust-exception-handling/
    关于 go 的 ,https://blog.golang.org/errors-are-values
    lcdtyph
        33
    lcdtyph  
       2019-11-16 16:43:58 +08:00 via iPhone
    @anonymous256
    非预期的错误就不应该被捕获,直接 log 然后 abort 掉才对
    wly19960911
        34
    wly19960911  
       2019-11-16 16:44:16 +08:00
    @wysnylc 最主要的是问题还是一些人代码写的烂,不懂写过程和重构。(以下只针对不懂怎么写流程的新手)

    通俗点说,多少人经历过业务逻辑自己来重构的?比如写一个过程,不少人从开始学习就以自己的角度来定义 interface 或者 private method,然后导致的问题就是自己并不是真正把一套流程看清楚然后编写的,比如一个业务流程被分到两个方法里面(当然,更多的还是流程写一个方法不重构的,偶尔写点 Utils 来处理下重复逻辑)。

    再简单点说,一个方法分为业务 /流程代码, 以及数据处理代码。流程代码本来就只需要调用重构后的方法为主和业务判断处理就行了,结果一个方法写的和面条一样。最后连流程都不知道,还祈求这些人能进行流程上的 try catch 这个根本做不到...所以我带人的时候建议他们,别写一些自以为是的封装方法和 interface,写完了流程自己整理一下,然后根据流程重构一下,更别在流程代码里面写一些过长的数据处理。

    这点我尤其要批评以 vue 和 angular 的部分前端开发, 一个组件下来连个有 return 都没有,连自己的流程都不清楚,还怎么控制流程,更不可能去做 try catch,做下来根本没法控制,只能引用更多的状态来控制多个方法的操作。本来不需要的东西结果越写越复杂。

    所以还是强调我第一句,一些人写代码根本没有流程的概念。没有就多写一些面条方法然后自己多看一眼。怎么重构才对。

    前端的话,我建议写方法养成一个习惯,一个只有一个异步调用的代码,分三个阶段,声明主要局部变量和把 this 上的数据 赋值 /浅拷贝 给局部变量,然后中间的所有流程不允许调用 this 上面的 data,然后赋值要等所有的操作再赋值(就像 react )。
    但是比 react 多的步骤是局部变量,原因还是这块的话能对重构友好,首先对 debug 影响少(因为 debug 的时候你总会去看看谁调用了状态),第二是对重构友好,重构瞬间能产生一个没有副作用的 function。这样的话本身写出来的代码能简洁而且极易 debug 和维护。同时如果需要 try catch 也是很容易的。
    wysnylc
        35
    wysnylc  
    OP
       2019-11-16 16:55:59 +08:00
    @lcdtyph #31 32 楼说得非常明确了,希望你多阅读和理解
    @anonymous256 #32 感谢回答和解惑,世界有你更精彩!
    @wly19960911 #34 很多人开发的时候脑子里其实是个单线程,只会思考当前方法怎么写而不会思考上下级调用,兼容性,扩展性,导致代码一团糟,人菜不能怪语言
    lcdtyph
        36
    lcdtyph  
       2019-11-16 17:02:37 +08:00 via iPhone
    @wysnylc
    你还是不能正视 catch 段会带来效率损失这个事实。
    wly19960911
        37
    wly19960911  
       2019-11-16 17:13:07 +08:00
    @wysnylc 其实我这里也没有聊兼容性和扩展性,这个更多需要依赖业务和经验,可惜我也对这块也不是很熟悉。我工作经验还不是很久。

    但是我能充分感受到的是,不用 try catch 最大的原因是因为看了一段代码之后,会开始思考究竟需要再哪个流程 try catch,那个流程怎么进行 try catch。但是往往面临的问题是我自己连 try catch 在哪里都不一定能确定下来

    比如 一个 (流程 A) 里面执行了 (方法 B) ,B 执行了 (方法 C) ,A 结束之后执行 (流程 H),但是这个时候 C 里面 throw 了 error,我需要在 流程 A 里面处理,但是问题是我需要来自 B 的数据。这时候这个代码就写的一塌糊涂。这还是 3 层,如果是 4 层、5 层呢,怎么处理?


    @lcdtyph 在运行效率面前我选择工作效率,如果舍弃了 try catch 还出了性能问题,那么已经是无力挽回了。有流程控制还能想办法优化。
    ragnaroks
        38
    ragnaroks  
       2019-11-16 17:19:38 +08:00
    try-catch 好用但不应该滥用,我觉得 java 有点滥用的趋势,之前写了几个 MC 的插件,到处都是 try-catch
    wysnylc
        39
    wysnylc  
    OP
       2019-11-16 17:21:11 +08:00
    @wly19960911 #37 我已经不怎么想回答"try-catch 会有性能问题"这个问题了,java 从来就不以高效著称要追求极致效率请出门右转 C 语言,代码逻辑一塌糊涂还谈什么运行效率?
    wysnylc
        40
    wysnylc  
    OP
       2019-11-16 17:21:57 +08:00
    @ragnaroks #38 滥用是人的问题,不是 try-catch 的问题,而且不用 try-catch 更加可怕
    ipwx
        41
    ipwx  
       2019-11-16 17:59:18 +08:00 via Android
    那是框架设计的烂吧
    WenhaoWu
        42
    WenhaoWu  
       2019-11-16 18:03:42 +08:00 via Android
    偏爱 RxJava 的是什么流派? eg.
    Flowable.error("demo").subscriber(
    res -> {},
    err -> { //handle error }
    )
    dany813
        43
    dany813  
       2019-11-16 18:05:26 +08:00
    友军
    hehheh
        44
    hehheh  
       2019-11-16 18:13:40 +08:00
    我不太清楚 Java,就 python c++而言,try catch 有时候偷懒不挺好用的么?如果底层扔错的代码包的太深,需要检测逻辑的部分又在最外层,一层一层返回错误也太繁琐了。不如直接在外面 try catch。。。
    wly19960911
        45
    wly19960911  
       2019-11-16 18:23:33 +08:00 via Android
    @WenhaoWu rxJava 的 catch 和 trycatch 没什么区别,讨论的核心还是要不要 catch。是我 do/tap/map 里面判断掉然后 filter 还是用 throw Error,来进行流程控制。

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

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

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

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

    catch(result has someException) {
    return null;
    }
    ```
    wysnylc
        60
    wysnylc  
    OP
       2019-11-17 00:24:40 +08:00
    @newtype0092 #59 不建议 return null,会有空指针引用问题.检查空指针也不应当使用捕获异常的方式检查
    hantsy
        61
    hantsy  
       2019-11-17 00:36:08 +08:00
    @wysnylc Java 8 Future, Java 9 Flow 代替不了 Rxjava。
    1. Java 8 中相关接口,没有 Publisher/Subscriber 模式,没有 Back Pressure 处理能力。而这两种才是 Reactive Stream 的精华。
    2. Java 9 中 Flow API 只是简单的 Copy 了 ReactiveStream JVM 接口,仅带一个简单的实现。实用性远不如 Rxjava 提供的 Single, Oberverable, Maybe, Flowable 丰富和易用。
    hantsy
        62
    hantsy  
       2019-11-17 00:43:48 +08:00
    Java 异常本来就有两种分类, 大约 Spring 的人都忘记了 try、catch,一味的认为 RuntimeException 才是正确的( Spring 内部的异常它自己有 Exception Translator 进行转换,最终丢客户端)。其实不然, 异常也应该是系统设计上约束的一种。

    比如你设计给一个服务给外部调用,必须强迫服务做什么检查步骤,用异常封装是最直接的表达方式,每一种异常路径用一个异常,比如,CreditCardExpiredException,UsernameWasTakeException,一目了然。
    opengps
        63
    opengps  
       2019-11-17 00:57:58 +08:00 via Android
    我的个人习惯,为了防止意外错误,开发期间避免 try catch,但是上线前一定都加上。开发期间加了容易隐藏问题。上线必须加是为了程序健壮,当然 catch 里必须记录日志。
    我做 socket 服务端,任何异常都不允许中断程序运行,所以上线时候即使人为没错,也必须加一层以防万一
    iwtbauh
        64
    iwtbauh  
       2019-11-17 01:02:20 +08:00 via Android
    @wysnylc #4

    外星人可读的错误消息是认真的??

    请看#2 第二句话

    你这种设计会导致实现各种功能的函数和本地化机制紧密耦合。

    正确的做法是返回(或通过异常)错误代码,或者某种定义良好的数据结构,然后通过错误代码或数据结构获取错误消息。这时候调用者可以选择最合适的国际化方式。也使本地化工作能更简单地完成。

    例如 open 函数,如果使用 errno,则调用者负责国际化可以非常灵活,一种方法是

    concat_string(_("open %s for %s failed, "), errorstr(errno))

    在英文下:open /var/xxx for write failed, Permision denied
    在中文下: 打开 /var/xxx 写入失败,拒绝访问

    想象一下,如果 open 函数自己搞一套或者用某一套国际化机制,对于调用者来说则太重量级了,可能会很不舒服,调用者可能很难选择更好的方式来进行国际化。

    考虑你需要调用库 a 和库 b,现在这两个库都返回人类可读的错误消息,然后分别搞了 2 套不同国际化机制,那个场面一定很好玩,而负责本地化的人也一定会向你抱怨。
    MinQ
        65
    MinQ  
       2019-11-17 01:05:04 +08:00 via Android
    我们 python boy 就看着你们掐架,吃瓜 ing
    soulzz
        66
    soulzz  
       2019-11-17 01:10:39 +08:00
    try{
    //do sth
    }catch(Exception e){
    //错误处理代码
    log.error(e.getMessage()+"代码区间 xxx")
    }

    通常快速迭代而没有反复推敲程序是否有问题时,配合上优秀的 log (必须是必要的 log,并且能够在日志中准确找到问题代码所在区块以及报错原因),下一个版本就能光速迭代
    CosimoZi
        67
    CosimoZi  
       2019-11-17 01:27:55 +08:00 via Android
    monad 它不香吗?为什么要 try catch ?
    wysnylc
        68
    wysnylc  
    OP
       2019-11-17 01:55:26 +08:00
    @iwtbauh #64 这里在讨论 try-catch 和你本地化有啥关系,好好好就算你是举个例子想说明
    那么有关系那么异常内容就不能做本地化了吗?你的想法很奇怪唉
    charlie21
        69
    charlie21  
       2019-11-17 07:57:18 +08:00 via Android
    1 你理解问题的方式就是你处理问题的方式 ( 不同人把它理解成不同的啥样,就会把它处理成不同的啥样 )
    2 这种能造成多种理解的片语 本身就有问题 ( 无论它被处理成不同的啥样 ) 。根本不值得深究
    3 带着不同理解的人,不应该合作 ( 否则 在整体角度上 就出现混杂,在个人角度上 不合作是在降低耦合 )
    4 在合作之前,应该统一好理解 ( 如果无法统一理解,那么 就降低耦合 )

    异常处理 是有争议问题 背后是合作类问题,以上四步基本上是合作类问题的通解 。

    -
    wysnylc
        70
    wysnylc  
    OP
       2019-11-17 10:01:01 +08:00
    @charlie21 #69 现在是两个派系的斗争而不是在所谓的合作,谁吃饱了撑的跟所有程序员合作啊
    搞清楚场合再来显摆理论行不行?
    nekoyaki
        71
    nekoyaki  
       2019-11-17 13:51:24 +08:00
    我不写 java,不过楼主说的大意我能领会。其他语言 /技术里,也常常会有一些机制,它们可能很强大,有一定的使用门槛,或是有一定的副作用,也容易造成滥用。
    因此,就会有另一些人从避免滥用的角度出发,认为应当避免、甚至不应该使用这些机制。

    对这类问题,我觉得是这样,如果我是小白,那么我会选择听从主流意见,避免使用这些机制; 但同时,我会去了解这些机制能带来什么好处,能解决什么问题,但相应地使用这些机制是否有风险和场景条件、带来的代价是什么。
    如果我能够作出判断,认为我愿意支付这些代价和风险,来换取这些好处,我就会使用。

    一味地说不要使用 XX,不是一个解决问题的思路。
    godoway
        72
    godoway  
       2019-11-18 08:51:47 +08:00 via Android
    @newtype0092
    其实不如学 rust 的 match
    match(rs) {
    Ok(target) => dosomething,
    Err(e)=> dosomething
    }
    solome
        73
    solome  
       2019-11-18 10:55:40 +08:00
    go\rust 的处理机制都是很优秀的思路。

    个人还是很喜欢将异常也作为函数执行返回结果的一种。比如 js 中稍微封装下也会避免 滥用 try..catch... 的情况。

    ```
    const noTryCatch = (func) => {
    try {
    const res = func()
    return [res, null]
    } catch (error) {
    return [null, error]
    }
    }

    const [res, err] = noTryCatch(() => /* 你的执行体 */)
    ```
    newtype0092
        74
    newtype0092  
       2019-11-18 14:39:49 +08:00
    @wysnylc #60 我不是想 return null,而是假设在函数的返回值里加入包含错误的 meta 数据,优先针对 meta 数据做错误处理,没有错误时才处理后续逻辑,说白了就是给 go 加个语法糖,多返回的那个 err 隐藏封装起来,不用写的那么痛苦。原理上还是和 try catch 一样,只是把范围限定到最小,防止什么不懂的人瞎 JB 一个大括号全扩起来 catch 下就不管了。。。
    try catch 搞成大括号说明白了就是方便不用处理每一处可能的错误,把很多异常一次性处理了,这样是省事了,可是很多人放任自流的就越省越大了。。。
    rovins
        75
    rovins  
       2019-11-23 03:22:39 +08:00 via iPhone
    异常当然是要 handle 的,但是并不应该非 3 即 4,异常参与业务逻辑的流程控制的话,还是对性能有很大消耗的。
    所以对业务逻辑的处理部分应不应该使用 checked exception,一直以来是有争议的。
    可以参考 icyfenix 和 R 大他们以前的讨论: http://icyfenix.iteye.com/blog/857722

    http://www.iteye.com/topic/2038
    wysnylc
        76
    wysnylc  
    OP
       2019-11-23 10:14:58 +08:00
    @rovins #75 关于性能的问题你的资料中有这么一段话,我猜你没仔细看

    所以如果十分在意性能,是可以优化的 try-catch 的性能消耗是与 if-else 一致的,性能差距在于 fillInStackTrace
    写出可读可维护的代码比这点性能要强太多
    waterlaw
        77
    waterlaw  
       2019-12-08 11:32:54 +08:00 via Android
    Spring 中配置 AOP 处理异常, 然后就不需要 try catch 了, 只要打印关键信息日志, 其他地方的话倒是得使用 try catch 并注意异常信息别丢失。
    bronya0
        78
    bronya0  
       2023-12-21 11:03:18 +08:00
    @guyeu go 完美的丢掉了这两点,既没有异常堆栈,对代码侵入也很大
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3916 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 38ms · UTC 00:56 · PVG 08:56 · LAX 16:56 · JFK 19:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.