1.异常的概念
package cn.jxufe.java.chapter08.demo01; public class Test01ArithmeticException { public static void main(String[] args) { // TODO Auto-generated method stub division(30, 3); division(30, 0); } public static void division(int a, int b) { System.out.println("计算除法"); System.out.println("结果。。。" + a / b); } }
2.异常类
3.异常和错误的区别
异常:指程序在编译、运行期间发生了某种异常(XxxException),我们可以对异常进行具体的处理。若不处理异常,程序将会结束运行。
错误:指程序在运行期间发生了某种错误(XxxError),Error错误通常没有具体的处理方式,程序将会结束运行。Error错误的发生往往都是系统级别的问题,都是jvm所在系统发生的,并反馈给jvm的。我们无法针对处理,只能修正代码。
package cn.jxufe.java.chapter08.demo01; public class Test02ExceptionAndError { /* * Throwable * Exception 异常 感冒,阑尾炎 * 将异常处理掉,可以继续执行 * RuntimeException * Error 非典,艾滋,癌 * 必须修改程序 */ public static void main(String[] args) { int[] arr = new int[999999999];//错误,没有那么大的堆内存 System.out.println(arr[3]); } }
4.异常对象的产生和处理流程
5.抛出异常throw和throws
在编写程序时,我们必须要考虑程序出现问题的情况。比如,在定义方法时,方法需要接受参数。那么,当调用方法使用接受到的参数时,首先需要先对参数数据进行合法的判断,数据若不合法,就应该告诉调用者,传递合法的数据进来。这时需要使用抛出异常的方式来告诉调用者。
在java中,提供了一个throw关键字,它用来抛出一个指定的异常对象。那么,抛出一个异常具体如何操作呢?
- 1,创建一个异常对象。封装一些提示信息(信息可以自己编写)。
- 2,需要将这个异常对象告知给调用者。怎么告知呢?怎么将这个异常对象传递到调用者处呢?通过关键字throw就可以完成。throw 异常对象;
throw用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行。
使用格式:
throw new 异常类名(参数);
例如: throw new NullPointerException("要访问的arr数组不存在"); throw new ArrayIndexOutOfBoundsException("该索引在数组中不存在,已超出范围");
声明:将问题标识出来,报告给调用者。如果方法内通过throw抛出了编译时异常,而没有捕获处理(稍后讲解该方式),那么必须通过throws进行声明,让调用者去处理。
声明异常格式:
修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2… { }
package cn.jxufe.java.chapter08.demo01; public class Test03Throw { public static void main(String[] args) throws Exception { // TODO Auto-generated method stub int[] arr = {}; int temp = getArray(arr); } public static int getArray(int[] arr) throws Exception { // 对方法参数进行合法性的判断,进行判断是不是null if (arr == null) { // 抛出异常的形式,告诉调用者 // 关键字 throw throw new Exception("数组不存在"); } // 对数组进行判断,判断数组中,是不是有元素 if (arr.length == 0) { // 抛出异常的形式,告诉调用者,数组没有元素 throw new Exception("数组中没有元素"); } int i = arr[arr.length - 1]; return 2 * i; } }
6.try-catch-finally捕获异常
捕获异常格式: try { //需要被检测的语句。 } catch(异常类 变量) { //参数。 //异常的处理语句。 } finally { //一定会被执行的语句。 }
try:该代码块中编写可能产生异常的代码。
catch:用来进行某种异常的捕获,实现对捕获到的异常进行处理。
finally:有一些特定的代码无论异常是否发生,都需要执行。另外,因为异常会引发程序跳转,导致有些语句执行不到。而finally就是解决这个问题的,在finally代码块中存放的代码都是一定会被执行的。
package cn.jxufe.java.chapter08.demo01; public class Test04TryCatchFinally { public static void main(String[] args) { // TODO Auto-generated method stub int[] arr = null; try { int i = getArray(arr); System.out.println(i); } catch (NullPointerException ex) { System.out.println("###" + ex); } catch (ArrayIndexOutOfBoundsException ex) { System.out.println("!!!!!!" + ex); } System.out.println("Game Over"); } /* * 定义方法,抛出异常 * 调用者使用try catch */ public static int getArray(int[] arr) throws NullPointerException, ArrayIndexOutOfBoundsException { // 对数组判空 if (arr == null) { // 手动抛出异常,抛出空指针异常 throw new NullPointerException("数组不存在"); } // 对数组的索引进行判断 if (arr.length < 3) { // 手动抛出异常,抛出数组的索引越界异常 throw new ArrayIndexOutOfBoundsException("数组没有3索引"); } return arr[3] + 1; } }
7.多个异常处理的细节
package cn.jxufe.java.chapter08.demo01; /* * 多catch写在一起 * 细节: * catch小括号中,写的是异常类的类名 * 有没有顺序的概念,有 * * 平级异常: 抛出的异常类之间,没有继承关系,没有顺序 * NullPointerException extends RuntimeException * NoSuchElementException extends RuntimeException * ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException extends RuntimeException * * 上下级关系的异常 * NullPointerException extends RuntimeException extends Exception * 越高级的父类,写在下面 */ public class Test05Catch { public static void main(String[] args) { // TODO Auto-generated method stub try { } catch (NullPointerException ex) { } catch (Exception ex) { } } public static void function(int a) throws NullPointerException, Exception { if (a == 0) { throw new NullPointerException(); } if (a == 1) { throw new Exception(); } } }
8.运行异常
package cn.jxufe.java.chapter08.demo01; import java.util.Scanner; /* * 异常分为编译异常和运行时期异常 * 编译异常: 调用了抛出异常的方法,不处理编译失败 (try throws) * 运行异常: 抛出的异常是RuntimeException类,或者是他的子类 * * 运行异常的特点: * 方法内部抛出的异常是运行异常, new XXXException * 方法的声明上,不需要throws语句,调用者,不需要处理 * 设计原因: * 运行异常,不能发生,但是如果发生了,程序人员停止程序修改源代码 * * 运行异常: 一旦发生,不要处理,请你修改源代码, 运行异常一旦发生,后面的代码没有执行的意义 */ public class Test06QuotientWtihException { public static void main(String[] args) { // TODO Auto-generated method stub Scanner input = new Scanner(System.in); System.out.println("enter two integers: "); int number1 = input.nextInt(); int number2 = input.nextInt(); try { int result = quotient(number1, number2); System.out.println(number1 + " / " + number2 + " is " + result); } catch (ArithmeticException e) { // TODO: handle exception System.out.println(e+"不能除以0"); } } public static int quotient(int number1, int number2) {//不用声明异常 if (number2 == 0) throw new ArithmeticException("Divisor cannot be zero");//ctrl+T查看继承关系:运行异常,不用声明异常 return number1 / number2; } }
运行异常的案例
package cn.jxufe.java.chapter08.demo01; public class Test07RuntimeException { public static void main(String[] args) { // TODO Auto-generated method stub double d = 0; try { d = getArea(-1); } catch (Exception e) { // TODO Auto-generated catch block } System.out.println(d); } /* * 定义方法,计算圆形的面积 * 传递参数0,或者负数,计算的时候没有问题 * 但是,违反了真实情况 * 参数小于=0, 停止程序,不要在计算了 */ public static double getArea(double r) throws Exception{ if(r <= 0) throw new Exception("圆形不存在"); return r*r*Math.PI; } }
这个例子说明,有些参数有问题,导致后面的程序无法计算了,那么就应该用RuntimeException。
package cn.jxufe.java.chapter08.demo01; public class Test07RuntimeException { public static void main(String[] args) { // TODO Auto-generated method stub double d = getArea(-1); System.out.println(d); } /* * 定义方法,计算圆形的面积 * 传递参数0,或者负数,计算的时候没有问题 * 但是,违反了真实情况 * 参数小于=0, 停止程序,不要在计算了 */ public static double getArea(double r) { if (r <= 0) throw new RuntimeException("圆形不存在"); return r * r * Math.PI; } }
9.使用异常的好处
package cn.jxufe.java.chapter08.demo01; import java.util.InputMismatchException; import java.util.Scanner; public class Test08MismatchException { public static void main(String[] args) { // TODO Auto-generated method stub Scanner input = new Scanner(System.in); boolean continueInput = true; System.out.println("enter an integer:"); do { try { int number = input.nextInt(); System.out.println("the number entered is " + number); continueInput = false; } catch (InputMismatchException e) { // TODO: handle exception System.out.println("try again : an integer is required "); input.nextLine(); //输入不合法可以连续输入 } } while (continueInput); System.out.println("输入完成。。。"); } }
10.从异常中获取信息
package cn.jxufe.java.chapter08.demo01; import com.sun.management.jmx.Trace; public class Test09Exception { public static void main(String[] args) { // TODO Auto-generated method stub try { System.out.print(sum(new int[] {1,2,3,4,5})); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); System.out.println(" "+ e.getMessage()); System.out.println(" " + e.toString()); System.out.println(" Trace Info Obtained from getStackTrace"); StackTraceElement[] stackTraceElements = e.getStackTrace(); for (int i = 0; i < stackTraceElements.length; i++) { System.out.println("method " + stackTraceElements[i].getMethodName()); System.out.println("(" + stackTraceElements[i].getClassName()+":"); System.out.println(stackTraceElements[i].getLineNumber()+")"); } } } public static int sum(int[] list) { int result = 0; for (int i = 0; i <= list.length; i++) { //有一个<=的异常 result += list[i]; } return result; } }
11.异常在方法重写中的细节
- 子类覆盖父类方法时,如果父类的方法声明异常,子类只能声明父类异常或者该异常的子类,或者不声明。
例如: class Fu { public void method () throws RuntimeException { } } class Zi extends Fu { public void method() throws RuntimeException { } //抛出父类一样的异常 //public void method() throws NullPointerException{ } //抛出父类子异常 }
- 当父类方法声明多个异常时,子类覆盖时只能声明多个异常的子集。
例如: class Fu { public void method () throws NullPointerException, ClassCastException{ } } class Zi extends Fu { public void method()throws NullPointerException, ClassCastException { } public void method() throws NullPointerException{ } //抛出父类异常中的一部分 public void method() throws ClassCastException { } //抛出父类异常中的一部分 }
- 当被覆盖的方法没有异常声明时,子类覆盖时无法声明异常的。
例如: class Fu { public void method (){ } } class Zi extends Fu { public void method() throws Exception { }//错误的方式 }
举例:父类中会存在下列这种情况,接口也有这种情况
问题:接口中没有声明异常,而实现的子类覆盖方法时发生了异常,怎么办?
答:无法进行throws声明,只能catch的捕获。万一问题处理不了呢?catch中继续throw抛出,但是只能将异常转换成RuntimeException子类抛出。
interface Inter { public abstract void method(); } class Zi implements Inter { public void method(){ //无法声明 throws Exception int[] arr = null; if (arr == null) { //只能捕获处理 try{ throw new Exception(“哥们,你定义的数组arr是空的!”); } catch(Exception e){ System.out.println(“父方法中没有异常抛出,子类中不能抛出Exception异常”); //我们把异常对象e,采用RuntimeException异常方式抛出 throw new RuntimeException(e); } } } }
12.自定义异常
通过阅读异常源代码:发现java中所有的异常类,都是继承Throwable,或者继承Throwable的子类。这样该异常才可以被throw抛出。
说明这个异常体系具备一个特有的特性:可抛性:即可以被throw关键字操作。
并且查阅异常子类源码,发现每个异常中都调用了父类的构造方法,把异常描述信息传递给了父类,让父类帮我们进行异常信息的封装。
例如NullPointerException异常类源代码: public class NullPointerException extends RuntimeException { public NullPointerException() { super();//调用父类构造方法 } public NullPointerException(String s) { super(s);//调用父类具有异常信息的构造方法 } }
package cn.jxufe.java.chapter08.demo01; public class Test10OurClassException { public static void main(String[] args) { // TODO Auto-generated method stub int avg = getAvg(50, 60, -70, 80); System.out.println(avg); } /* * 传递成绩,计算成绩的平均数 * 成绩没有负数,需要抛出异常,停止运算 */ public static int getAvg(int... source) { int sum = 0; for (int s : source) { if (s < 0) { throw new FuShuException("成绩错误 " + s); } sum = sum + s; } return sum / source.length; } } /* * 自定义异常 * 继承Exception,或者继承RuntimeException * 构造方法中,super将异常信息,传递给父类 */ class FuShuException extends RuntimeException { public FuShuException(String s) { super(s); } public FuShuException() { } }