08java进阶——异常

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() {
    }
}

原文地址:https://www.cnblogs.com/xinmomoyan/p/10958363.html