我们在写代码的时候都或多或少碰到了大大小小的异常,例如:
public class test { public static void main(string[] args) { int[] arr = {1,2,3}; system.out.println(arr[5]); }}
当我们数组越界时,编译器会给我们报数组越界,并提示哪行出了错。
再比如:
class test{ int num = 10; public static void main(string[] args) { test test = null; system.out.println(test.num); }}
当我们尝试用使用空对象时,编译器也会报空指针异常:
那么究竟什么是异常?
所谓异常指的就是程序在 运行时 出现错误时通知调用者的一种机制 .
关键字 “运行时” ,有些错误是这样的, 例如将 system.out.println 拼写错了, 写成了
system.out.println. 此时编译过程中就会出 错, 这是 “编译期” 出错.
而运行时指的是程序已经编译通过得到 class 文件了 , 再由 jvm 执行过程中出现的错误 .
java异常处理依赖于5个关键字:try、catch、finally、throws、throw。下面来逐一介绍下。
①try:try块中主要放置可能会产生异常的代码块。如果执行try块里的业务逻辑代码时出现异
常,系统会自动生成一个异常对象,该异常对象被提交给运行环境,这个过程被称为抛出
(throw)异常。java环境收到异常对象时,会寻找合适的catch块(在本方法或是调用方
法)。
②catch: catch 代码块中放的是出现异常后的处理行为,也可以写此异常出错的原因或者打
印栈上的错误信息。但catch语句不能为空,因为一旦将catch语句写为空,就代表忽略了此
异常。如:
空的catch块会使异常达不到应有的目的,即强迫你处理异常的情况。忽略异常就如同忽略
火警信号一样——若把火警信号关掉了,当真正的火灾发生时,就没有人能看到火警信号
了。或许你会侥幸逃过一劫,或许结果将是灾难性的。每当见到空的catch块时,我们都应该
警钟长鸣。
当然也有一种情况可以忽略异常,即关闭fileinputstream(读写本地文件)的时候。因为你还
没有改变文件的状态,因此不必执行任何恢复动作,并且已经从文件中读取到所需要的信
息,因此不必终止正在进行的操作。
③finally:finally 代码块中的代码用于处理善后工作, 会在最后执行,也一定会被执行。当遇
到try或catc青春素材h中return或throw之类可以终止当前方法的代码时,jvm会先去执行finally中的语
句,当finally中的语句执行完毕后才会返回来执行try/catch中的return,throw语句。如果
finally中有return或throw,那么将执行这些语句,不会在执行try/catch中的return或throw语
句。finally块中一般写的是关闭资源之类的代码。但是我们一般不在finally语句中加入return
语句,因为他会覆盖掉try中执行的return语句。例如:
finally将最后try执行的return 10覆盖了,最后结果返回了20.
④throws:在方法的签名中,用于抛出此方法中的异常给调用者,调用者可以选择捕获或者
抛出,如果所有方法(包括main)都选择抛出(或者没有合适的处理异常的方式,即异常类
型不匹配)那么最终将会抛给jvm,就会像我们之前没使用try、catch语句一样。jvm打印出
栈轨迹(异常链)。
⑤throw:用于抛出一个具体的异常对象。常用于自定义异常类中。
ps:
关于 “调用栈”,方法之间是存在相互调用关系的, 这种调用关系我们可以用 “调用栈” 来描述.
在 jvm 中有一块内存空间称为 “虚拟机栈” 专门存储方法之间的调用关系. 当代码中出现异常
的时候, 我们就可以使用 e.printstacktrace() 的方式查看出现异常代码的调用栈,一般写在catch语句中。
存在即合理,举个例子
//不使用异常 int[] arr = {1, 2, 3}; system.out.println("before"); system.out.println(arr[100]); system.out.println("after");
当我们不使用异常时,发现出现异常程序直接崩溃,后面的after也没有打印。
//使用异常 int[] arr = {1, 2, 3}; try { system.out.println("before"); system.out.println(arr[100]); system.out.println("after"); } catch (arrayindexoutofboundxception e) { //打印出现异常的调用栈 e.printstacktrace(); } sy哈职院有什么专业stem.out.println("after try catch");
当我们使用了异常,虽然after也没有执行,但程序并没有直接崩溃,后面的sout语句还是执行了
这不就是异常的作用所在吗?
再举个例子,当玩王者荣耀时,突然断网,他不会让你直接程序崩溃吧,而是给你断线重连的机会吧:
我们再用伪代码演示一把王者荣耀的对局过程:
不使用异常处理boolean ret = fal; ret = 登陆游戏(); if (!ret) { 处理登陆游戏错误; return; } ret = 开始匹配(); if (!ret) { 处理匹配错误; return; }ret = 游戏确认(); if (!ret) { 处理游戏确认错误; return; }ret = 选择英雄(); if (!ret) { 处理选择英雄错误; return; } ret = 载入游戏画面(); if (!ret) { 处理载入游戏错误; return; } ......
使用异常处理try { 登陆游戏(); 开始匹配(); 游戏确认(); 选择英雄(); 载入游戏画面(); ... } catch (登陆游戏异常) { 处理登陆游戏异常; } catch (开始匹配异常) { 处理开始匹配异常; } catch (游戏确认异常) { 处理游戏确认异常; } catch (选择英雄异常) { 处理选择英雄异常; } catch (载入游戏画面异常) { 处理载入游戏画面异常; }......
我们能明显的看到不使用异常时,正确流程和错误处理代码混在一起,不易于分辨,而用了
异常后,能更易于理解代码。
当然使用异常的好处还远不止于此,我们可以在try、catch语句中加入信息提醒功能,比如你
开发了一个软件,当那个软件出现异常时,发个信息提醒你及时去修复。博主就做了一个小
小的qq邮箱信息提醒功能,源码在码云,有兴趣的可以去看看呀!需要配置qq邮箱pop3服
务,友友们可以去查查怎么开启呀,我们主旨不是这个所以不教怎么开启了。演示一下:
别群发消息哦,不然可能会被封号???
try{ int i = 0; while(true) system.out.println(a[i++]);}catch(arrayindexoutofboundxception e){ }
这段代码有什么用?看起来根本不明显,这正是它没有真正被使用的原因。事实证明,作为
一个要对数组元素进行遍历的实现方式,它的构想是非常拙劣的。当这个循环企图访问数组
边界之外的第一个数组元素时,用抛出(throw)、捕获(catch)、
忽略(arrayindexoutofboundxception)的手段来达到终止无限循环的目的。假定它与数
组循环是等价的,对于任何一个java程序员来讲,下面的标准模式一看就会明白:
for(int m : a) system.out.println(m);
为什么优先异常的模式,而不是用行之有效标准模式呢?
可能是被误导了,企图利用异常机制提高性能,因为jvm每次访问数组都需要判断下标是否越
界,他们认为循环终止被隐藏了,但是在foreach循环中仍然可见,这无疑是多余的,应该避
免。
上面想法有三个错误:
1.异常机制设计的初衷是用来处理不正常的情况,所以jvm很少对它们进行优化。
2.代码放在try…catch中反而阻止jvm本身要执行的某些特定优化。
3.对数组进行遍历的标准模式并不会导致冗余的检查。
这个例子的教训很简单:顾名思义,异常应只用于异常的情况下,它们永远不应该用于正常
的控制流。
总结:异常是为了在异常情况下使用而设计的,不要用于一般的控制语句。
在java中提供了三种可抛出结构:受查异常(checked exception)、运行时异常(run-time exception)和错误(error)。
(补充)
什么是受查异常?只要不是派生于error或runtime的异常类都是受查异常。举个例子:
我们自定义两个异常类和一个接口,以及一个测试类
interface iur { void changepwd() throws safeexception,rejectexception;} class safeexception extends exception {//因为继承的是execption,所以是受查异常类 public safeexception() { } public safeexception(string message) { super(message); } } class rejectexception extends exception {//因为继承的是execption,所以是受查异常类 public rejectexception() { } public rejectexception(string message) { super(message); }} public class test { public static void main(string[] args) { iur ur = null; ur.changepwd(); }}
我们发现test测试类中ur使用方法报错了,因为java认为checked异常都是可以再编译阶
段被处理的异常,所以它强制程序处理所有的checked异常,java程序必须显式处checked
异常,如果程序没有处理,则在编译时会发生错误,无法通过编译。
①try、catch包裹
iur ur = null; try { ur.changepwd(); }catch (safeexception e){ e.printstacktrace(); } catch (rejectexception e){ e.printstacktrace(); }
②抛出异常,将处理动作交给上级调用者,调用者在调用这个方法时还是要写一遍try、catch
包裹语句的,所以这个其实是相当于声明,让调用者知道这个函数需要抛出异常
public static void main(string[] args) throws safeexception, rejectexception { iur ur = null; ur.changepwd(); }
派生于error或runtime类的所有异常类就是非受查异常。
可以这么说,我们现在写程序遇到的异常大部分都是非受查异常,程序直接崩溃,后面的也
不执行。
像空指针异常、数组越界异常、算术异常等,都是非受查异常。由编译器运行时给你检查出
来的,所以也叫作运行时异常。
如果不能阻止异常条件的产生,并且一旦产生异常,程序员可有关清明节的手抄报以立即采取有用的动作,这种
受查异常才是可取的。否则,更适合用非受查异常。这种例子就是
clonenotsuppportedexception(受查异常)。它是被object.clone抛出来的,object.clone
只有在实现了cloneable的对象上才可以被调用。
被一个方法单独抛出的受查异常,会给程序员带来非常高的额外负担,如果这个方法还有其
他的受查异常,那么它被调用是一定已经出现在一个try块中,所以这个异常只需要另外一个
catch块。但当只抛出一个受查异常时,仅仅一个异常就会导致该方法不得不处于try块中,也
就导致了使用这个方法的类都不得不使用try、catch语句,使代码可读性也变低了。
受查异常使接口声明脆弱,比如一开始一个接口只有一个声明异常
interfaceur{ //修改用户名,抛出安全异常 运动会通讯稿publicvoid changepassword() throws mycurityexcepiton; }
但随着系统开发,实现接口的类越来越多,突然发现changepassword还需要抛出另一个异
常,那么实现这个接口的所有类也都要追加对这个新异常的处理,这个工程量就很大了。
总结:如果不是非用不可,尽量使用非受查异常,或将受查异常转为非受查异常。
我们用自定义异常来实现一个登录报错的小应用
class nameexception ext浙江高考改革ends runtimeexception{//用户名错误异常 public nameexception(string message){ super(message); }}class passwordexception extends runtimeexception{//密码错误异常 public passwordexception(string message){ super(message); }}
test类来测试运行
public class test { private static final string name = "bit"; private static final string password ="123"; public static void login(string name,string password) throws nameexception,passwordexception{ try{ if(!test.name.equals(name)){ throw new nameexception("用户名错误!"); } }catch (nameexception e){ e.printstacktrace(); } try { if(!test.password.equals(password)){ throw new passwordexception("密码错误"); } }catch (passwordexception e){ e.printstacktrace(); } } public static void main(string[] args) { scanner scanner = new scanner(system.in); string name = scanner.nextline(); string password = scanner.nextline(); login(name,password); }}
关于异常就到此为止了,怎么感觉还有点意犹未尽呢?
到此这篇关于java异常处理机制深入理解的文章就介绍到这了,更多相关java 异常处理机制内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!
本文发布于:2023-04-04 16:08:54,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/f357bda71f7ac81a70d60bcb9663a17a.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:Java异常处理机制深入理解.doc
本文 PDF 下载地址:Java异常处理机制深入理解.pdf
留言与评论(共有 0 条评论) |