异常:有异于常态,和正常情况不一样,有错误出现。阻止当前方法或作用域继续执行的问题,称之为异常。【说白了从广义上讲就是程序的运行与预期不符】
当程序出现异常的时候,就会得不到正常的运行以至于不正常退出,严重情况会造成数据的丢失,程序运行所占用的资源得不到有效释放
对于异常的正确处理能够将异常提示给编程人员或者用户,使本来已经中断了的程序以适当的方式继续运行或者是退出,并且能够保存用户的当前操作或者进行数据回滚,最后释放占用资源
运行时异常(RuntimeException)会由JAVA虚拟机自动抛出,并自动捕获,运行时异常的出现,绝大部分情况说明了代码本身有问题,应该重逻辑上改进代码。检查异常(CheckException)需要手动添加捕获以及处理语句
一般ERROR出现就表明程序彻底挂了,而Exception出现还能抢救一下
try-catch
以及try-catch-finally
,其作用分别对标Python中的try-except
以及try-except-finally
,如果try
中的代码出现异常,会把程序控制权交由catch
语句块,最终会运行finally
语句块【如果存在finally语句块】。
结构如下,对于catch
捕捉异常的方式采用就近原则【因为子类继承于父类,针对于父类的异常程序对于子类也是适用的】,所以应该先子类后父类进行编写【编译器对于catch
的编写会自动检查,对于错误的顺序会进行提示,所以无需慌促】。
try{
//一些会抛出异常的方法
} catch (Exception1 e){
//处理该异常的代码块
} catch (Exception2 e){
//处理该异常的代码块
}...(n个catch块)...{
} finally {
//最终将要执行的一些代码
}
Main.java
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
try {
System.out.println("请输入你的年龄:");
int age = input.nextInt();
System.out.println("十年后你" + (age + 10) + "岁");
System.out.println("请输入除数:");
int num = input.nextInt();
System.out.println("年龄是" + num + (age / num) + "倍");
}catch (InputMismatchException e){
System.out.println("你应该输入整数");
e.printStackTrace();
}catch (ArithmeticException e){
System.out.println("除数不能为0");
e.printStackTrace();
}catch (Exception e){
System.out.println("Unkonwn Error!");
e.printStackTrace();
}
finally {
System.out.println("程序结束!");
}
}
}
TryCatchTest.java
package com.template22;
public class TryCatchTest {
public static void main(String[] args) {
TryCatchTest tct = new TryCatchTest();
int result = tct.test();
System.out.println("test方法,执行完毕!返回值为:" + result);
int result2 = tct.test2();
System.out.println("test2方法,执行完毕!返回值为:" + result2);
int result3 = tct.test3();
System.out.println("test3方法,执行完毕!返回值为:" + result3);
}
/*
*divider(除数):
*result(结果):
*try-catch捕获while循环
*每次循环,divider减一,result=result+100/divider
*如果:捕获异常,打印输出“抛出异常了!!!”,返回-1
*/
public int test(){
int divider = 10;
int result = 100;
try{
while(divider > -1){
divider--;
result = result + 100/divider;
}
return result;
} catch (Exception e){
//输出异常以及异常抛出的位置
e.printStackTrace();
System.out.println("循环抛出异常了!!!");
return -1;
}
}
/*
*divider(除数):
*result(结果):
*try-catch捕获while循环
*每次循环,divider减一,result=result+100/divider
*如果:捕获异常,打印输出“抛出异常了!!!”,返回 result=999
* finally:打印输出“finally执行完毕了!!!”
*/
public int test2(){
int divider = 10;
int result = 100;
try{
while(divider > -1){
divider--;
result = result + 100/divider;
}
return result;
} catch (Exception e){
//输出异常以及异常抛出的位置
e.printStackTrace();
System.out.println("循环抛出异常了!!!");
return result = 999;
} finally {
System.out.println("finally执行完毕了!!!result值为" + result);
}
}
/*
*divider(除数):
*result(结果):
*try-catch捕获while循环
*每次循环,divider减一,result=result+100/divider
*如果:捕获异常,打印输出“抛出异常了!!!”
* finally:打印输出“finally执行完毕了!!!”
* 最后,返回1111作为结果
*/
public int test3(){
int divider = 10;
int result = 100;
try{
while(divider > -1){
divider--;
result = result + 100/divider;
}
} catch (Exception e){
//输出异常以及异常抛出的位置
e.printStackTrace();
System.out.println("循环抛出异常了!!!");
} finally {
System.out.println("finally执行完毕了!!!result值为" + result);
}
System.out.println("test3运行完毕!");
return 1111;
}
}
在TryCatchTest.java
中的test2
方法中,在执行完catch
语句块的return赋值语句后,并不会马上返回至main
方法,而是执行完finally
语句块后返回至main
方法。在TryCatchTest.java
中的test3
方法中,如果整个try-except-finally
都没有return
返回值语句,将执行语句块外的return
语句【如果语句块外存在return
语句】
虽然JAVA标准类库中提供了丰富的异常种类,但是在实际应用情景中也难免用到JAVA类库中没有的异常,因此我们需要使用自定义异常
自定义异常是自己定义的异常,它必须继承于JAVA标准类库中意思相近的异常类型,或者直接继承所有异常类的基类,也就是Exception
类型。
throw | throws |
---|---|
将产生的异常抛出(动作),写在方法体内部,表示具体抛出异常的动作 | 声明将要抛出何种类型的异常(声明) public void 方法名(参数列表)throws 异常列表{ //调用会抛出异常的方法或者:throw new Exception(); } throws 可以抛出多种异常类型,每种异常类型用逗号(,)隔开 |
如果某个方法调用到了会抛出异常的方法,那么必须添加try-catch
语句去尝试捕获这种异常,或者添加throws声明,来将异常的抛出给更上一层的调用者进行处理
可处理异常 | 抛出异常 | 不能处理异常 |
---|---|---|
通过try-catch 捕获并处理异常public void compute(){ /* *此处省略…代码… **/ try{ divide(5, 0); } catch (Exception e){ System.out.println(e.getMessage()); } } |
public void divide(int one, int two) throws Exception{ if two==0) throw new Exception(“两数相除,除数不能为0!!”); else System.out.println(“两数相除,结果为:”+one/two); } |
不能处理异常,将异常声明抛出,给更上一层调用者去处理 public void compute() throws Exception{ /* *此处省略…代码… **/ divide(5, 0); } |
class 自定义异常类 extends 异常类型{
//类体
}
自定义一个DrunkException
异常
DrunkException.java
package com.template22;
public class DrunkException extends Exception{
public DrunkException(){
}
public DrunkException(String message){
super(message);
}
}
JAVA中的异常链:将捕获的异常包装成一个新的异常,在新的异常里添加对原始异常的引用,再把新异常抛出,就如同链式反应一样,一个导致另一个。
定义ChainTest.java
实现异常链,需要用到前面自定义的DrunkException
这个类,这是自定义的一个异常
ChainTest.java
package com.template22;
public class ChainTest {
/**
* test1():抛出“喝大了”异常
* test2():调用test1(),捕获“喝大了”异常,并且包装成运行时异常,继续抛出
* main方法中,调用test2(),尝试捕获test2()方法抛出的异常
*/
public static void main(String[] args){
ChainTest ct = new ChainTest();
try{
ct.test2();
} catch (Exception e){
e.printStackTrace();
}
}
public void test1() throws DrunkException{
throw new DrunkException("喝酒别开车");
}
public void test2(){
try{
test1();
} catch (DrunkException e){
// RuntimeException newExc = new RuntimeException("司机一滴酒,情人两行泪");
// newExc.initCause(e);
// throw newExc;
throw new RuntimeException(e);
}
}
}
java.lang.RuntimeException: com.template22.DrunkException: 喝酒别开车
at com.template22.ChainTest.test2(ChainTest.java:31)
at com.template22.ChainTest.main(ChainTest.java:13)
Caused by: com.template22.DrunkException: 喝酒别开车
at com.template22.ChainTest.test1(ChainTest.java:20)
at com.template22.ChainTest.test2(ChainTest.java:25)
... 1 more
在代码中,我们首先自定义了一个异常DrunkException
【喝大了异常】,在ChainTest.java
中,我们先定义了一个test1()
方法用于抛出定义的异常,我们定义了一个test2()
方法使用try-catch
来捕获test1()
抛出的异常,并将这个异常包装成了一个新的异常RuntimeException
【运行时异常】并抛出,在main
方法中调用test2()
,尝试捕获test2()
方法抛出的异常。
1.处理运行时异常可以采用优化代码,采用逻辑加以控制去合理规避的同时辅助加上try-catch
进行处理
2.在多重catch
块后面,可以加一个catch(Exception)
来处理可能会被遗漏的异常
3.对于不确定的代码,也可以加上try-catch
,处理潜在的异常
4.尽量去处理异常,切忌只是简单的调用printStackTrace()
去打印输出
5.具体如何处理异常,需要根据不同的业务需求和异常类型去决定
6.尽量添加finally
语句块去释放占用资源,尤其是有网络连接和连接数据库的情况