异常的概念:
程序在运行过程中出现的特殊情况
异常处理的必要性:任何程序都可能存在大量的未知问题、错误;如果不对这些问题进行正确处理,则可能导致程序的中断,造成不必要的损失。
异常的分类:
Throwable:
可抛出的,一切错误或异常的父类,位于java.lang包中。
|- Error:
JVM、硬件、执行逻辑错误,不能手动处理。(程序无法处理的)
|-Exception:
程序在运行和配置中产生的问题,可处理。
|-RuntimeException:
运行时异常,可处理,可不处理。(编译可通过)
|-CheckedException:
受查异常,必须处理。(编译不通过)
package com.james; import java.io.File; public class TestException02 { public static void main(String[] args) { File f = new File(""); // NullPointerException(空指针异常)任何直接或者间接继承了RunTimeException的异常对象 // 在编译期间,即使不做额外的处理,编译器也是不会报错的 NullPointerException n; // f.createNewFile()属于检测异常,如果在编译期间不做处理,编译器会报错 //f.createNewFile(); System.out.println(); System.out.println("over"); } }
异常的产生
自动抛出异常
当程序在运行时遇到不符合规范的代码或结果时,会产生异常。
package com.james; public class TestException01 { public static void main(String[] args) { String str = null; // 如果程序出现了异常,那么程序会在出现异常处中断,即使后面的代码没有问题 System.out.println(str.length()); System.out.println("over"); } }
手动抛出异常
语法:throw new 异常类型(“实际参数”);
产生异常结果
相当于遇到return语句,导致程序因异常而终止。
异常的传递
定义:按照方法的调用链反向传递,如始终没有处理异常,最终会由JVM进行默认异常处理
受查异常:throws声明异常,修饰在方法参数列表后端。
运行时异常:因可处理可不处理,无需声明异常。
异常的处理
try{ 可能出现异常的代码 }catch(Exception e){ 异常处理的相关代码,如:getMessage()、printStackTrace() }finally{ 无论是否出现异常,都需执行的代码结构,常用于释放资源 }
异常机制的目的是,在程序出现异常后,不至于异常处之后的代码也中断执行
常见异常处理结构
try{ } catch{ }
适用于可以确定异常类型
package com.james; public class TestException04 { public static void main(String[] args) { String str = null; // 异常机制的目的是,在程序出现异常后,不至于异常处之后的代码也中断执行 try { System.out.println(str.length()); } catch (Exception e) // catch (NullPointerException e) (两个打印结果一样) // 不仅仅可以写Exception,也可以写Exception的子类,但是异常类型必须等于或大于代码异常类型的范围 //例如程序中出现的异常对象为NullPointerException,但是你要用IndexOutOfBoundsException(数组越界异常)来处理 //异常机制相当于“形同虚设”,程序依然会提前中断执行(over不会被打印) //这是因为NullPointerException和IndexOutOfBoundsExceptionde的父类都是RunTimeException //这两个异常之间没有从属关系,是并列关系 { System.out.println("exception"); // 程序出现异常后,可以通过e.printStackTrace()来进行异常信息的追踪 //e.printStackTrace()不写不会报编译错误,但是一般需要写,利于代码的维护,比较容易发现错误代码的位置 e.printStackTrace(); } System.out.println("over"); } } 输出: exception java.lang.NullPointerException: Cannot invoke "String.length()" because "str" is null at com.james.TestException03.main(TestException03.java:10) over
package com.james; public class TestException06 { public static void main(String[] args) { String str = "abc"; try { System.out.println(str.length()); System.out.println(str.charAt(99)); } catch (IndexOutOfBoundsException e) { //异常处理的类型符合代码异常的类型,则异常会被处理 System.out.println("exception"); e.printStackTrace(); } System.out.println("over"); } } 输出: 3 exception java.lang.StringIndexOutOfBoundsException: String index out of range: 99 at java.base/java.lang.StringLatin1.charAt(StringLatin1.java:48) at java.base/java.lang.String.charAt(String.java:712) at com.james.TestException06.main(TestException06.java:12) over
try{ } catch{ } catch{ }
对异常类型不确定,需要进行判断
不仅仅可以只有两个 catch{ }
一个try{ }多个catch{ }
一旦判定一种异常以后不会再判定其他异常(判定了一个代码以后不会再判定其他的代码)
package com.james; /** * 一段try块的代码可以对应多个catch块的代码 * 要注意:异常对象的编排顺序 * 先子类异常对象,后父类的异常对象 * 如果不存在继承关系,顺序可以随意 * 最大的异常时Exception * 考虑效率问题,不要上来就使用Exception,而应该使用各自具体的Exception对象 * @author james * */ public class TestException07 { public static void main(String[] args) { String str = null; String st = "ab"; try { System.out.println(str.length()); //只会对第一个代码进行判定,后续代码直接跳过 System.out.println(str.charAt(99)); } catch (NullPointerException e) { // 若程序中出现的异常对象为IndexOutOfBoundsException,则NullPointerException不能处理,会选择其他的异常处理结构 System.out.println("null poniter exception"); e.printStackTrace(); } catch (StringIndexOutOfBoundsException e) { System.out.println("string index exception"); e.printStackTrace(); } catch (IndexOutOfBoundsException e) { System.out.println("index exception"); e.printStackTrace(); } //IndexOutOfBoundsException,StringIndexOutOfBoundsException ,IndexOutOfBoundsException没有继承关系,顺序可以随意 catch (RuntimeException e) { System.out.println("Runtime exception"); e.printStackTrace(); } catch (Exception e) { System.out.println("exception"); e.printStackTrace(); } //RuntimeException 是Exception的子类,需要放在前面 System.out.println("over"); } } 输出: null poniter exception java.lang.NullPointerException: Cannot invoke "String.length()" because "str" is null at com.james.TestException07.main(TestException07.java:21) over
每段代码一个try{ }一个catch{ }
判定一个代码以后,继续判定下一个代码
package com.james; /** * 异常处理机制中,可以多段代码共享一个try catch结构 * 也可以每段代码编写自己的try catch结构, * 每个代码有自己的try catch结构的话,该代码出问题,后面的代码可以继续执行 * 多段代码共享try catch,那么在出问题的代码块之后的代码将不被执行 * @author james * */ public class TestException08 { public static void main(String[] args) { String str = null; // 异常机制的目的是,在程序出现异常后,不至于异常处之后的代码也中断执行 try { System.out.println(str.length()); } catch (NullPointerException e) { // 程序中出现的异常对象为NullPointerException,但是你要用IndexOutOfBoundsException来处理 // 异常机制“形同虚设”,程序依然会提前中断执行 System.out.println("string index exception"); // 程序出现异常后,可以通过e.printStackTrace()来进行异常信息的追踪 e.printStackTrace(); } /* try { System.out.println(str.charAt(99)); }//错误类型依旧是NullPointerException,判定的异常类型错误,程序会中断 catch(IndexOutOfBoundsException e) { System.out.println("index exception"); e.printStackTrace(); }*/ try { System.out.println(str.charAt(99)); } catch(NullPointerException e) { System.out.println("index exception"); e.printStackTrace(); } System.out.println("over"); } } 输出: string index exception java.lang.NullPointerException: Cannot invoke "String.length()" because "str" is null at com.james.TestException08.main(TestException08.java:18) index exception java.lang.NullPointerException: Cannot invoke "String.charAt(int)" because "str" is null at com.james.TestException08.main(TestException08.java:38) over
try{ } catch{ } finally{ }
package com.james; public class TestException09 { public static void main(String[] args) { try { String str = ""; System.out.println(str.length()); } catch (Exception e) { System.out.println("Exception"); e.printStackTrace(); }finally { System.out.println("finally"); //无论Catch的异常类型是否异常对象的类型,都会执行finally } System.out.println("over"); } }
try{ } catch{ } catch{ } finally{ }
package com.james; public class TestException10 { public static void main(String[] args) { try { String str = null; System.out.println(str.length()); } catch (NullPointerException e) { System.out.println("Nullpointer Exception"); e.printStackTrace(); } catch (Exception e) { System.out.println("Exception"); e.printStackTrace(); }//选择了一个catch以后,直接执行finally finally { System.out.println("finally"); } System.out.println("over"); } }
try{ } finally{ }
package com.james; /** * try catch结构也可以有try直接加finally结构 * 注意:该结构并不真正处理异常对象,try finally块之后的代码将不会被执行 * @author james * */ public class TestException11 { public static void main(String[] args) { try { String str = null; System.out.println(str.length()); } finally { System.out.println("finally"); } System.out.println("over"); } } 输出: finally Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "str" is null at com.james.TestException11.main(TestException11.java:15)
注:多重catch,遵循从子(小)到父(大)的顺序,父类异常在最后
不可以是单独的try{ }
拓展
在主函数main和无返回值的方法中的用法
package com.james; public class TestException12 { public static void main(String[] args) { try { System.out.println("try"); String str = "aaaa"; System.out.println(str.length()); } catch(Exception e) { System.out.println("catch"); e.printStackTrace(); } finally { System.out.println("finally"); } System.out.println("over"); } } 输出: try 4 finally over
带有返回值的方法中的用法
package com.james; /** * 对于try catch finally结构而言, * finally代码块一定在try或者catch的return之前执行 *每种可能的结果只能有一个返回值 * */ public class TestException13 { public static void main(String[] args) { System.out.println(getNum()); } public static int getNum() { try { System.out.println("try"); System.out.println(1 / 0); // return 1; } catch (Exception e) { System.out.println("catch"); return -1; }finally { System.out.println("finally"); // return 0; } /*在try catch结构中判定以后先暂存return的值,执行finally后在返回return的值 * * */ return 0; } } 输出: try catch finally -1
package com.james; /** * * */ public class TestException14 { public static void main(String[] args) { System.out.println(getNum()); } public static int getNum() { int a = 0; try { System.out.println("try"); // return瞬间,a的值为0,即使在finally代码块中对a做了额外的运算, // return a的值的瞬间的值永远是0,所以返回结果为0 return a; } catch (Exception e) { System.out.println("catch"); return -a; }finally { System.out.println("finally"); a++; // return a; } // return 0; } } 输出: try finally 0
自定义异常
概念:需继承Exception或Exception的子类,代表特定问题。
经验:异常类型名称望文生义,可在发生特定问题时抛出对应的异常。
常用构造方法:
无参构造方法
String message参数的构造方法
package com.james; public class IDLengthException extends Exception { public IDLengthException(String msg) { super(msg); } }
应用:验证身份证号位数
package com.james; public class TestMyException { public static void main(String[] args) throws IDLengthException { Person p = new Person(); p.setPid("123456789012345678"); p.setName("zhangsan"); p.setAddr("nanyaoguoji"); System.out.println(p); } } /** * throws和throw * throws用于方法上的异常声明,意思是当前方法中可能出现的所有的异常对象, * 都可以在这里进行声明,写在当前方法的头部, 如果有多个,可以使用逗号分隔 * * throw遇到某种具体的异常进行特殊的处理,进行异常的抛出处理 * 解决异常的一种手段,写在方法体内 * @author james * */ public class Person { private String pid; private String name; private String addr; @Override public String toString() { return "Person [pid=" + pid + ", name=" + name + ", addr=" + addr + "]"; } public String getPid() { return pid; } /** * 身份证号码的长度必须是15或者18 * @param pid * @throws IDLengthException */ public void setPid(String pid) throws IDLengthException//使用自定义异常进行判定 { if(!(pid.length() == 15 || pid.length() == 18)) { throw new IDLengthException("the length of ID must be 15 or 18"); } this.pid = pid; } //位数符合条件则打印身份证,不符合则报出异常 public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddr() { return addr; } public void setAddr(String addr) { this.addr = addr; } }
异常的方法重写
方法名、参数列表、返回值类型必须和父类相同。
子类的访问修饰符和父类相同或是范围大于父类。
子类的方法,不能抛出比父类更多、更宽(范围更大)的异常
package com.james; /** * 重写关系中的异常抛出 * 子类的方法的异常抛出不能大于父类的异常抛出 * @author james * */ public class Son extends Father{ @Override //编译错误,子类的方法的异常抛出大于父类的异常抛出 public void test() throws Exception { // TODO Auto-generated method stub super.test(); } }
package com.james; public class Father { public void test () throws RuntimeException{ System.out.println("test in father"); } }