异常
异常:
运行期异常
编译期异常
Throwable是所有异常的父类也叫超父类
,Exception和它的子类都继承于Throwable。
Error与Exception的区别:
Error:类似于癌症。无法解决,只能修改代码
Error错误通常没有具体的处理方式,程序将会结束运行。
Exception:异常
运行期异常RuntimeException->癌症,无法解决,只能改代码
编译期异常 Exception ->感冒,可以处理
- 运行时异常:都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。 - 非运行时异常 (编译异常):是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。
异常过程解析
jvm虚拟机接收到异常对象会做两件事:
1.终止程序
2.将异常对象中的异常位置和异常信息以红字的形式打印在控制台;
jvm虚拟机在方法里面检查到异常,并发现这个异常是那种异常(如数组越界异常):
1.并创建异常对象Array IndexOf Bounds Exception并把异常对象抛给方法的调用者(main方法里面的调用语句如:int num=get(arr));抛完异常后代码终止程序;语句不会再往下执行
2.main方法里面的调用者在加收到异常后会在mian方法里面寻找解决方法,如果main方法里没有解决的办法那么会把异常抛给虚拟机jvm
异常的处理
throw 出现在函数体throws 出现在方法头里
抛出异常throw
throw用在方法内,不想解决这个异常,用来抛出一个异常对象,将这个异常对象传递到调用者处,不捕获异常就结束当前方法的执行。
关于抛出异常,还有一个点需要补充,那就是声明可检查异常。在设计程序的时候,如果一个方法明确可能发生某些可检查异常,那么,可以在方法的定义中带上这些异常,如此,这个方法的调用方就必须对这些可检查异常进行处理。
使用格式:
throw new 异常类名(参数);
声明异常throws
声明:将问题标识出来,报告给调用者。如果方法内通过throw抛出了编译时异常,而没有捕获处理(稍后讲解该方式),那么必须通过throws进行声明,让调用者去处理。
声明异常格式:
修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2… { }
public static int get(int[] arr)throws Exception{
throws的书写方式:修饰符 返回值类型 方法名(参数) Throws 异常类名1,异常类名2...
throws用于进行异常类的声明,若该方法可能有多种异常情况产生,那么在throws后面可以写多个异常类,用逗号隔开。
多个异常的情况,例如:
public static int getElement(int[] arr,int index) throws NullPointerException, ArrayIndexOutOfBoundsException {
throws是抛出指定异常的对象,格式: throw new 异常类名(参数)
throw 与 throws 区别
throw:
表示方法内抛出某种异常对象
如果异常对象是非 RuntimeException 则需要在方法申明时加上该异常的抛出 即需要加上 throws 语句 或者 在方法体内 try catch 处理该异常,否则编译报错
执行到 throw 语句则后面的语句块不再执行
throws:
方法的定义上使用 throws 表示这个方法可能抛出某种异常
需要由方法的调用者进行异常处理
package com.oracle.Demo01; public class demo5 { public static void main(String[] args) { int[] arr = {} ; try { int num = getArr(arr); System.out.println(num); /* 当产生异常时,必须有处理方式。要么捕获,要么声明。*/ } catch (NullPointerException ex) { //括号中()try中抛出的是什么异常,在括号中就定义什么异常类型。 System.out.println("出错了 "); ex.printStackTrace(); } catch (ArrayIndexOutOfBoundsException ex) { System.out.println("出错 "); ex.printStackTrace(); } catch (Exception ex) { System.out.println("出错了"); ex.printStackTrace(); } finally { System.out.println("一定会执行的代码"); //通过垃圾回收器,进行内存垃圾的清除 } System.out.println("哈哈"); }
Throw就是自己处理一个异常,有两种方式
1. 捕获异常
2. 抛出异常
Throws不对异常做出任何处理,抛出异常。可以写多个异常用逗号隔开
public static int getArr(int[] arr) throws NullPointerException, ArrayIndexOutOfBoundsException { if (arr == null) { throw new NullPointerException("你传的数组的值是null"); throw是抛出异常,不会声明别人并不知道;throws是声明异常,告诉别人自己哪里出了问题 } if (arr.length == 0) { throw new ArrayIndexOutOfBoundsException("你传的数组中没有值"); } int i = arr[arr.length - 1]; return i;}}
捕获异常:
抛出异常十分容易,抛出去便不用再理睬,但是,在一些场景下,必须捕获异常并进行相应的处理。如果某个异常发生后没有在任何地方被捕获,那么,程序将会终止。
try…catch…finally
特点:不会终止程序
Catch可以写多个
捕获异常格式:
try {
//需要被检测的语句。
}
catch(异常类 变量) { //参数。
//异常的处理语句。
}
finally {
//一定会被执行的语句。
}
try...catch...finally:此类处理异常,不会终止下面语句的执行但会把异常信息以红字的方式打印在控制台
public static void main(String[] args){
int[] arr=null;
try{
这里面会放容易出异常的语句,一般是main方法里调用异常方法的语句
int num=get(arr);
system.out.println(num);
}
catch(Exception ex){
括号中()try中抛出的是什么异常,在括号中就定义什么异常类型
system.out.println("出错了");
ex.printStackTrace}
finally{
system.out.println("一定会执行的代码")
}
public class demo4 { public static void main(String[] args){ int[] arr=null; try { int num=getArr(arr); System.out.println(num); }catch(Exception ex) { System.out.println("出错了"); ex.printStackTrace(); }finally { System.out.println("一定会执行的代码"); } System.out.println("哈哈"); } public static int getArr(int[]arr) throws Exception{ if(arr==null) { throw new Exception("你传的数组的值是null"); } int i=arr[arr.length-1]; return i; } }
try...catch处理的异常虚拟机jvm并不会终止程序,语句会继续执行下去
throw new Exception("你传的数组的值是null");
给Exception ex赋值了,这里的Excertion是根据下面方法抛的异常类型来接的,下面抛什么异常这里就用什么异常来接,和变量一样'
通过调用Exception的.printStackTrace()方法把下面方法里面的报错"你传的数组的值是null"以红字的形式打印在控制台;上面的ex被下面的语句赋值了
//catch里的传的参数()父类的必须要放最下面,子类的放上面,不然一个父类就把子类的都处理了,而且处理的结果不准确
try:该代码块中编写可能产生异常的代码。
catch:用来进行某种异常的捕获,实现对捕获到的异常进行处理。
finally:有一些特定的代码无论异常是否发生,都需要执行。另外,因为异常会引发程序跳转,导致有些语句执行不到。而finally就是解决这个问题的,在finally代码块中存放的代码都是一定会被执行的。
l try catch finally组合:检测异常,并传递给catch处理,并在finally中进行资源释放。
l try catch组合 : 对代码进行异常检测,并对检测的异常传递给catch处理。对异常进行捕获处理。
一个try 多个catch组合 : 对代码进行异常检测,并对检测的异常传递给catch处理。对每种异常信息进行不同的捕获处理。注意:这种异常处理方式,要求多个catch中的异常不能相同,并且若catch中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的catch处理,父类异常在下面的catch处理。
l try finally 组合: 对代码进行异常检测,检测到异常后因为没有catch,所以一样会被默认jvm抛出。异常是没有捕获处理的。但是功能所开启资源需要进行关闭,所有finally。只为关闭资源。最终虚拟机终止程序
public static void main(String[] arge){ int[] arr={1,2,3}; int num; try{ num=getArr(arr); system.out.println(num); } catch(Exception e){ //将异常类名、异常信息、异常位置以红字的形式打印在控制台:e.printStackTrace();//这个方法更适合我们日常使用 //获取异常信息:e.getMessage(); //获取异常类名+信息:e.toString(); //将异常类名,信息,异常位置以红字的方式打印在控制台 //e.printStackTrace(); //获取异常信息(简单获取) //System.out.println(e.getMessage()); //获取异常类名+异常信息 System.out.println(e.toString());
异常在方法重写中细节
l 子类覆盖父类方法时,如果父类的方法声明异常,子类只能声明父类异常或者该异常的子类,或者不声明。
例如:
class Fu { public void method () throws RuntimeException { } } class Zi extends Fu { public void method() throws RuntimeException { } //抛出父类一样的异常 //public void method() throws NullPointerException{ } //抛出父类子异常 }
l 当父类方法声明多个异常时,子类覆盖时只能声明多个异常的子集。
例如:
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 { } //抛出父类异常中的一部分 }
3、当被覆盖的方法没有异常声明时,子类覆盖时无法声明异常的。
例如:
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); } } } }
.运行异常的特点:运行异常无序throws处理在,直接把异常抛给虚拟机进行处理,调用者也无需处理次异常,运行时一旦出现异常只能修改异常代码子类在继承了父类的异常后在声明异常的时候需要声明父类的异常类或者是异常类的子类,或者无需声明异常
public class fu { public void eat() throws ParseException{} public void sleep(){} } public class zi extends fu { public void eat() throws ParseException { } @Override public void sleep() { // TODO Auto-generated method stub super.sleep(); } } 子类继承父类后;父类抛了异常,子类可以抛(抛的异常和父类一样)也可以不抛如果父类的方法没有抛异常那么子类也不能抛 调用抛了异常的方法这里不能throws要用tyr..catch;其中一个方法sleep()父类没有Throws所以这里就不能throws public class FuShuException extends RuntimeException { public FuShuException(String message) { super(message);// 给main方法里面的FuShuException()异常处理方法提供有参构造 super(message); } }
这里是子类继承父类的方法调父类现有的方法实现效果,这里的FuShuException直接调用父类RuntimeException已经写好的方法就能在打印台输出红字了 super(message)中的message传的是需要打印的内容
自定义异常:
,发现每个异常中都调用了父类的构造方法,把异常描述信息传递给了父类,让父类帮我们进行异常信息的封装。
格式:
Class 异常名 extends Exception{ //或继承RuntimeException public 异常名(){ } public 异常名(String s){ super(s); } }
l 自定义异常继承Exception演示
class MyException extends Exception{ /* 为什么要定义构造函数,因为看到Java中的异常描述类中有提供对异常对象的初始化方法。 */ public MyException(){ super(); } public MyException(String message) { super(message);// 如果自定义异常需要异常信息, 可以通过调用父类的带有字符串参数的构造函数即可。 } }
l 自定义异常继承RuntimeException演示
class MyException extends RuntimeException{ /* 为什么要定义构造函数,因为看到Java中的异常描述类中有提供对异常对象的初始化方法。 */ MyException(){ super(); } MyException(String message) { super(message);// 如果自定义异常需要异常信息,可以通过调用父类的带有字符串参数的构造函数即可。 } }
1.1 自定义异常的练习
定义Person类,包含name与age两个成员变量。
在Person类的有参数构造方法中,进行年龄范围的判断,若年龄为负数或大于200岁,则抛出NoAgeException异常,异常提示信息“年龄数值非法”。
要求:在测试类中,调用有参数构造方法,完成Person对象创建,并进行异常的处理。
l 自定义异常类
class NoAgeException extends Exception{ NoAgeException() { super(); } NoAgeException(String message) { super(message); } }
l Person类
class Person{ private String name; private int age; Person(String name,int age) throws NoAgeException { //加入逻辑判断。 if(age<0 || age>200) { throw new NoAgeException(age+",年龄数值非法"); } this.name = name; this.age = age; } //定义Person对象对应的字符串表现形式。覆盖Object中的toString方法。 public String toString() { return "Person[name="+name+",age="+age+"]"; } }
l 测试类
class ExceptionDemo{ public static void main(String[] args) { try { Person p = new Person("xiaoming",20); System.out.println(p); } catch (NoAgeException ex){ System.out.println("年龄异常啦"); } System.out.println("over"); } }
总结一下,构造函数到底抛出这个NoAgeException是继承Exception呢?还是继承RuntimeException呢?
l 继承Exception,必须要throws声明,一声明就告知调用者进行捕获,一旦问题处理了调用者的程序会继续执行。
l 继承RuntimeExcpetion,不需要throws声明的,这时调用是不需要编写捕获代码的,因为调用根本就不知道有问题。一旦发生NoAgeException,调用者程序会停掉,并有jvm将信息显示到屏幕,让调用者看到问题,修正代码。