异常——try、catch、finally、throw、throws

异常(exception)就是程序在执行过程中发生的、破坏程序正常指令流的事件。

异常是对象,对象都要用类来定义。异常的根类是java.lang.Throwable。

 

 

 异常分为三类:

  • 检查性异常(checked exception):

    不是修改程序能解决的问题。

    java语法规定:受检查异常,比如IO异常,SQL异常,必须向上声明抛出(使用throwrs关键字)或者处理掉否则无法通过编译。

    Java要求非运行异常必须函数抛出,让编译器帮助定位异常出现的代码段,并且必须使用try、catch块,否则编译都不会通过,所以也叫可检查异常。

    最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。

    例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。

    

    //既要声明(throws)也要抛出(throw),调用时用try、catch捕捉
    public void last() throws IOException{
        throw new IOException();
    }

 

  • 运行时异常(runtime exception):

    是程序写的有问题。

    运行时异常是可能被程序员避免的异常。

    与检查性异常相反,运行时异常可以在编译时被忽略。

  • 错误(error):

    错误不是异常,而是脱离程序员控制的问题。

    错误在代码中通常被忽略。

    例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。

 

常见可检查异常:Java编译器要求必须手动catch。

  • 操作数据库异常:SQLException
  • 输入输出异常:IOException
  • 文件未找到异常:FileNotFoundException
  • 反射操作异常:ReflectiveOperationException
  • 类未找到异常: ClassNotFoundException
  • 方法未找到异常:NoSuchMethodException
  • 字段未找到异常:NoSuchFieldException
  • 非法访问权限异常:IllegalAccessException
  • 实例化异常:InstantiationException
  • 不支持克隆异常:CloneNotSupportedException
  • 被中止异常:InterruptedException

常见运行时异常: RuntimeExecption在java.lang包下。我们可以不处理,出现异常时由虚拟机接管。

  • 访问数组越界:ArrayIndexOutOfBoundsException  
  • 输入数据类型与声明不匹配:InputMismatchException  
  • 数学运算异常:ArithmeticException  
  • 类转换异常:ClassCastException
  • 空指针:NullPointException
  • 数据存储异常:ArrayStoreException

详细参考博客:

https://blog.csdn.net/yuwenlanleng/article/details/84646448

https://blog.csdn.net/inflaRunAs/article/details/103542478

 

抛出异常有三种形式:①系统自动抛出 throw throws 

 

①系统自动抛出

 1   import java.util.Scanner;
 2   public class Test {
 3   
 4       public static void main(String[] args) {
 5           Scanner input =new Scanner(System.in);
 6           System.out.println("请输入被除数:");
 7                  try {                     
 8                        int num1=input.nextInt();
 9                        System.out.println("请输入除数:");
10                        int num2=input.nextInt();
11                        System.out.println(String.format("%d / %d = %d",num1, num2, num1 / num2));
12                   }catch (Exception e) {
13                       System.err.println("出现错误:被除数和除数必须是整数,"+"除数不能为零。");
14                       System.out.println(e.getMessage());
15                   }
16      }
17  }

 

运行结果:

System.err.println:输出错误信息,控制台呈红色。

当除数为0时,系统抛出异常,然后转而调用catch,catch捕捉到了异常进入catch内部执行。

我们没有写throw语句抛出异常,当然try-catch也可以不用,试试效果。

 

 如果不用try、catch捕捉:

    import java.util.Scanner;
    public class Test {
        public static void main(String[] args) {
            Scanner input =new Scanner(System.in);
            System.out.println("请输入被除数:");           
            int num1=input.nextInt();
            System.out.println("请输入除数:");
            int num2=input.nextInt();
            System.out.println(String.format("%d / %d = %d",num1, num2, num1 / num2));
      }
  }

运行结果:

 

 

 所以运行时异常是可以不用写throw语句的,调用层也可以不用try-catch。

 


 

throw

 

import java.util.Scanner;
public class Run {
  public static int quotient(int number1,int number2) {
        if(number2==0) 
            throw new ArithmeticException("除数不能为零") ;
        return number1 /number2;
        }
    public static void main(String[] args) {
        Scanner input =new Scanner(System.in);
        System.out.println("请输入被除数:");
        try {                 
            int num1=input.nextInt();
            System.out.println("请输入除数:");
            int num2=input.nextInt();
            int result=quotient(num1,num2);//调用quotien()
            System.out.println(String.format("%d / %d = %d",num1, num2, result));
            }catch (ArithmeticException e) {
                System.err.println("出现错误:被除数和除数必须是整数,"+"除数不能为零。");
                System.out.println(e.getMessage());
                }     
        System.out.println("继续...");
        }
}

 

运行结果:

 

方法quotient来抛出异常,该异常可以被调用者捕获和处理。

throw语句的执行称为抛出一个异常,异常类是java.lang.ArithmeticException。

当异常被抛出,正常的执行流程就被中断,throw相当于调用catch块,如果类型匹配则执行执行catch块,执行完后不反回到throw语句,而是执行catch块后的下一语句。

当然这个异常(ArithmeticException)是可以直接丢给系统抛出的,但是有些自定义异常就必须把抛出异常的方法写出来然后try-catch捕捉。

 

比如自定义一个除数为奇数时抛出异常的例子:

import java.util.Scanner;
public class MyException extends ArithmeticException {//继承Exception也可以
    public MyException() {
        super();
    }
    public MyException(String s) {
        super(s);
    }
}

public class Run {
  public static int quotient(int number1,int number2) {
        if((number2%2)==1) 
            throw new MyException("除数不能为奇数") ;
        return number1 /number2;
        }
    public static void main(String[] args) {
        Scanner input =new Scanner(System.in);
        System.out.println("请输入被除数:");
        try {                 
            int num1=input.nextInt();
            System.out.println("请输入除数:");
            int num2=input.nextInt();
            int result=quotient(num1,num2);//调用quotien()
            System.out.println(String.format("%d / %d = %d",num1, num2, result));
            }catch(MyException e){//子类异常在父类之前,所以MyException要在前,避免覆盖。
//如果MyException继承的是Exception,与ArithmeticException不构成父子关系,顺序就随便了。
System.err.println("出现错误:除数不能为奇数。"); }catch (ArithmeticException e) { System.err.println("出现错误:被除数和除数必须是整数,"+"除数不能为零。"); System.out.println(e.getMessage()); } System.out.println("继续..."); } }

运行结果:

 

 

 当除数为奇数时,quotien()方法抛出异常。第一次就是奇数抛异常然后被第一个catch捕获;第二次除数为0,系统会抛出运算异常(ArithmeticException)被第二个catch捕获。

 

下面来修改程序,如果自定义异常继承Exception类:

import java.util.Scanner;
public class MyException extends Exception {//修改为继承Exception
    public MyException() {
        super();
    }
    public MyException(String s) {
        super(s);
    }
}

public class Run {
  public static int quotient(int number1,int number2) throws MyException {//必须加throws声明
        if((number2%2)==1) 
            throw new MyException("除数不能为奇数") ;
        return number1 /number2;
        }
    public static void main(String[] args) {
        Scanner input =new Scanner(System.in);
        System.out.println("请输入被除数:");
        try {                 
            int num1=input.nextInt();
            System.out.println("请输入除数:");
            int num2=input.nextInt();
            int result=quotient(num1,num2);//调用quotien()
            System.out.println(String.format("%d / %d = %d",num1, num2, result));
            }catch (ArithmeticException e) {
                System.err.println("出现错误:被除数和除数必须是整数,"+"除数不能为零。");
                System.out.println(e.getMessage());
            }catch(MyException e){//此时catch块顺序随意
                System.err.println("出现错误:除数不能为奇数。");
            }         
        System.out.println("继续...");
        }
}

运行结果:

 

现在又有新的疑问了——为什么抛出异常的方法头要有声明throws? 接着往下看!

 


 

throws 

throws就是声明一系列可能抛出的异常,多个异常逗号分开。

1 public static int quotient(int number1,int number2) throws ArithmeticException{
2         if(number1==0) 
3             throw new ArithmeticException("除数不能为零") ;
4         return number1 /number2;
5     }

然后回到之前问题:

Excepiton分两类:checked exception、runtime exception;直接继承自Exception就是checked exception,继承自RuntimeException就是runtime的exception。

checked exception就是要强制你去处理这个异常(不管throws多少层,终归要在某个地方catch它);而runtime exception则没有这个限制,可以自由选择是否catch。

那些强制异常处理的代码块,必须进行异常处理,否则编译器会提示“Unhandled exception type Exception”错误警告。

 


 

finally

不论异常是否产生,finally子句总是会被执行。

    try {
        //
    }catch{
        //
    }finally {
        //此处代码都会执行
    }

 

posted @ 2020-07-30 16:53  codercql  阅读(361)  评论(0编辑  收藏  举报