一、什么是异常处理
异常处理从字面的意思来讲就是一种发生在 java 程序中的错误并对其处理,但对程序员而言,异常的处理不单单的简单去处理异常,就 ok 了,还有众多的概念和使用异常的方式方法需要掌握
异常在 java 中分有三种:
1、编译时异常(受检异常)------> 这种异常发生的概率很高;
2、运行时异常 ------> 这种异常发生的概率较低,发生时直接退出 JVM;
3、错误(error)-----> 错误和异常是不同,错误其实是脱离了程序员控制的问题,错误在代码中经常被忽略。比如内存溢出,在编译过程也是发现不了的;
如上图可以看出受检异常与运行时异常都继承于 Exception 类, 而 Exception 与 Error 继承 Throwable 类,最终它们的父类都是 Object;
二、处理异常的方式有哪些
处理异常的方式有两种:
1、throws <上抛>:在需要进行异常处理的方法体上加上
这样的操作其实只是将异常交给了其他的程序处理,如果其他程序没有能力进行处理,则将一直进行抛出,直到遇见一个能够处理该异常的程序,并终止异常(该如何处理该异常呢?);
通过一个程序来更深次的了解 throws 如何处理异常:
1 1 import java.io.FileInputStream; 2 2 import java.io.FileNotFoundException; 3 3 4 4 /** 5 5 * throws 的深层次理解 6 6 */ 7 7 public class Test17 { 8 8 9 9 // public static void main(String[] args) throws FileNotFoundException{ // 当然这里也可以直接使用父类 Exception 10 10 11 11 public static void main(String[] args) throws Exception{ 12 12 13 13 System.out.println("我是入口"); 14 14 15 15 m1(); 16 16 } 17 17 18 18 public static void m1() throws FileNotFoundException{ 19 19 // 从m1 - m3 的调用可以看出 throws 处理机制是不对异常处理,只是抛出异常,谁调用该异常,谁处理,最终会流向哪里处理呢? 20 20 21 21 System.out.println("m1...start!"); 22 22 m2(); // m1() 也同样出现了异常 23 23 System.out.println("m1...end!"); 24 24 } 25 25 26 26 public static void m2() throws FileNotFoundException{ 27 27 // 28 28 29 29 System.out.println("m2...start!"); 30 30 m3(); // 从这个地方我们就可以看出了当 m2()进行调用 m3()时,m2() 方法体也出现需要处理的异常 31 31 // 同样的可以依照 m3() 方法进行处理 32 32 33 33 System.out.println("m2...end!"); 34 34 } 35 35 36 36 // public static void m3() throws NullPointerException{ 37 37 // 已经对该方法体进行了抛出异常的设置,为什么是无效的? 38 38 // NullPointerException 因为这个是一个空指针的异常,它不对 IO 流的异常进行处理 39 39 40 40 public static void m3() throws FileNotFoundException { 41 41 // FileNotFoundException 异常机制对 m3 起到了效果,主要因为 FileNotFoundException 继承 IOException; 42 42 43 43 FileInputStream fis = new FileInputStream("E:\\jaa 作业室\\temp.java"); 44 44 // FileInputStream 是一个继承了异常基本类的 IO 流; 所以同样在使用它的时候程序员就需要对它进行处理 45 45 46 46 System.out.println("m3...end!"); 47 47 } 48 48 } 49 49 /** 看看如果读取的路径出现错误会发生什么结果吧 50 50 * 我是入口 51 51 * m1...start! 52 52 * m2...start! 53 53 * // 这里也出现一了一件有意思的事情: 54 54 * // 每个方法体中只有在 异常 前的值被输出,紧跟异常之后的值直接不被读取了 55 55 * // 从中得出一个结论: 一个方法中通过 throws 抛出的异常,在被调用时,调用者与被调用本身,异常之后的代码将被执行中断,不会输出结果,异常前的值会进行输出 56 56 * 57 57 * 58 58 * Exception in thread "main" java.io.FileNotFoundException: E:\jaa 作业室\temp.java (系统找不到指定的路径。) 59 59 * // 这里就是异常的处理结果了: Exception in thread "main" java.io.FileNotFoundException: E:\jaa 作业室\temp.java (系统找不到指定的路径。) 60 60 * // 是不是发现奇妙的事情:各个方法体不断的对异常抛出,最后在 main 方法中进行了异常处理,那为什么方法体中就不能处理,只能在 main 中处理呢? 61 61 * // 其实 main 方法是主调方法,是一切程序的入口,方法体只有被 main 调用才能输出结果,(当然后面还有一种方法可以在方法体中处理)而异常的处理其实 62 62 * // 也不是在 main 方法中处理的,可以看到 main 方法体上也出现了抛出异常的设置, 其实在程序员不能看到的 JVM 中,程序将异常最终交给了 63 63 * // FileNotFoundException 类处理该异常。 64 64 * 65 65 * 66 66 * at java.base/java.io.FileInputStream.open0(Native Method) 67 67 * at java.base/java.io.FileInputStream.open(FileInputStream.java:213) 68 68 * at java.base/java.io.FileInputStream.(FileInputStream.java:155) 69 69 * at java.base/java.io.FileInputStream.(FileInputStream.java:110) 70 70 * 71 71 * at Rview.Test17.m3(Test17.java:43) 72 72 * at Rview.Test17.m2(Test17.java:30) 73 73 * at Rview.Test17.m1(Test17.java:22) 74 74 * at Rview.Test17.main(Test17.java:15) 75 75 * // 这里表示的是异常出现的行号索引 76 76 */
2、try...catch... <捕捉,处理异常>: 捕捉有可能发生异常的代码,并将它处理,不会对异常抛出,只会终止异常的向后发展
通过一个程序来了解 try...catch... 如何处理异常:
1 1 import java.io.FileInputStream; 2 2 import java.io.FileNotFoundException; 3 3 import java.io.FileOutputStream; 4 4 import java.io.IOException; 5 5 6 6 /** 7 7 * try...catch... 的使用方法和重点 8 8 */ 9 9 10 10 // 这是一个与 Test17 相同的程序,但我们需要对这个程序使用使用不同的异常处理机制 11 11 12 12 public class Test18 { 13 13 14 14 public static void main(String[] args) { 15 15 16 16 m1(); 17 17 } 18 18 19 19 public static void m1(){ 20 20 21 21 System.out.println("m1...start!"); 22 22 m2(); 23 23 System.out.println("m1...end!"); 24 24 } 25 25 26 26 public static void m2(){ 27 27 28 28 System.out.println("m2...start!"); 29 29 m3(); 30 30 System.out.println("m2...end!"); 31 31 } 32 32 33 33 34 34 35 35 public static void m3(){ 36 36 37 37 38 38 // try...catch... 的重点: 可以在发生异常点进行直接处理异常,当其他调用者调用时将不会发现该方法曾经有过异常的情况,同时也不会将异常感染 39 39 // 给调用者,总之可以直接对异常直接处理 40 40 41 41 // try...catch...不仅仅只能对单个异常进行捕捉处理,同时可以对多个不同类型的异常进行处理 42 42 try { 43 43 FileInputStream fis = new FileInputStream("E:\\java\\作业室\\temp.java"); 44 44 // try 中将可能发生异常的代码段进行捕捉 45 45 fis.close(); // 再次出现了一个异常,再处理 46 46 47 47 // 以上代码出现异常,直接进入 catch 分支,不对其后的代码进行编译; 48 48 System.out.println("hello "); 49 49 } 50 50 catch (FileNotFoundException e/*必须要加变量名*/) { /*将捕捉到的异常进行处理*/ 51 51 52 52 // e.printStackTrace(); // 显示处理结果的方法 53 53 54 54 System.out.println("出现了异常,已经处理!!"); 55 55 56 56 }catch (IOException e){ 57 57 // 已经进行了异常处理,为什么出现了错误? 58 58 // 两个 catch 对不同的异常进行了处理,但是,前一个 catch 的异常处理类是最高类 Exception,而后一个 catch 的异常处理类 59 59 // FileNotFoundException 是 Exception 的一个子类,所以在进行多次的 catch 异常处理时注意子类与父类的位置关系 60 60 //(子类在前、父类在后 或者 同时是子类) 61 61 e.printStackTrace(); 62 62 } 63 63 // 同样要进行异常处理,这次我们将使用 try... catch... 的方式处理 64 64 65 65 System.out.println("m3...end!"); 66 66 } 67 67 } 68 68 69 69 /** output result: 70 70 * m1...start! 71 71 * m2...start! 72 72 * m3...end! 73 73 * // 奇妙的事情又发生了,使用 try... catch...的方式处理异常并不影响异常之后的代码运行 74 74 * 75 75 * m2...end! 76 76 * m1...end! 77 77 * 78 78 * 79 79 * java.io.FileNotFoundException: E:\java\作业室\temp.java (系统找不到指定的路径。) 80 80 * // 这个结果的输出靠 printStackTrace() 方法; 81 81 * 82 82 * at java.base/java.io.FileInputStream.open0(Native Method) 83 83 * at java.base/java.io.FileInputStream.open(FileInputStream.java:213) 84 84 * at java.base/java.io.FileInputStream.(FileInputStream.java:155) 85 85 * at java.base/java.io.FileInputStream.(FileInputStream.java:110) 86 86 * 87 87 * 88 88 * at Rview.Test18.m3(Test18.java:42) 89 89 * at Rview.Test18.m2(Test18.java:30) 90 90 * at Rview.Test18.m1(Test18.java:23) 91 91 * at Rview.Test18.main(Test18.java:17) 92 92 * // 同样是异常出现所在的行号索引 93 93 */
三、getMessage() 和 printStackTrace()
这是两个都是命令行打印异常信息在程序中出错的位置及原因,其两者的区别在于:前者的简单的打印出错原因,后者是详细的打印出错的位置及原因,一起用一段程序了解一下吧
1 1 import java.io.FileInputStream; 2 2 import java.io.FileNotFoundException; 3 3 4 4 public class Test19 { 5 5 6 6 public static void main(String[] args) { 7 7 8 8 m2(); 9 9 } 10 10 11 11 public static void m2 (){ 12 12 m3(); 13 13 } 14 14 15 15 public static void m3(){ 16 16 try{ 17 17 FileInputStream fis = new FileInputStream("E:\\自考本科学习笔记\\英语\\基础英语语法、English.一般现在时态.docx"); 18 18 19 19 }catch(FileNotFoundException e){ 20 20 //e.printStackTrace(); 21 21 /* printStackTrace(); 方法的使用 将详细的描述错误的原因和错误出现的位置 22 22 java.io.FileNotFoundException: E:\自考本科学习笔记\英语\基础英语语法、English.一般现在时态.docx (系统找不到指定的文件。) 23 23 at java.base/java.io.FileInputStream.open0(Native Method) 24 24 at java.base/java.io.FileInputStream.open(FileInputStream.java:213) 25 25 at java.base/java.io.FileInputStream.(FileInputStream.java:155) 26 26 at java.base/java.io.FileInputStream.(FileInputStream.java:110) 27 27 at Rview.Test19.m3(Test19.java:19) 28 28 at Rview.Test19.m2(Test19.java:14) 29 29 at Rview.Test19.main(Test19.java:10)*/ 30 30 31 31 String st =e.getMessage(); 32 32 System.out.println(st); 33 33 /* getMessage() 方法的使用,会简单的描述错误的原因 34 34 E:\自考本科学习笔记\英语\基础英语语法、English.一般现在时态.docx (系统找不到指定的文件。) 35 35 */ 36 36 37 37 // System.out.println(e.getMessage()); 另一种写法 38 38 } 39 39 } 40 40 }
四、finally的使用
finally 关键字的使用: finally 是在catch之后添加使用,它的作用是无论 try...catch...是否执行,它一定会执行
1 1 import java.io.FileInputStream; 2 2 import java.io.FileNotFoundException; 3 3 import java.io.IOException; 4 4 5 5 /* finally 通常在什么情况下使用 6 6 通常在流关闭的情况下使用,这样可以保证关闭可以一定执行 7 7 */ 8 8 9 9 public class Test20 { 10 10 11 11 public static void main(String[] args) { 12 12 FileInputStream fis = null; 13 13 try{ 14 14 fis = new FileInputStream("E:\\自考本科学习笔记\\英语\\基础英语语法\\English.句子成分.docx"); 15 15 16 16 System.out.println("HelloWord!"); 17 17 String s = null; 18 18 s.toString(); 19 19 20 20 }catch(FileNotFoundException e) { 21 21 e.printStackTrace(); 22 22 } finally { 23 23 try{ 24 24 if (null != fis){ 25 25 fis.close(); 26 26 } 27 27 }catch(IOException e){ 28 28 e.printStackTrace(); 29 29 } 30 30 } 31 31 } 32 32 }
finally的面试题:
1 1 import java.io.IOException; 2 2 3 3 public class Test21 { 4 4 5 5 public static void main(String[] args) { 6 6 int result = m(); 7 7 System.out.println(result); 8 8 // output result: 100 9 9 /** 为什么出现这种情况? finally 难道没有执行吗? 10 10 * 11 11 * java程序有两个亘古不变的原则: 1、 方法体中代码必须遵循自上而下顺序依次执行 12 12 * 2、 return 语句一旦执行,整个方法必须结束 13 13 * 14 14 * */ 15 15 } 16 16 17 17 public static int m(){ 18 18 int i = 100; 19 19 try{ 20 20 return i; 21 21 } finally{ 22 22 i++; 23 23 } 24 24 25 25 /** 26 26 * 所以 该方法的底层程序执行的是这样的程序 27 27 * public static int m(){ 28 28 * int i = 100; 29 29 * int j = i; 30 30 * i ++; 31 31 * return j; 32 32 * } 33 33 * */ 34 34 } 35 35 }
细谈 final、 finally、 finalize的区别
fiinal: 修饰符(关键字) 如果一个类被声明为final,意味着它不能再派生新的子类,不能作为父类被继承。因此一个类不能既被声明为abstract,又被声明为final的。将变量或方法声明为final,可以保证他们使用中不被改变。被声明为final的变量必须在声明时给定初值,而以后的引用中只能读取,不可修改,被声明为final的方法也同样只能使用,不能重载。与 static 连用将变量变为常量;
finally:在异常处理时提供 finally 块来执行清楚操作。如果抛出一个异常,那么相匹配的 catch 语句就会执行,然后控制就会进入finally块;
finalize:是方法名。java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除之前做必要的清理工作。这个方法是在垃圾收集器在确定了被清理对象没有被引用的情况下调用的。finalize 是在Object 类中定义的,因此,所有的类都继承了它。子类可以覆盖 finalize() 方法,来整理系统资源或者执行其他清理工作。
四、运行时异常什么时候出现
程序在编译阶段是正常执行的,且没有任何的错误,但是当运行时,就立马出现了错误,并退出 JVM 这种情况在什么时候发生呢?
110 / 0 ---------> 这个就是一个很经典的运行时异常