概述
对异常的理解
程序在运行过程中出现不正常情况。是对问题的描述,将问题进行对象的封装。
异常的由来
问题也是现实生活中一个具体的事物,也可以通过Java的类的形式进行描述,并封装成对象。
对于问题的划分
一种是严重的问题,一种是非严重的问题。
- 对于严重的:Java通过Error类进行描述。
对于Error一般不编写针对性的代码对其进行处理。 对于非严重的:Java通过Exception类进行描述。
对于Exception可以使用针对性的处理方式进行处理。
无论Error还是Exception都具有一些共性内容。
比如:不正常情况的信息,引发原因。异常体系
Throwable |--Error |--Exception |--RuntimeException
异常体系的特点:
异常体系中的所有类以及被建立的对象都具备可抛性。
异常的处理
代码语句格式
try { 需要被检测的代码 } catch(异常类 变量) { 处理异常的代码(处理方式) } finally { 一定会执行的语句; }
对异常的常见操作方法
String getMessage();
String toString();
void printStackTrace();
class Demo { int div(int x,int y) { return x/y; } } public class ExceptionDemo { public static void main(String[] args) { Demo d = new Demo(); try { int z = d.div(1,0); System.out.println(z); } catch (Exception e) { //Exception e = new ArithmeticExceptin(); System.out.println(e.getMessage()); // /by zero System.out.println(e.toString()); // 异常名称:异常信息 e.printStackTrace(); // 异常名称:异常信息,异常出现的位置 //(jvm默认的处理异常的机制就是在调用printStackTrace()方法) } System.out.println("over"); } }
运行结果
/ by zero java.lang.ArithmeticException: / by zero java.lang.ArithmeticException: / by zero at Demo.div(ExceptionDemo.java:3) at ExceptionDemo.main(ExceptionDemo.java:10) over
异常声明throws
- throws关键字用来声明一个方法有可能会出现问题。
- throws关键字可以将异常抛给调用者,可以层层向上抛,直到最后由Java虚拟机抛出。
比如:
class Demo { int div(int x,int y) throws Exception { return x/y; } } public class ExceptionDemo1 { public static void main(String[] args) throws Exception { Demo d = new Demo(); int y = d.div(1,0); System.out.println(y); System.out.println("over"); } }
- 如果调用者对被调用方法抛出的异常不加处理会出现编译错误。
class Demo { int div(int x,int y) throws Exception { return x/y; } } public class ExceptionDemo2 { public static void main(String[] args) { Demo d = new Demo(); int y = d.div(1,0); System.out.println(y); System.out.println("over"); } }
上述代码编译会提示:
Error:(9, 22) java: 未报告的异常错误java.lang.Exception; 必须对其进行捕获或声明以便抛出
我们对可能出现的异常加以处理:
class Demo { int div(int x,int y) throws Exception { return x/y; } } public class ExceptionDemo3 { public static void main(String[] args) { Demo d = new Demo(); try { int y = d.div(1,0); System.out.println(y); } catch (Exception e) { System.out.println(e.toString()); } System.out.println("over"); } }
运行结果:
java.lang.ArithmeticException: / by zero over
对可能出现异常的div方法进行处理之后,程序可以正常编译运行。
多异常处理
1.声明异常时,声明为更为具体的异常,这样可以处理得更具体。
2.方法声明有几个异常,对应就有几个catch块。(不要定义多余的catch块)
如果多个catch块中的异常出现继承关系,父类异常catch块放在最下面。
class Demo { int div(int x,int y) throws ArithmeticException,ArrayIndexOutOfBoundsException { int[] arr = new int[x]; System.out.println(arr[3]); return x/y; } } public class ExceptionDemo4 { public static void main(String[] args) { Demo d = new Demo(); try { int y = d.div(2,0); } catch (ArithmeticException e) { System.out.println("被零除了"); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("数组角标越界"); } } }
运行结果:
数组角标越界
上述代码中显然有两个一场出现,但是当方法中出现一个异常之后方法会停止运行,不会继续执行下去。
自定义异常
项目中会出现特有的问题,而这些问题并未被Java描述并封装对象。所以对这些特有的问题可以按照Java的对问题封装的思想,将特有的问题,进行自定义的异常封装。
需求:在本程序中,对于除数是负数,也视为是错误的。
当在函数内部出现了throw抛出异常对象,那么就必须要给对应的处理。
要么在内部使用try catch处理。
要么在函数上声明让调用者处理。
class FuShuException extends Exception { } class Demo { int div(int x,int y) throws FuShuException{ if(y<0) { throw new FuShuException();//通过throw关键字手动抛出一个自定义异常对象 } return x/y; } } public class ExceptionDemo5 { public static void main(String[] args) { Demo d = new Demo(); try { int y = d.div(1,-1); System.out.println(y); } catch (FuShuException e) { System.out.println(e.toString()); System.out.println("除数出现负数了"); } } }
运行结果
FuShuException 除数出现负数了
我们可以发现以上的代码运行的结果中只有异常的名称,却没有异常的信息。因为我们并未在自定义的异常中定义具体信息。
定义异常信息
因为父类中已经把异常信息的操作完成了。所以子类只要在构造时,将异常信息传递给父类通过super语句,就可以通过getMessage方法获取自定义的异常信息。
class FuShuException extends Exception { private int value; FuShuException(){ super(); } FuShuException(String message,int value) { super(message); this.value = value; } public int getValue() { return value; } } class Demo { int div(int x,int y) throws FuShuException{ if(y<0) { throw new FuShuException("--被零除了--",y);//通过throw关键字手动抛出一个自定义异常对象 } return x/y; } } public class ExceptionDemo5 { public static void main(String[] args) { Demo d = new Demo(); try { int y = d.div(1,-1); System.out.println(y); } catch (FuShuException e) { System.out.println(e.toString()); System.out.println("除数出现了负数:"+e.getValue()); } } }
运行结果:
FuShuException: --被零除了-- 除数出现了负数:-1
注意
自定义异常必须是是自定义类继承Exception
异常体系中的异常类和异常对象都具备可抛性,这个可抛性是Throwable这个体系中独有的特点。只有这个体系中的类和对象才可以被throw和throws操作。
throw和throws的区别
- throws使用在函数上,throw使用在函数内。
- throws后面跟的是异常类,可以跟多个用逗号隔开。throw后跟的是异常对象。
RuntimeException
异常分两种
- 编译时被检测的异常。
- 编译时不被检测的异常(运行时的异常。RuntimeException以及其子类)
RuntimeException是Exception中的一个特殊的子类。
如果在函数内容中抛出异常,函数上可以不用声明。
如果在函数上声明了异常,调用者可以不用进行处理。
之所以不用在函数上声明是因为不需要让调用者处理,当该异常发生,程序希望停止,对代码进行修正,而不是去让调用者处理。
自定义异常时,如果该异常发生,无法再继续进行运算,就让自定义异常继承RuntimeException。
class FuShuException extends RuntimeException{ FuShuException(String message) { super(message); } } class Demo { int div(int x,int y) { //函数上为声明异常也可以正常编译 if (y < 0) { throw new FuShuException("--除数为负数--");//在函数内容中抛出异常 } else if(y == 0){ throw new ArithmeticException("--除数为零--");//在函数内容中抛出异常 } return x/y; } } public class ExceptionDemo6 { public static void main(String[] args) { Demo d = new Demo(); try { int y = d.div(1,-1); } catch (FuShuException e) { System.out.println(e.toString()); } catch (ArithmeticException e) { System.out.println(e.toString()); } } }
finally
try { } catch () { } finally { }
finally中存放的是一定会被执行(无论是否有异常)的代码,即使catch中有return语句。
finally只有一种情况不会被执行,当代码执行到System.exit(0);
例如:如果需要对数据库进行操作,无论有没有成功连接上数据库,在最后操作完成的时候,都需要断开数据库的连接,以释放有限的资源。
异常处理语句的另一种格式
try { } finally { }
异常在覆盖时的特点
- 子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法只能抛出父类的异常或者该异常的子类。子类程序在继承时不能抛出新的异常。
- 如果父类方法抛出多个异常,那么子类在覆盖方法时,只能抛出父类异常的子集。
- 如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常如果子类方法发生了异常,就必须要进行try处理,不可以抛出。
异常的好处
- 将问题进行封装。
将正常流程代码和问题处理代码相分离,增强代码可读性。
异常的处理原则
- 两种处理方式: try和throws
- 调用到抛出异常的功能时,抛出几个,就需要处理几个。一个try对应多个catch
- 多个catch需要将父类异常的catch放到最下面。
- catch内,需要定义针对性处理方式,不要简单地定义printStackTrace。
- 当捕获到地异常,无法处理,可以继续在catch中抛出该无法处理的异常
- 如果该异常处理不了,但并不属于该功能出现的异常,可以将该异常进行转换然后再抛出
- 如果异常可以处理,但需要使抛出的异常和本功能相关,也可以对异常进行转换
异常练习
有圆形和长方形,都可以获取面积,对于面积,如果出现非法的数值,视为是获取面积出现问题。
出现的问题通过异常来表示。(使用异常可以,使问题处理代码和正常流程代码分开)
interface Shape { void getArea(); } class InvaidValueException extends RuntimeException {//如果一个传入一个图形的参数有错误,我们无法对其进行有效的处理,只抛出异常即可,所以继承RuntimeException InvaidValueException(String message) { super(message); } } class Rec implements Shape { private double len,wid; Rec(double len,double wid) { if(len <= 0 || wid <= 0) throw new InvaidValueException("非法值"); this.len = len; this.wid = wid; } @Override public void getArea() { System.out.println(wid*len); } } class Circle implements Shape { private double r; public static final double PI = 3.14; Circle(double r) { if(r <= 0) throw new InvaidValueException("非法值"); this.r = r; } @Override public void getArea() { System.out.println(r*r*PI); } } public class ExceptionDemo7 { public static void main(String[] args) { Rec R = new Rec(1,2); R.getArea(); Circle C = new Circle(-1); C.getArea(); System.out.println("over"); } }
上述程序的运行结果为:
2.0 Exception in thread "main" InvaidValueException: 非法值 at Circle.<init>(ExceptionDemo7.java:28) at ExceptionDemo7.main(ExceptionDemo7.java:41)
来源:https://www.cnblogs.com/liyuxin2/p/12380483.html