java try catch

一、参考为什么不建议用 try catch

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

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

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

3.很多情况下,函数需要把详细的原因,用字符串的方式,返回:
<pre>
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检查文件系统是否存在问题。"。
</pre>
4.我个人推崇上面这种方式,完整,美观。但是这种流程,容易与正常的代码混在一起,不好区分开。因此,Java、C#等设计了try catch这一种特殊的方式:
<pre>
void CreateFile()
//如果创建成功就不会抛出异常。
//如果是因为没有权限,导致失败,会抛出AccessException,这个Exception的Msg属性为"用户【guest】没有权限在【C:\】这个目录下创建该文件。建议您向管理员申请权限,或者更换具有权限的用户。"。
//如果是因为文件已经存在,导致失败,会抛出FileExistedException,这个Exception的Msg属性为"文件【C:\abc.txt】已经存在。如果需要覆盖,请添加参数:arg_overwrite = true"。
//如果是因为创建文件发生超时,导致失败,会抛出TimeoutException,这个Exception的Msg属性为"在创建文件时超时,请使用chkdsk检查文件系统是否存在问题。"。
</pre>
可见,上述机制,实际上是用不同的Exception代替了【3】的State。

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

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

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

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

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

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

二、参考项目中到处都是try-catch是一种常态吗

通常try/catch适用于以下场景:

  • 在代码中对可预见而又无法掌控的情况进行处理。比如在SOCKET BIND时发现端口已经被占用了、或者IO在打开文件时发现文件不存在,就需要在catch中做适当的处理避免程序crash掉;
  • 将问题向更上一层面传递,将处理权让渡给caller。假如你写了个ORM FRAMEWORK,在delete的时候发现外键关联删除失败,FRAMEWORK不能擅自替上层的代码决定该怎么办,于是只好把DB的报的错误原样(或者加层外衣)throw出来,调用者根据业务需要选择处理方式;

除此之外,所有问题应该由程序员主动判断,就地解决。在规模比较大的软件中,定义自己的Exception体系并正确、克制地使用try/catch,可以让代码变得易读易维护还美观。

传递给上层来解决例子如下:
<pre>
void handlearray(int a[]) throws Npe
{
if(a==null)
throw new Npe();
a[0]……//处理部分
}

上层:
try{
handlearray(a);
}catch(E… e)
{
//对a进行处理。
}
</pre>
这时候传入数组为空,这个错误不是你当前这个函数所能处理的,只能是抛给上层,也就是生成这个数组,或者能对这个数组负责的那部分代码,让上层去处理,上层去try cacth,并在catch中对异常处理,类库中类似的像文件io的时候很多读写类都会抛出FileNotFoundException,也是一个道理,当上层给我一个找不到的文件,那在我的io类中肯定无法处理你这个异常,只能抛到给我这个文件的那一层,让那一层的代码对这个问题进行反应。

当然有些时候不需要,比如:
<pre>
void makearray(int a[])
{
a=new int[];
……//生成部分
if(a==null)
……//处理部分,此处一般不用抛异常,直接可以在这一层处理掉。
}
</pre>
像这个就不一样了,因为这次发生的问题是在我这一层代码所能控制之内的,所以我直接把问题处理掉就好了,没必要给上层了。

为什么讲“正确”并“克制”地使用?因为有些又蠢又懒的程序员喜欢这么干:

  1. 将函数所有代码都放到try{}之中,哪怕 int i = 1这种赋值的都不放过。然后在catch里输出一个错误信息就万事大吉。这样看起来是很省心哇,不用动脑子去分析哪里可能发生什么错误,反正所有错误都在catch的掌控之中;
  2. 用try/catch来控制流程。举个简单的例子,假设有这么个要求:
    将字符串转换成数字,并返回该数字的绝对值,如果出错了就返回-1. 于是乎,就能见到类似下面代码的奇葩做法:
    <pre>
    int parse_number(const char* s){
    try{
    return abs(atoi(s));
    }catch(Exception){
    return -1;
    }
    }
    </pre>
    这多省事儿,不用考虑s是不是NULL、不用考虑s是不是包含非数字的字符、不用考虑s是不是超出int的取值范围...我是个优秀的程序员耶~~,我的代码好简洁。

try/catch和errno可以结合起来使用,二者不是非此即彼的关系,比如在某些场景下,可以将不确定的错误简化归纳为固定的errno输出,调用者直接检查返回的errno即可,简化了代码,也减轻了负担。比如某函数,成功返回0,失败返回-1:
<pre>
int foo(double d){
try{
do_something(d);
return 0;
}catch(Exception){
return -1;
}
}

void bar(double d){
int result = foo(d);
if(result == -1) return;
do_next_steps();
}
</pre>

三、细节

Java中try,catch,finally的用法
Java异常处理的组合方式:
1.try+catch
运行流程:运行到try块中,如果有异常抛出,则转到catch块去处理。然后执行catch块后面的语句
2.try+catch+finally
运行流程:运行到try块中,如果有异常抛出,则转到catch块,catch块执行完毕后,执行finally块的代码,再执行finally块后面的代码。
如果没有异常抛出,执行完try块,也要去执行finally块的代码。然后执行finally块后面的语句

Java finally语句到底是在return之前还是之后执行?

网上有很多人探讨Java中异常捕获机制try...catch...finally块中的finally语句是不是一定会被执行?很多人都说不是,当然他们的回答是正确的,经过我试验,至少有两种情况下finally语句是不会被执行的:

(1)try语句没有被执行到,如在try语句之前就返回了,这样finally语句就不会执行,这也说明了finally语句被执行的必要而非充分条件是:相应的try语句一定被执行到。

(2)在try块中有System.exit(0);这样的语句,System.exit(0);是终止Java虚拟机JVM的,连JVM都停止了,所有都结束了,当然finally语句也不会被执行到。

四、检查型异常和非检查型异常

Paste_Image.png

上图摘自Java 进阶 之 检查型异常与非检查型异常

  • 直接继承 Exception 的异常,属于检查型异常,必须用try语句块进行处理或者把异常交给上级方法处理总之就是必须写代码处理它。如IOException,SQLException
  • 继承自Runtime Exception或 Error 的是非检查型异常,可以不用捕获
五、throw 和 throws区别

1、throws出现在方法函数头;而throw出现在函数体。
2、throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常对象。
3、两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。

六、关于异常的面试问答
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,784评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,745评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,702评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,229评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,245评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,376评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,798评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,471评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,655评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,485评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,535评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,235评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,793评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,863评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,096评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,654评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,233评论 2 341

推荐阅读更多精彩内容

  • 1 抛出 Exception,没有 finally,当 catch 遇上 return 1 2public sta...
    织田信长阅读 838评论 0 2
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,547评论 18 399
  • try...catch...finally由两段代码展开下面要讨论的话题 try..catch...finally...
    snoweek阅读 259评论 0 3
  • 当夜入深处时,天地万物寂静,我们需要做的静待署光,而此时美在静静地蔓延,没有人知道,那些花何时爬满了枯墙,就像没有...
    水善万物阅读 134评论 0 0
  • 我今天仔细的在脑海里问了自己一个问题,你到底想不想结婚? 上课的时候,任课老师和周围几个同学不知道借由什么话题,引...
    鹿遇阅读 394评论 0 1