1、
/* * 异常即是一种意外,是程序运行过程中产生的一种突发(意外) * 事件,该事件会干扰程序的正常执行,打破程序的正常流程。 * * 当异常产生时,会创建一个相关异常类的对象,该对象含有异常的 * 相关信息。 * 异常产生时,会在异常的上下文中寻找异常处理程序,如果没有 * 异常处理程序,则异常产生之后的语句将不会得到执行。该异常 * 会向上传播(传播给方法的调用端)。如果传播到main方法里,还 * 未处理该异常,则main方法会将异常传播给JVM,此时,整个 * 线程终止执行。在控制台会打印出异常的相关信息与堆栈的调用 * 轨迹。该轨迹是按照方法调用的逆序打印,即离异常发生地最近 * 的方法会最先打印。 */ package day12; public class ExceptionTest { public static void main(String[] args) { a(); } public static void a() { b(); } public static void b() { c(); } public static void c() { int x = 5; int y = 0; int z = x / y; System.out.println("x除以y的结果是:"); System.out.println(z); } }
2、
异常可以分成以下三种:
受检异常 运行时异常 错误
说明:运行时异常与错误统称为非受检异常。
当异常发生时,我可以采用两种方式进行处理:
捕获异常
抛出异常
说明:受检异常要求程序员在编译时显式处理,而非受检异常则不需要。
3、异常处理
/* * 异常处理程序(异常处理器) * try { * 可能产生异常的程序 * } catch (异常1) { * 恢复措施 * } catch (异常2) { * 恢复措施 * } …… * catch (异常n) { * 恢复措施 * } * * try-catch的三种情况: * 1 try语句块中没有产生异常。此时try语句块全部执行完毕, * 之后执行try-catch之后的语句。 * 2 try语句块中产生异常,catch捕获了该异常。 * try中如果产生异常,则此时会创建一个相关异常类的对象,异常之后 * 的语句将不会得到执行。程序会跳转到catch分支,从上到下依次使用 * 异常类对象与每个catch中的异常类型进行匹配,哪一个catch分支 * 匹配成功,则执行哪个catch语句块,此时异常被成功捕获。try-catch * 之后的语句正常执行。(最多只会执行一个catch分支) * 3 try语句块中产生异常,catch没有捕获该异常。 * 当try中产生异常时,try异常之后的语句不会得到执行。程序 * 跳转到catch分支进行异常匹配,没有匹配成功,则表示没有捕获 * 该异常(该异常继续存在),则try-catch之后的语句不会得到 * 执行,该异常会继续向上传播(传播给方法的调用端)。 */ package day12; public class Try { public static void main(String[] args) { try { int x = 1; // int y = 10; int y = 0; int z = x / y; // System.out.println(z); // String s = null; // s.length(); // System.out.println("dd"); } catch (ArithmeticException e) { // 打印异常错误的堆栈轨迹 e.printStackTrace(); // 打印异常的信息。 // System.out.println(e.getMessage()); } System.out.println("try-catch后的语句执行"); } }
4、finally
/* * try-catch-finally * try-catch语句块可以同时使用finally。 * catch与finally都是可选的。但是二者不能同时缺失。 * finally的特征: * 不管try是否产生异常,也不管catch是否捕获了try中产生 * 的异常,finally都将会得到执行。 * 因为finally总是会得到执行(虚拟机退出的情况例外),所以 * 我们在finally语句块中进行资源的释放是非常合适的。 * * finally中的返回值会镇压try中的返回值。 */ package day12; public class Finally { public static void main(String[] args) { try { // System.out.println("没有产生异常"); // String s = null; // s.length(); // System.out.println(5 / 0); // 回收程序 } catch (NullPointerException e) { // e.printStackTrace(); } finally { // System.out.println("finally执行了"); } // for (int i = 0; i < 10; i++) { // try { // break; // } finally { // System.out.println("finally执行了"); // } // } // f(); // try { // // 退出虚拟机 // System.exit(0); // } finally { // System.out.println("finally执行了"); // } System.out.println(g()); } public static void f() { try { return; } finally { System.out.println("finally执行了"); } } // 返回的是fianlly中的return值。 public static int g() { try { return 1; } finally { return 2; } } }
5、
/* * 捕获异常时,如果多个catch捕获的异常类型存在父子关系, * 则需要将子类型方法前面,父类型放在后面。因为子类型能够 * 捕获的,父类型都能够捕获,如果父类型的catch放在前面, * 则子类型的catch永远没有机会得到执行。 */ package day12; public class CatchOrder { public static void main(String[] args) { try { } catch (NullPointerException e) { } catch (RuntimeException e) { } catch (Exception e) { } /* * try { * * } catch(Exception e) { * * } catch (RuntimeException e) { //永远没有机会得到执行 } */ } }
6、多重捕获异常
/* * 多重捕获异常 * * 当try语句块中,可能会产生多个异常,而多个异常的处理方式 * 又完全相同时,我们就可以使用多重捕获异常。 * 注意:不要使用多个异常的父类来代替多重捕获异常,因为父类 * 会捕获更多的异常(我们意料之外的异常),这可能会隐藏掩盖 * 我们程序的漏洞。 * * 当使用多重异常捕获时,catch捕获异常的参数将隐式使用final * 来修饰。 */ package day12; public class MultiCatch { public static void main(String[] args) { try { // 可能产生异常的代码 } catch (ArithmeticException e) { e.printStackTrace(); } catch (NullPointerException e) { e.printStackTrace(); } // 多重捕获 try { // String s = null; // s.length(); // System.out.println(1 / 0); } catch (ArithmeticException | NullPointerException e) { e.printStackTrace(); // e = null; } try { String s = null; s.length(); // System.out.println(1 / 0); } catch (RuntimeException e) { e.printStackTrace(); } } }
7、throws 异常列表
/* * throws 异常列表 * * 如果一个方法中可能会抛出受检异常,则要求该异常必须 * 同时声明在方法的异常列表中。对于非受检异常(运行时 * 异常与错误),则不要求声明在方法的异常列表中。 * * 异常分为三种: * 1 受检异常(编译时异常) * 2 运行时异常 * 3 错误 * 运行时异常和错误统称为非受检异常。 * * 受检异常继承Exception类或其子类。 * (不包括RuntimeException或其子类) * 运行时异常继承RuntimeException或其子类。 * 错误继承Error类或其子类。 * * 受检异常与非受检异常的区别: * 受检异常必须在编译期间就需要明确对其进行处理, * 而非受检异常则不需要。 */ package day12; import java.io.FileNotFoundException; public class Throws { public static void main(String[] args) { } public void f(int x, int y) throws FileNotFoundException { if (x < 0) { throw new NullPointerException(); } if (y < 0) { throw new FileNotFoundException(); } } // 该方法不会抛出FileNotFoundException(受检异常) public void g(int x) { // try { // if (x < 0) { // throw new FileNotFoundException(); // } // } catch(FileNotFoundException e) { } } }
8、方法重写的第五条
/* * 方法重写的第五条要求: * 子类重写父类的方法,则子类方法不能比父类方法抛出更多 * 的受检异常。(只能与父类相同,或者是父类抛出受检异常的 * 子类异常)。对于非受检异常,则不受限制。 */ package day12; public class Override5 { public static void main(String[] args) { Override5 o = new Override5(); o.use(new WhiteHorse()); } public void use(Horse h) { h.run(); } /* * public int x() { throw new NullPointerException(); } */ } class Horse { public void run() { } } class WhiteHorse extends Horse { // 错误,子类方法比父类方法抛出了更多的受检异常。 /* * @Override public void run() throws Exception { * * } */ @Override public void run() throws NullPointerException, IllegalArgumentException { } }
9、手动抛出异常
/* * 手动抛出异常 throw */ package day12; public class Throw { public static void main(String[] args) { Throw t = new Throw(); t.deal(-1); } public void deal(int age) { if (age <= 0) { throw new IllegalArgumentException("年龄必须大于0!输入年龄为:" + age); } System.out.println("执行了吗?"); // 对年龄进行处理 } }
10、自定义异常
/* * 自定义异常 * 为什么要使用自定义异常? * 1 Java类库提供的异常类不能满足所有人的需要。 * 2 自定义异常可以令我们快速定位问题。 * * 按照惯例,自定义异常名使用Exception进行结尾。 */ package day12; public class SelfException { public void use() { SelfException s = new SelfException(); //s.deal(-1); try { s.deal(-1); } catch (NegtiveAgeException e) { System.out.println("进行异常的处理"); } } public void deal(int age) { if (age <= 0) { throw new NegtiveAgeException("年龄为负!" + age); } System.out.println("年龄合理,进行处理。"); } } class NegtiveAgeException extends RuntimeException { public NegtiveAgeException(String s) { super(s); } public NegtiveAgeException() { this("年龄为负数,请输入核对!"); } }
11、异常优势:
使用异常具有如下优势: 可以将正常的代码与异常处理的代码相分离。 可以将异常信息沿着方法调用轨迹向上传播。 异常具有明确的类型与继承体系, 便于分类处理。
补充:
1、捕获异常
使用try(尝试着)去执行可能产生异常的程序,一旦异常发
生,系统会自动创建一个相应的异常类对象,同时终止try的
执行( try语句块异常产生位置之后的代码将不会执行)。
一旦产生异常,将会从上到下去匹配catch中异常类,一旦匹
配成功,则异常对象就会传递给catch参数,执行相应catch
语句块。如果没有匹配成功,则该异常会继续向上抛出给方
法调用端。
catch语句块最多只会执行一个。如果所有方法中都没有合适
的异常处理器,则当前线程终止。
无论try是否抛出异常,也无论catch是否捕获异常, finally
语句块都将在try或catch执行后执行。
catch捕获的异常类型必须是Throwable类型或其子类型。
finally是可选的。 catch也是可选的,但catch与finally不能
同时为空。
2、
常见异常:
1:非受检异常
NullPointerException,ClassCastException,ArrayIndexsOutOfBoundsException,ArithmeticException(算术异常,除0溢出)
2)受检:Exception,FileNotFoundException,IOException,SQLException.