异常(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 { //此处代码都会执行 }