Java开发入门到精通之Java异常处理机制
小职 2021-07-05 来源 :沉默王二 阅读 448 评论 0

摘要:本篇主要介绍了Java开发入门到精通之Java异常处理机制,通过具体的内容展示,希望对Java开发的学习有一定的帮助。

本篇主要介绍了Java开发入门到精通之Java异常处理机制,通过具体的内容展示,希望对Java开发的学习有一定的帮助。

Java开发入门到精通之Java异常处理机制

"二哥,今天就要学习异常了吗?”三妹问。

 

“是的。只有正确地处理好异常,才能保证程序的可靠性,所以异常的学习还是很有必要的。”我说。

 

“那到底什么是异常呢?”三妹问。

 

“异常是指中断程序正常执行的一个不确定的事件。当异常发生时,程序的正常执行流程就会被打断。一般情况下,程序都会有很多条语句,如果没有异常处理机制,前面的语句一旦出现了异常,后面的语句就没办法继续执行了。”

 

“有了异常处理机制后,程序在发生异常的时候就不会中断,我们可以对异常进行捕获,然后改变程序执行的流程。”

 

“除此之外,异常处理机制可以保证我们向用户提供友好的提示信息,而不是程序原生的异常信息——用户根本理解不了。”

 

“不过,站在开发者的角度,我们更希望看到原生的异常信息,因为这有助于我们更快地找到 bug 的根源,反而被过度包装的异常信息会干扰我们的视线。”

 

“Java 语言在一开始就提供了相对完善的异常处理机制,这种机制大大降低了编写可靠程序的门槛,这也是 Java 之所以能够流行的原因之一。”

 

“那导致程序抛出异常的原因有哪些呢?”三妹问。

 

比如说:

 

程序在试图打开一个不存在的文件;

程序遇到了网络连接问题;

用户输入了糟糕的数据;

程序在处理算术问题时没有考虑除数为 0 的情况;

等等等等。

 

挑个最简单的原因来说吧。

 

public class Demo {

    public static void main(String[] args) {

        System.out.println(10/0);

    }

}

这段代码在运行的时候抛出的异常信息如下所示:

 

Exception in thread "main" java.lang.ArithmeticException: / by zero

 at com.itwanger.s41.Demo.main(Demo.java:8)

“你看,三妹,这个原生的异常信息对用户来说,显然是不太容易理解的,但对于我们开发者来说,简直不要太直白了——很容易就能定位到异常发生的根源。”

 

“哦,我知道了。下一个问题,我经常看到一些文章里提到 Exception 和 Error,二哥你能帮我解释一下它们之间的区别吗?”三妹问。

 

“这是一个好问题呀,三妹!”

 

从单词的释义上来看,error 为错误,exception 为异常,错误的等级明显比异常要高一些。

 

从程序的角度来看,也的确如此。

 

Error 的出现,意味着程序出现了严重的问题,而这些问题不应该再交给 Java 的异常处理机制来处理,程序应该直接崩溃掉,比如说 OutOfMemoryError,内存溢出了,这就意味着程序在运行时申请的内存大于系统能够提供的内存,导致出现的错误,这种错误的出现,对于程序来说是致命的。

 

Exception 的出现,意味着程序出现了一些在可控范围内的问题,我们应当采取措施进行挽救。

 

比如说之前提到的 ArithmeticException,很明显是因为除数出现了 0 的情况,我们可以选择捕获异常,然后提示用户不应该进行除 0 操作,当然了,更好的做法是直接对除数进行判断,如果是 0 就不进行除法运算,而是告诉用户换一个非 0 的数进行运算。

 

“三妹,还能想到其他的问题吗?”

 

“嗯,不用想,二哥,我已经提前做好预习工作了。”三妹自信地说,“异常又可以分为 checked 和 unchecked,它们之间又有什么区别呢?”

 

“哇,三妹,果然又是一个好问题呢。”

 

checked 异常(检查型异常)在源代码里必须显式地捕获或者抛出,否则编译器会提示你进行相应的操作;而 unchecked 异常(非检查型异常)就是所谓的运行时异常,通常是可以通过编码进行规避的,并不需要显式地捕获或者抛出。

 

“我先画一幅思维导图给你感受一下。”

 Java开发入门到精通之Java异常处理机制

 

 

首先,Exception 和 Error 都继承了 Throwable 类。换句话说,只有 Throwable 类(或者子类)的对象才能使用 throw 关键字抛出,或者作为 catch 的参数类型。

 

面试中经常问到的一个问题是,NoClassDefFoundError 和 ClassNotFoundException 有什么区别?

 

“三妹你知道吗?”

 

“不知道,二哥,你解释下呗。”

 

它们都是由于系统运行时找不到要加载的类导致的,但是触发的原因不一样。

 

NoClassDefFoundError:程序在编译时可以找到所依赖的类,但是在运行时找不到指定的类文件,导致抛出该错误;原因可能是 jar 包缺失或者调用了初始化失败的类。

ClassNotFoundException:当动态加载 Class 对象的时候找不到对应的类时抛出该异常;原因可能是要加载的类不存在或者类名写错了。

其次,像 IOException、ClassNotFoundException、SQLException 都属于 checked 异常;像 RuntimeException 以及子类 ArithmeticException、ClassCastException、ArrayIndexOutOfBoundsException、NullPointerException,都属于 unchecked 异常。

 

unchecked 异常可以不在程序中显示处理,就像之前提到的 ArithmeticException 就是的;但 checked 异常必须显式处理。

 

比如说下面这行代码:

 

Class clz = Class.forName("com.itwanger.s41.Demo1");

如果没做处理,比如说在 Intellij IDEA 环境下,就会提示你这行代码可能会抛出 java.lang.ClassNotFoundException。

 

 Java开发入门到精通之Java异常处理机制

 

建议你要么使用 try-catch 进行捕获:

 

try {

    Class clz = Class.forName("com.itwanger.s41.Demo1");

} catch (ClassNotFoundException e) {

    e.printStackTrace();

}

注意打印异常堆栈信息的 printStackTrace() 方法,该方法会将异常的堆栈信息打印到标准的控制台下,如果是测试环境,这样的写法还 OK,如果是生产环境,这样的写法是不可取的,必须使用日志框架把异常的堆栈信息输出到日志系统中,否则可能没办法跟踪。

 

要么在方法签名上使用 throws 关键字抛出:

 

public class Demo1 {

    public static void main(String[] args) throws ClassNotFoundException {

        Class clz = Class.forName("com.itwanger.s41.Demo1");

    }

}

这样做的好处是不需要对异常进行捕获处理,只需要交给 Java 虚拟机来处理即可;坏处就是没法针对这种情况做相应的处理。

 

“二哥,针对 checked 异常,我在知乎上看到一个帖子,说 Java 中的 checked 很没有必要,这种异常在编译期要么 try-catch,要么 throws,但又不一定会出现异常,你觉得这样的设计有意义吗?”三妹提出了一个很尖锐的问题。

 

“哇,这种问题问的好。”我不由得对三妹心生敬佩。

 

“的确,checked 异常在业界是有争论的,它假设我们捕获了异常,并且针对这种情况作了相应的处理,但有些时候,根本就没法处理。”我说,“就拿上面提到的 ClassNotFoundException 异常来说,我们假设对其进行了 try-catch,可真的出现了 ClassNotFoundException 异常后,我们也没多少的可操作性,再 Class.forName() 一次?”

 

另外,checked 异常也不兼容函数式编程,后面如果你写 Lambda/Stream 代码的时候,就会体验到这种苦涩。

 

当然了,checked 异常并不是一无是处,尤其是在遇到 IO 或者网络异常的时候,比如说进行 Socket 链接,我大致写了一段:

 

public class Demo2 {

    private String mHost;

    private int mPort;

    private Socket mSocket;

    private final Object mLock = new Object();

 

    public void run() {

    }

 

    private void initSocket() {

        while (true) {

            try {

                Socket socket = new Socket(mHost, mPort);

                synchronized (mLock) {

                    mSocket = socket;

                }

                break;

            } catch (IOException e) {

                e.printStackTrace();

            }

        }

    }

}

当发生 IOException 的时候,socket 就重新尝试连接,否则就 break 跳出循环。意味着如果 IOException 不是 checked 异常,这种写法就略显突兀,因为 IOException 没办法像 ArithmeticException 那样用一个 if 语句判断除数是否为 0 去规避。

 

或者说,强制性的 checked 异常可以让我们在编程的时候去思考,遇到这种异常的时候该怎么更优雅的去处理。显然,Socket 编程中,肯定是会遇到 IOException 的,假如 IOException 是非检查型异常,就意味着开发者也可以不考虑,直接跳过,交给 Java 虚拟机来处理,但我觉得这样做肯定更不合适。

 

“好了,三妹,关于异常处理机制这节就先讲到这里吧。”我松了一口气,对三妹说。

 

“好的,二哥,你去休息吧。”

 

“对了,三妹,我定个姑婆婆的外卖吧,晚上我们喝粥。”

 

“好呀,我要两个豆沙包。”



我是小职,记得找我

✅ 解锁高薪工作

✅ 免费获取基础课程·答疑解惑·职业测评

Java开发入门到精通之Java异常处理机制

本文由 @小职 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程