异常:
异常概述:
异常时程序运行时代码序列中产生的一种异常情况。这里采用的名词是异常而不是错误,是为了强调发生的事件未必是一个错误,异常也可能是某些很少出现的特殊事件。也可以理解为程序在运行时出现的不正常的情况。
例如:
1.想要打开的文件不存在
2.程序执行时除数为0
3.数组下标越界
...
一个除数为0 的例子
import java.util.Scanner; public class EXCEPTION { public static void main (String[] args) { int a,b,c; Scanner readScanner = new Scanner(System.in); a = readScanner.nextInt(); b = readScanner.nextInt(); c = a/b; System.out.println(c); } }
输入 10 0 在控制台会出现:
10 0 Exception in thread "main" java.lang.ArithmeticException: / by zero at EXCEPTION.main(EXCEPTION.java:10)
在main函数中出现了算术异常:被0除了。
这就是一个异常。
在Java中任何异常都是继承自Throwable的,Throwable分为Error和Exception,其中Error是不可恢复的错误,只有尽量不免,而Exception则是异常,是可以进行处理的。
异常的处理:try-catch-finally
try { //需要被检测的代码 } catch (Exception e) { // TODO: handle exception //处理异常的代码(处理方式) } finally{ //一定会执行的语句 }
还是刚才的那个程序,进行处理:
import java.util.Scanner; public class EXCEPTION { public static void main (String[] args) { int a,b,c; Scanner readScanner = new Scanner(System.in); a = readScanner.nextInt(); b = readScanner.nextInt(); try { c = a/b; System.out.println(c); } catch(Exception e) { System.out.println("出现异常,分母为0"); } } }
再输入 10 0
打印结果为:
10 0 出现异常,分母为0
程序运行到 c = a/b; 的时候出现了异常,相当于新创建了一个异常类的对象 //new Exception ();
catch捕获到了这个异常,并进行处理 :System.out.println("出现异常,分母为0");
对捕获到的异常对象进行常见方法操作:
1.String getMessage()获取异常的信息
在catch语句中加上:System.out.println(e.getMessage());
显示结果为:/ by zero
2.toString
在catch语句中加上:System.out.println(e.toString());
显示结果为:
java.lang.ArithmeticException: / by zero
显示 异常名称,异常信息
3.printStackTrace
打印堆栈中的追踪信息,此方法无返回值,不需打印,直接调用
Catch语句中,e.printStackTrace();
显示结果为:
java.lang.ArithmeticException: / by zero at EXCEPTION.main(EXCEPTION.java:11)
显示异常名称,异常信息,异常出现的位置
其实JVM默认的异常处理机制就是调用此方法。
throw和throws
import java.util.Scanner; class div { int div (int a,int b)throws Exception//在功能上通过throes关键字声明了该功能有可能会出现问题 { return a/b; } } public class EXCEPTION { public static void main (String[] args) { int a,b,c; div d = new div(); Scanner readScanner = new Scanner(System.in); a = readScanner.nextInt(); b = readScanner.nextInt(); c = d.div(a, b); System.out.println(c); } }
编译不能通过。
处理方式有两种:1.捕获(try-catch),2.抛出
第一种处理方式:捕获
import java.util.Scanner; class div { int div (int a,int b)throws Exception//在功能上通过throes关键字声明了该功能有可能会出现问题 { return a/b; } } public class EXCEPTION { public static void main (String[] args) { int a,b,c; div d = new div(); Scanner readScanner = new Scanner(System.in); a = readScanner.nextInt(); b = readScanner.nextInt(); try { c = d.div(a, b); System.out.println(c); } catch (Exception e) { // TODO: handle exception System.out.println(e.toString()); } } }
第二种方式:抛出throws
import java.util.Scanner; class div { int div (int a,int b)throws Exception//在功能上通过throes关键字声明了该功能有可能会出现问题 { return a/b; } } public class EXCEPTION { public static void main (String[] args)throws Exception { int a,b,c; div d = new div(); Scanner readScanner = new Scanner(System.in); a = readScanner.nextInt(); b = readScanner.nextInt(); c = d.div(a, b); System.out.println(c); } }
在主函数后加 throws Exception
将异常抛给虚拟机
显示结果:
1 0 Exception in thread "main" java.lang.ArithmeticException: / by zero at div.div(EXCEPTION.java:6) at EXCEPTION.main(EXCEPTION.java:25)
就相当于 将麻烦推给别人(JVM)了。
对多异常的处理:
我们在定义功能的时候有可能发生不止一个异常,需要我们对多个异常进行处理
比如说在刚才的异常的基础上,发生了数组下标越界。
class div { int div (int a,int b)throws ArithmeticException,ArrayIndexOutOfBoundsException { int arr[] = new int[b]; System.out.println(arr[5]);//有可能会造成数组越界 return a/b; } }
这是该如何处理?
1.声明异常时,建议声明更为具体的异常,这样处理的可以更具体
2.如果有多个异常,就需要对应多个catch语句进行捕获。当然只用一个catch语句,catch (Exception e)
也能实现处理,但是这样做没有针对性。
如果多个catch块中出现继承关系,父类异常catch块放在最下面。
public class EXCEPTION { public static void main (String[] args)//throws Exception { int a,b,c; div d = new div(); Scanner readScanner = new Scanner(System.in); a = readScanner.nextInt(); b = readScanner.nextInt(); try { c = d.div(a, b); System.out.println(c); } catch(ArithmeticException e) { System.out.println("00000"+"\n"+e.toString()); } catch (ArrayIndexOutOfBoundsException e) { // TODO: handle exception System.out.println("数组下标越界"+"\n"+e.toString()); } } }
逐个测试异常是否被处理
1.
4 1 数组下标越界 java.lang.ArrayIndexOutOfBoundsException: 5
第二个catch语句处理
2.
10 0 0 00000 java.lang.ArithmeticException: / by zero
第一个catch语句处理
3.有没有可能两种情况都发生?
10 0 0 00000 java.lang.ArithmeticException: / by zero
只处理了下标越界的问题,而没有处理0的问题,这是为什么?
按照程序执行的顺序,首先执行的是 System.out.println(arr[5]); 到这里发生了异常,程序就会停止并进行处理。
自定义异常:
因为项目中有可能会出现一些没有被Java所描述并封装对象的问题,对这些问题
按照Java对问题封装的思想,进行自定义异常。
除数为0已经被Java封装,现在假定除数为负数也是异常。
自定义异常类:
class FushuException extends Exception{ }
必须是自定义类继承Exception。
继承Exception的原因:
异常体系有一个特点:
异常类和异常对象都需要被抛出,他们都具备可抛性。这个可抛性是Throwable这个体系中的独有特点。只有这个体系中的类和对象才可以被throws、throw操作。
我们自定义的异常,Java 无法识别,无法自动抛出,只能手动抛出(throw)。
当在函数内部出现throw抛出异常对象,就必须要给出对应的处理动作,要么在函数内部进行try,要么在函数上声明(throws)。
一般情况下,函数内出现异常,函数上需要声明。
代码:
import java.util.Scanner; class FushuException extends Exception { } class div { int div (int a,int b)throws ArithmeticException,FushuException { if (b < 0) throw new FushuException(); return a/b; } } public class EXCEPTION { public static void main (String[] args)//throws Exception { int a,b,c; div d = new div(); Scanner readScanner = new Scanner(System.in); a = readScanner.nextInt(); b = readScanner.nextInt(); try { c = d.div(a, b); System.out.println(c); } catch(ArithmeticException e) { System.out.println("00000"+"\n"+e.toString()); } catch (FushuException e) { // TODO: handle exception System.out.println("出现负数"+"\n"+e.toString()); } } }
显示结果:
3 -2 出现负数 FushuException
发现打印结果中只有异常的名称,而没有异常的信息。
以为自定义的异常并未定义信息。
如何定义异常信息?
复写getMessage方法。
class FushuException extends Exception { private String s; public FushuException(String s) { super(); this.s = s; } public String getMessage () { return s; } }
复写完成后,在抛异常的时候,将信息内容传递过来。
class div { int div (int a,int b)throws ArithmeticException,FushuException { if (b < 0) throw new FushuException("出现负数"); return a/b; } }
显示结果:
3 -2 FushuException: 出现负数
这样做虽然没有问题,但是太麻烦了。
Exception都是继承自Throwable的。
Throwable中所有的方法在子类中都可以直接调用。
我们通过查API文档发现Exception类中没有定义新的方法,所有的方法都是继承自Throwable的。
所以只需将调用Exception的构造方法完成初始化,就可以调用父类中的getMessage方法。无需复写。
class FushuException extends Exception { public FushuException(String s) { super(s); } }
Throw和throws的区别:
Throws使用在函数上
Throw使用在函数内
Throws后面跟的是异常类,可以跟多个。
Throw后面跟的是异常对象。
RuntimeException是一个非常特殊的异常类。(运行时异常)
如果在函数内抛出该异常,函数上可以不用声明,编译一样通过。
如果在函数上声明了该异常,调用者可以不用处理(try、throw),编译一样通过。
class div { int div (int a,int b) { if (b == 0) throw new ArithmeticException(); return a/b; } } class div { int div (int a,int b)throws ArithmeticException { //if (b == 0) //throw new ArithmeticException(); return a/b; } }
这两种方式都可以通过编译。
之所以不用在函数上声明,是因为不需要让调用者处理。
当该异常发生,希望程序停止。因为运行时,出现了无法继续运算的情况,希望停止程序后,对代码进行修改。
自定义异常时,如果该异常的发生无法再继续进行运算的话,就让自定义的异常继承RuntimeException。
finally
finally中存放的是一定会执行的代码。
无论有没有异常,finally中的代码一定会被执行。
import java.util.Scanner; class FushuException extends Exception { private String s; public FushuException(String s) { super(s); } } class div { int div (int a,int b)throws FushuException { if ( b < 0) throw new FushuException("出现负数"); return a/b; } } public class EXCEPTION { public static void main (String[] args)//throws Exception { int a,b,c; div d = new div(); Scanner readScanner = new Scanner(System.in); a = readScanner.nextInt(); b = readScanner.nextInt(); try { c = d.div(a, b); System.out.println(c); } catch (FushuException e) { // TODO: handle exception System.out.println(e.toString()); } finally { System.out.println("finally"); } System.out.println("Over"); } }
显示结果:
4 1 4 finally Over
4 -2 FushuException: 出现负数 finally Over
Over和finally都被执行了,看上去好像没什么区别。
当发生这种情况的时候:当程序出现异常时,处理完成后,程序return了,那么finall中的代码还会执行吗?Over还会执行吗?
在catch块中加return
显示结果;
4 -1 FushuException: 出现负数 finally
finally依然会执行,但是over却不会执行。
finally代码块中存放的是一定会被执行的代码。无论有没有异常都会执行。通常用于关闭资源。
Try-catch-finally语句的格式:
3个格式:
1.
try { } catch (Exception e) { // TODO: handle exception }
2.
try { } catch (Exception e) { // TODO: handle exception } finally { }
3.
try { } finally { }
异常在子父类覆盖中的体现;
1.子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者改异常的子类。
class AException extends Exception { } class BException extends AException { } class CException extends Exception { } class Fu { void show () throws AException } class Zi extends Fu { void show ()throws AException 或者 BException { } }
2.如果父类方法抛出多个异常,那么子类在覆盖该方法时,只能抛出父类异常的子集。
3.如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常。如果子类方法发生了异常,就必须进行try处理,绝对不能抛。