茫茫人海千千万万,感谢这一秒你看到这里。希望我的文章对你的有所帮助!
愿你在未来的日子,保持热爱,奔赴山海!
相信各位都会在使用java中或多或少的出现一些异常bug,那这些异常是从何而来的呢?
异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。其实在java中,异常是java提供的一种识别及响应错误的一致性机制。从而可以达到程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提高程序健壮性。
异常,就是不正常的意思。而在生活中:医生说,你的身体某个部位有异常,该部位和正常相比有点不同,该部位的功能将受影响。在程序中的意思就是:
异常 :指的是程序在执行过程中,出现的非正常的情况,最终会导致jvm的非正常停止。异常指的并不是语法错误,语法错了,编译不通过,不会产生字节码文件,根本不能运行。在java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象。java为异常设计了一套异常处理机制,当程序运行过程中发生一些异常情况时,程序不会返回任何值,而是抛出封装了错误信息的异常对象。这样保证程序代码更加优雅,并提高程序健壮性。为什么要设计异常呢?首先,引入异常之后,我们就可以把错误代码从正常代码中分离出来进行单独处理,这样使代码变得更加整洁;其次,当出现一些特殊情况时,我们还可以抛出一个检查异常,告知调用者让其处理。而throwable体系下包含有两个子类,error(错误)和exception(异常),它们通常用于指示发生了异常情况。二者都是 java 异常处理的重要子类,各致妈妈的一封信自都包含大量子类。所有的异常都是从throwable继承而来的,是所有所有错误与异常的超类。throwable包含了其线程创建时线程执行堆栈的快照,它提供了 printstacktrace()等接口用于获取堆栈跟踪数据等信息。
exception 是另外一个非常重要的异常子类。程序本身可以捕获并且可以处理的异常。这类异常一旦出现,我们就要对代码进行更正,修复程序。exception这种异常又分为两类:运行时异常和编译时异常。
通常,java的异常(throwable)分为受检异常(checked exceptions)和非受检异常(unchecked exceptions)。
正确的程序在运行过程中,经常容易出现的、符合预期的异常情况。一旦发生此类异常,就必须采用某种方式进行处理。除了excep庄子心得tion中的 runtimeexception 及其子类以外,其他的 exception类及其子类异常就是非运行时期异常都属于受检异常。这种异常编译器会检查它,也就是说当编译器检查到应用中的某处可能会此类异常时,将会提示你处理本异常——要么使用try-catch捕获,要么使用方法签名中用 throws 关键字抛出,否则编译不通过。编译器要求必须处理的异常。
此类异常,就是当程序中出现此类异常时,即使我们没有try-catch捕获它,也没有使用throws 抛出该异常,编译也会正常通过。该类异常包括运行时异常(runtimeexception 极其子类)和错误( error)。runtimeexception 发生的时候,表示程序中出现了编程错误,所以应该找出错误修改程序,而不是去捕获runtimeexception 。编译器不会进行检查并且不要求必须处理的异常。
在 java 应用程序中,异常处理机制为:抛出异常,捕捉异常。
这里先了解下关键词,具体定义格式和使用方法在下面介绍:
接下来用程序具体演示下吧:
public class throwdemo { public static void main(string[] args) { //创建一个数组 int[] arr = {2,4,52,2}; //根据索引找对应的元素 int index = 4; int element = getelement(arr, index); system.out.println(element); system.out.println("over"); } /* * 根据 索引找到数组中对应的元素 */ public static int getelement(int[] arr,int index){ //判断索引是否越界 if(index<0 || index>arr.length-1){ /* 判断条件如果满足,当执行完throw抛出异常对象后,方法已经无法继续运算。 这时就会结束当前方法的执行,并将异常告知给调用者。这时就需要通过异常来解决。 */ throw new arrayindexoutofboundxception("老弟,你的索引越界了,别这么干"); } int element = arr[index]; return element; }}
运行后输出结果为:
exception in thread "main" java.lang.arrayindexoutofboundxception: 老弟,你的索引越界了,别这么干 at com.it.test2.throwdemo.getelement(throwdemo.java:25) at com.it.test2.throwdemo.main(throwdemo.java:10)
可以看到我定义了索引为4,但是数组的长度只有4。所以会报错。
注意:所以如果产生了问题,我们就会throw将问题描述类即异常进行抛出,也就是将问题返回给该方法的调用者。结果是
arrayindexoutofboundxception 的数组索引越界的问题。
那么对于调用者来说,该怎么处理呢?一种是进行捕获处理,另一种就是继续讲问题声明出去,使用throws声明处理。
声明异常:将问题标识出来,报告给调用者。如果方法内通过throw抛出了编译时异常,而没有捕获处理(稍后讲解该方式),那么必须通过throws进行声明,让调用者去处理。关键字throws运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常).定义格式: throws语句用在方法定义时声明该方法要抛出的异常类型,如果抛出的是exception异常类型,则该方法被声明为抛出所有的异常。多个异常可使用逗号分割。修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{ } 注意:当方法抛出异常列表的异常时,方法将不对这些类型及其子类类型的异常作处理,而抛向调用该方法的方法,由他去处理。使用throws关键字将异常抛给调用者后,如果调用者不想处理该异常,可以继续向上抛出,但最终要有能够处理该异常的调用者。比如汽车坏了,开车的人也不会修理,只能叫修车公司来修理了。演示一下:一般来说,throws和 throw通常是成对出现的,例如:public class throwsdemo { public static void main(string[] args) throws filenotfoundexception { readfile(“a.txt”); }public class throwsdemo {如果一个方法可能会出现异常,但没有能力处理这种异常,可以在方法声明处用throws子句来声明抛出异常。例如汽车在运行时它可能会出现故障,汽车本身没办法处理这个故障,那就让开车的人来处理。
而throws用于进行异常类的声明,若该方法可能有多种异常情况产生,那么在throws后面可以写多个异常类,用逗号隔开。
public class throwsdemo2 { public static void main(string[] args) throws ioexception { readfile("a.txt"); } //若该方法可能有多种异常情况产生,那么在throws后面可以写多个异常类,用逗号隔开 //若有异常a是异常b的子类,也可以直接省略,写b异常 private static void readfile(string path) throws filenotfoundexception, ioexception { if (!path.equals("a.txt")) {//如果不是 a.txt这个文件 // 我假设 如果不是 a.txt 认为 该文件不存在 是一个错误 也就是异常 throw throw new filenotfoundexception("文件不存在"); } if (!path.equals("b.txt")) { throw new ioexception(); } }}
throws抛出异常的规则:
如果是非受检异常(unchecked exception),即error、runtimeexception或它们的子类,那么可以不使用throws关键字来声明要抛出的异常,编译仍能顺利通过,但在运行时会被系统抛出。如果一个方法可能出现受检异常(checked exception),要么用try-catch语句捕获,要么用throws子句声明将它抛出,否则会导致编译错误。只有当抛出了异常时,该方法的调用者才必须处理或者重新抛出该异常。若当方法的调用者无力处理该异常的时候,应该继续抛出。调用方法必须遵循任何可查异常的处理和声明规则。若覆盖一个方法,则不声明与覆盖方法不同的异常。声明的任何异常必须是被覆盖方法所声明异常的同类或子类。这三个关键字主要有下面几种组合方式try-catch 、try-finally、try-catch-finally。
注意:catch语句可以有一个或者多个或者没有,finally至多有一个,try必要有。
那这里你会问有没有单独try模块出现,那我想问下你,try是用来监听是否有异常,那如果发生了异常,谁来捕获呢?所以没有try单独出现。而且编译不能通过。
所以跟try模块一样,例如catch,finally也不能单独使用出现
程序通常在运行之前不报错,但是运行后可能会出现某些未知的错误,不想异常出现导致程序终止,或者不想直接抛出到上一级,那么就需要通过try-catch等形式进行异常捕获,之后根据不同的异常情况来进行相应的处理。捕获异常:java中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方式的处理。捕获异常语法如下:
try{ 编写可能会出现异常的代码}catch(异常类型 e){ 处理异常的代码 //记录日志/打印异常信息/继续抛出异常}
例如:
public class trycatchdemo { public static void main(string[] args) { simpledateformat simpledateformat = new simpledateformat("yyyy-mm-dd"); try { //当产生异常时,必须有处理方式。要么捕获,要么声明。 date date = simpledateformat.par("2020-10-06"); } catch (parexception e) {// 括号中需要定义什么呢? //try中抛出的是什么异常,在括号中就定义什么异常类型 e.printstacktrace();//记录日志/打印异常信息/继续抛出异常 } /* public date par(string source) throws parexception{} //par抛出了parexception异常 public class parexception extends exception {} */ }}
如何获取异常信息:
throwable类中定义了一些查看方法:
public string getmessage():获取异常的描述信息,原因(提示给用户的时候,就提示错误原因。public string tostring():获取异常的类型和异常描述信息。public void printstacktrace():打印异常的跟踪栈信息并输出到控制台。具体我们可以来看下:public class trycathdemo2 { public static void main(string[] args) { simpledateformat simpledateformat = new simpledateformat("yyyy-mm-dd"); try { //当产生异常时,必须有处理方式。要么捕获,要么声明。 //演示下获取异常信息,修改了格式。 date date = simpledateformat.par("2020年10月06"); } catch (parexception e) { //public string getmessage():获取异常的描述信息,原因(提示给用户的时候,就提示错误原因 system.out.println(e.getmessage());//unparable date: "2020年10月06" system.out.println(e.tostring());//java.text.parexception: unparable date: "2020年10月06" e.printstacktrace();//输出信息而且飘红!!! /* java.text.parexception: unparable date: "2020年10月06" at java.text.dateformat.par(dateformat.java:366) at com.it.test3.trycathdemo2.main(trycathdemo2.java:13) */ } }}
而如果有多个异常使用捕获我们又该如何处理呢?
多个异常分别处理。多个异常一次捕获,多次处理。一般我们是使用一次捕获多次处理方式,格式如下:
try{ 编写可能会出现异常的代码}catch(异常类型a e){ 当try中出现a类型异常,就用该catch来捕获. 处理异常的代码 //记录日志/打印异常信息/继续抛出异常}catch(异常类型b e){ 当try中出现b类型异常,就用该catch来捕获. 处理异常的代码 //记录日志/打印异常信息/继续抛出异常}
例如:
public class trycatchdemo3 { public static void main(string[] args) { //test(); test2(); } //多个异常一次捕获,多次处理。 public static void test2(){ int[] arr = {11, 22, 66, 0}; try { //system.out.println(arr[5]);//一旦这个报错,下面的代码就不会执行 system.out.println(arr[2]); system.out.println(arr[0] / arr[arr.length-1]); } catch (arithmeticexception e) { system.out.println("除数不为0"); }catch (arrayindexoutofboundxception e) { system.out.println("数组下标越界"); }catch (exception e) { e.printstacktrace(); } } //分别处理的方式 public static void test() { int a = 10; int b = 0; try { system.out.println(a / b); } catch (arithmeticexception e) { system.out.println("除数不为0");//除数不为0 } int[] arr = {1, 2, 3}; try { system.out.println(arr[4]); } catch (arrayindexoutofboundxception e) { system.out.println("数组下标越界");//数组下标越界 } }}
注意:一次捕获,多次处理的异常处理方式,要求多个catch中的异常不能相同,并且若catch中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的catch处理,父类异常在下面的catch处理。
例如:
try{ //(尝试运行的)程序代码 }finally{ //异常发生,总是要执行的代码 }
try-finally表示对一段代码不管执行情况如何,都会走 finally 中的代码,
例如:
public class tryfinallydemo { public static void main(string[] args) { int a = 10; int b = 0; try{ s巨虫公园ystem.out.println(a / b); system.out.println("会走try吗"); }finally{ system.out.println("会finally吗");//会finally吗 } system.o一着ut.println("会走外面吗"); /* 没有捕获的话,他只会走finally语句然后报出异常。 会finally吗 exception in thread "main" java.lang.arithmeticexception: / by zero at com.it.test3.tryfinallydemo.main(tryfinallydemo.java:8) */ }}
可以看到程序异常了,还是会去走finally语句块的代码。
try { // 可能会发生异常的程序代码 } catch (异常类型a e){ // 捕获并处置try抛出的异常类型a} finally { // 无论是否发生异常,都将执行的语句块 }
跟try-finally一样表示对一段代码不管执行情况如何,都会走 finally 中的代码。
当方法中发生异常,异常处之后的代码不会再执行,如果之前获取了一些本地资源需要释放,则需要在方法正常结束时和 catch 语句中都调用释放本地资源的代码,显得代码比较繁琐,finally 语句可以解决这个问题。
例如:
public class trycatchfinallydemo { public static void main(string[] args) { test(); } public static void test() { simpledateformat simpledateformat = new simpledateformat("yyyy-mm-dd"); date date = null; try { //date = simpledateformat.par("2020-10-06");//第一次运行成功 date = simpledateformat.par("2020年10月06日"); } catch (parexception e) { e.printstacktrace(); }finally{ system.out.println("finally这里一定会执行"); } system.out.println("会走外面这里吗" + date); }}
运行成功的代码后结果:
finally这里一定会执行会走外面这里吗tue oct 06 00:00:00 cst 2020
运行失败的代码后结果:
java.text.parexception: unparable date: "2020/10/06" at java.text.dateformat.par(dateformat.java:366) at com.it.test3.trycatchfinallydemo.test(trycatchfinallydemo.java:19) at com.it.test3.trycatchfinallydemo.main(trycatchfinallydemo.java:12)finally这里一定会执行会走外面这里吗null
可以看到,无论失败,都会执行finally语句块的代码。
注意:try-catch-finally中,如果catch中 return了,finally还会执行吗?public class trycatchfinallydemo2 {运行结果:
java.lang.arithmeticexception: / by zero at com.it.test3.trycatchfinallydemo2.test(trycatchfinallydemo2.java:11) at com.it.test3.trycatchfinallydemo2.main(trycatchfinallydemo2.java:5)finally
可以看到,就算catch中 return了,finally也会执行。
那finally是在return前呢,还是return后呢?
让我们看下面的代码?
public class trycatchfinallydemo2 { public static void main(string[] args) {// test(); system.out.println(test2()); // 我有执行到吗 try system.out.println(test3()); // 我有执行到吗 catch } public static string test3() { string str = ""; try { str = "try"; system.out.println(10 / 0); return str; }catch(exception e) { str = "catch"; return str; }finally { str = "finally"; system.out.println("我有执行到吗"); } } public static string test2() { string str = ""; try { str = "try"; return str; }catch(exception e) { str = "catch"; return str; }finally { str = "finally"; system.out.println("我有执行到吗"); } }}
运行结果:
我有执行到吗try我有执行到吗catch
看到这里发现无论是否异常,finally都会执行,但是都在在return之前就执行了代码。可是可是,为什么返回出来的字符串不是finally呢?让我们一起来思考思考:
我们看test2()方法,程序执行try语句块,把变量str赋值为”try”,由于没有发现异常,接下来执行finally语句块,把变量str赋值为”finally”,然后return str,则t的值是finally,最后str的值就是”finally”,程序结果应该显示finally,但是实际结果为“try”。实际上,在try语句的return块中,当我们执行到return str这一步的时候呢,这里不是return str 而是return “try”了,这个放回路径就已经形成了。相当于return返回的引用变量(str是引用类型)并不是try语句外定义的引用变量str,而是系统重新定义了一个局部引用str2 ,返回指向了引用str2 对应的值,也就是”try”字符串。但是到这里呢,它发现后面还有finally,所以继续执行finally的内容,str = “finally”; system.out.println(“我有执行到吗”);, 再次回到以前的路径,继续走return “try”,形成返回路径之后,这里的return的返回引用就不是变量str 了,而是str2引用的值”try”字符串。是不是对这个现象有了一定的了解。嘿嘿,这里我们再转换下:
public class trycatchfinallydemo2 { public static void main(string[] args) {// test();// system.out.println(test2()); // try// system.out.println(test3()); // catch system.out.println(test4()); } public static string test4() { string str = ""; try { str = "try"; return str; }catch(exception e) { str = "catch"; return str; }finally { str = "finally"; return str; } }}
这里我们猜测下,结果是什么呢?
运行结果:finally
我们发现try语句中的return语句给忽略。可能jvm认为一个方法里面有两个return语句并没有太大的意义,所以try中的return语句给忽略了,直接起作用的是最后finally中的return语句,就又重新形成了一条返回路径,所以这次返回的是“finally”字符串。再看一下: 我们是不是知道finally语句是一定执行的,但是能有办法使他不执行吗?
既然我说了,那么就是一定有的啦。
public class trycatchfinallydemo3 { public static void main(string[] args) { try{ system.out.println(10 / 0); }catch(exception e) { e.printstacktrace(); system.exit(0); }finally { system.out.println("finally我有执行到吗"); } }}
执行结果:
java.lang.arithmeticexception: / by zero at com.it.test3.trycatchfinallydemo3.main(trycatchfinallydemo3.java:6)
可以发现:
当只有在try或者catch中调用退出jvm的相关方法,此时finally才不会执行,否则finally永远会执行。
可以根据下图来选择是捕获异常,声明异常还是抛出异常
我们在日常处理异常的代码中,应该遵循的原则:
不要捕获类似exception 之类的异常,而应该捕获类似特定的异常,方便排查问题,而且也能够让其他人接手你的代码时,会减少骂你的次数。不要生吞异常。这是异常处理中要特别注重的事情。如果我们不把异常抛出来,或者也没有输出到日志中,程序可能会在后面以不可控的方式结束。有时候需要线上调试代码。java 类库中有许多资源需要通过 clo 方法进行关闭。比如 inputstream、outputstream等。作为开发人员经常会忽略掉资源的关闭方法,导致内存泄漏。当然不是我啦!
在jdk1.7之前呢,try-catch-finally语句是确保资源会被关闭的最佳方法,就算异常或者返回也一样可以关闭资源。
让我们先看看之前我们如何关闭资源吧:public static string readfile(string path) {是不是我们必须finally语句块中手动关闭资源,否则会导致资源的泄露
在jdk1.7及以后的版本:
jdk1.7 中引入了try-with-resources 语句。
try-with-resources 语句是一个声明一个或多个资源的try语句。try-with-resources 语句确保在语句的最后每个资源都被关闭,只要是实现了autocloable接口或者是cloable接口的对象都可以使用try-with-resources 来实现异常处理和关闭资源。实际上,在编译时也会进行转化为try-catch-finally语句。那我们来看看怎么使用吧:
格式:
try (创建流对象语句,如果多个,使用';'隔开) { // 读写数据} catch (ioexception e) { e.printstacktrace();}
演示下:
/** * jdk1.7之后就可以使用try-with-resources,不需要 * 我们在finally块中手动关闭资源 */public class trywithresourcesdemo { public static string readlineformfile(string path) { try (bufferedreader br = new bufferedreader(new filereader(path))) { return br.readline(); } catch (ioexception e) { e.printstacktrace(); } return null; }}
两者的对比:
代码精炼,在jdk1.7之前都有finally块,如果使用一些框架可能会将finally块交由框架处理,如spring。jdk1.7及以后的版本只要资源类实现了autocloable或cloable程序在执行完try块后会自动clo()所使用的资源无论br.readline()是否抛出异常。代码更完全。在出现资源泄漏的程序中,很多情况是开发人员没有或者开发人员没有正确的关闭资源所导致的。jdk1.7之后采用try-with-resources 的方式,则可以将资源关闭这种与业务实现没有很大直接关系的工作交给jvm 完成。省去了部分开发中可能出现的代码风险。以readlineformfile方法为例,如果调用 readline()和 clo()方法都抛出异常,后一个异常就会被禁止,以保留第一个异常。我们说了java中不同的异常类,分别表示着某一种具体的异常情况,那么在开发中,当java内置的异常都不能明确的说明异常情况的时候,需要创建自己的异常。例如年龄负数问题,考试成绩负数问题。
什么是自定义异常类呢:
在开发中根据自己业务的异常情况来自己定义异常类。例如:登录系统中,年龄能为负数吗,不能就需要自己定义一个登录异常类。
public class myexception extends exception { public myexception(){ } public myexception(string message){ super(message); } }
首先定义一个登陆异常类loginexception :/**需求:我们模拟登陆操作,如果用户名已存在,则抛出异常并提示:亲,该用户名已经被注册。这个相信大家也经常见到吧。
模拟登陆操作,使用数组模拟数据库中存储的数据,并提供当前注册账号是否存在方法用于判断。
public class logintest { // 模拟数据库中已存在账号 private static string[] names = {"hello", "world", "fish"}; public static void main(string[] args) { //调用方法 try{ // 可能出现异常的代码 checkurname("fish"); system.out.println("注册成功");//如果没有异常就是注册成功 } catch(loginexception e) { //处理异常 e.printstacktrace(); } } //判断当前注册账号是否存在 //因为是编译期异常,又想调用者去处理 所以声明该异常 public static boolean checkurname(string uname) throws loginexception { for (string name : names) { if(name.equals(uname)){//如果名字在这里面 就抛出登陆异常 throw new loginexception("亲"+name+"已经被注册了!"); } } return true; }}
执行结果:注册成功。
相信各位看官都对异常这一个体系有了一定了解,在我们实际应用当中,一般都会对可能出现异常的地方进行一些有效处理,比如在如果出现异常的情况,可能会返回到友好界面或者友好信息给用户,总不可能把一些异常信息给用户吧,这对用户的体验是相当差劲滴!所以异常处理相当必要!
小伙伴们,帮忙一键三连呀
题外话,我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在java学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多程序员朋友无法获得正确的资料得到学习提
本文发布于:2023-04-05 07:37:05,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/f07d2763ade663c432c6fa1186e86953.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:程序异常退出是什么原因(0x40000015装系统出现c报错).doc
本文 PDF 下载地址:程序异常退出是什么原因(0x40000015装系统出现c报错).pdf
留言与评论(共有 0 条评论) |