异常讲解【二】

129 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第29天,点击查看活动详情

哈喽,大家好!我是Why,一名在读学生,目前刚刚开始进入自己的编程学习生涯。虽然学习起步较晚,但我坚信做了才有0或1的可能。学了一段时间以后也是选择在掘金上分享自己的日常笔记,也希望能够在众多道友的大家庭中打成一片。 本文主要讲解异常,如果大家读后觉得有用的话,还请大家多多支持博主:欢迎 ❤️点赞👍、收藏⭐、留言💬 ✨✨✨个人主页:JinHuan

异常对象的两个重要方法

     获取异常简单的描述信息:
         String msg = exception.getMessage();
 ​
     打印异常追踪的堆栈信息:
         exception.printStackTrace();

实例

 public class ExceptionTest08 {
     public static void main(String[] args) {
         // 这里只是为了测试getMessage()方法和printStackTrace()方法。
         // 这里只是new了异常对象,但是没有将异常对象抛出。JVM会认为这是一个普通的java对象。
         NullPointerException e = new NullPointerException("空指针异常fdsafdsafdsafds");
 ​
         // 获取异常简单描述信息:这个信息实际上就是构造方法上面String参数。
         String msg = e.getMessage(); //空指针异常fdsafdsafdsafds
         System.out.println(msg);
 ​
         // 打印异常堆栈信息
         // java后台打印异常堆栈追踪信息的时候,采用了异步线程的方式打印的。
         e.printStackTrace();
 ​
         for(int i = 0; i < 1000; i++){
             System.out.println("i = " + i);
         }
 ​
         System.out.println("Hello World!");
     }
 }
 运行结果如下:

怎样查看异常并做处理

 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 ​
 /*
 我们以后查看异常的追踪信息,我们应该怎么看,可以快速的调试程序呢?
     异常信息追踪信息,从上往下一行一行看。
     但是需要注意的是:SUN写的代码就不用看了(看包名就知道是自己的还是SUN的。)。
     主要的问题是出现在自己编写的代码上。
  */
 public class ExceptionTest09 {
     public static void main(String[] args) {
         try {
             m1();
         } catch (FileNotFoundException e) {
             // 获取异常的简单描述信息
             String msg = e.getMessage();
             System.out.println(msg); //C:\jetns-agent.jar (系统找不到指定的文件。)
 ​
             //打印异常堆栈追踪信息!!!
             //在实际的开发中,建议使用这个。养成好习惯!
             // 这行代码要写上,不然出问题你也不知道!
             //e.printStackTrace();
             /*
             java.io.FileNotFoundException: C:\jetns-agent.jar (系统找不到指定的文件。)
                 at java.base/java.io.FileInputStream.open0(Native Method)
                 at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
                 at java.base/java.io.FileInputStream.<init>(FileInputStream.java:155)
                 at java.base/java.io.FileInputStream.<init>(FileInputStream.java:110)
                 at com.bjpowernode.javase.exception.ExceptionTest09.m3(ExceptionTest09.java:31)
                 at com.bjpowernode.javase.exception.ExceptionTest09.m2(ExceptionTest09.java:27)
                 at com.bjpowernode.javase.exception.ExceptionTest09.m1(ExceptionTest09.java:23)
                 at com.bjpowernode.javase.exception.ExceptionTest09.main(ExceptionTest09.java:14)
                 因为31行出问题导致了27行
                 27行出问题导致23行
                 23行出问题导致14行。
                 应该先查看31行的代码。31行是代码错误的根源。
              */
         }
 ​
         // 这里程序不耽误执行,很健壮。《服务器不会因为遇到异常而宕机。》
         System.out.println("Hello World!");
     }
 ​
     private static void m1() throws FileNotFoundException {
         m2();
     }
 ​
     private static void m2() throws FileNotFoundException {
         m3();
     }
 ​
     private static void m3() throws FileNotFoundException {
         new FileInputStream("C:\jetns-agent.jar");
     }
 }
 ​

Finally语句

     "1、在finally子句中的代码是最后执行的,并且是一定会执行的,即使try语句块中的代码出现了异常。
         finally子句必须和try一起出现,不能单独编写。
 ​
     "2finally语句通常使用在哪些情况下呢?
         通常在finally语句块中完成资源的释放/关闭。
         因为finally中的代码比较有保障。
         即使try语句块中的代码出现异常,finally中代码也会正常执行。

实例

 public class ExceptionTest10 {
     public static void main(String[] args) {
         FileInputStream fis = null; // 声明位置放到try外面。这样在finally中才能用。
         try {
             // 创建输入流对象
             fis = new FileInputStream("D:\course\02-JavaSE\document\JavaSE进阶讲义\JavaSE进阶-01-面向对象.pdf");
             // 开始读文件....
 ​
             String s = null;
             // 这里一定会出现空指针异常!
             s.toString();
             System.out.println("hello world!");
 ​
             // 流使用完需要关闭,因为流是占用资源的。
             // 即使以上程序出现异常,流也必须要关闭!
             // 放在这里有可能流关不了。
             //fis.close();
         } catch (FileNotFoundException e) {
             e.printStackTrace();
         } catch(IOException e){
             e.printStackTrace();
         } catch(NullPointerException e) {
             e.printStackTrace();
         } finally {
             System.out.println("hello 浩克!");
             // 流的关闭放在这里比较保险。
             // finally中的代码是一定会执行的。
             // 即使try中出现了异常!
             if (fis != null) { // 避免空指针异常!
                 try {
                     // close()方法有异常,采用捕捉的方式。
                     fis.close();
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             }
         }
 ​
         System.out.println("hello kitty!");
 ​
     }
 }

Try和finally的连用

验证finally语句一定是最后执行的

 public class ExceptionTest11 {
     public static void main(String[] args) {
         /*
         tryfinally,没有catch可以吗?可以。
             try不能单独使用。
             try finally可以联合使用。
         以下代码的执行顺序:
             先执行try...
             再执行finally...
             最后执行 returnreturn语句只要执行方法必然结束。)
          */
         try {
             System.out.println("try...");
             return;
         } finally {
             // finally中的语句会执行。能执行到。
             System.out.println("finally...");
         }
 ​
         // 这里不能写语句,因为这个代码是无法执行到的。
         //System.out.println("Hello World!");
     }
 }

关于Finally中的特例

 "注意,当退出JVM的时候,Finally语句就不会执行了"
 ​
 public class ExceptionTest12 {
     public static void main(String[] args) {
         try {
             System.out.println("try...");
             // 退出JVM
             System.exit(0); // 退出JVM之后,finally语句中的代码就不执行了!
         } finally {
             System.out.println("finally...");
         }
     }
 }
 ​

Finally易错点

 "试推测下列result的返回值
 public class Test {
         public static void main(String[] args) {
             int result = m();
             System.out.println(result); //猜测结果
         }
         public static int m(){
             int i = 100;
             try {
                return i;
             } finally {
                 i++;
                 System.out.println("Finally中的i"+i);
             }
         }
     }

分析

  "java语法规则(有一些规则是不能破坏的,一旦这么说了,就必须这么做!):
         java中有一条这样的规则:
             方法体中的代码必须遵循自上而下顺序依次逐行执行(亘古不变的语法!)
         java中还有一条语法规则:
             return语句一旦执行,整个方法必须结束(亘古不变的语法!)

运行结果

 可以看出,虽然finally语句块执行了,但是,return的值还是100!!
     为什么?看看反编译的结果
             public static int m(){
                 int i = 100;
                 int j = i;
                 i++;
                 return j;
             }
 "不难看出,在反编译的时候,引入了一个新的中间变量,来解决我们的疑惑

Final 、Finally、 Finalize的区别

     "final 关键字
         final修饰的类无法继承
         final修饰的方法无法覆盖
         final修饰的变量不能重新赋值。
 ​
     "finally 关键字
         和try一起联合使用。
         finally语句块中的代码是必须执行的。
 ​
     "finalize 标识符
         是一个Object类中的方法名。
         这个方法是由垃圾回收器GC负责调用的。

实例

 public class ExceptionTest14 {
     public static void main(String[] args) {
 ​
         // final是一个关键字。表示最终的。不变的。
         final int i = 100;
         //i = 200;
 ​
         // finally也是一个关键字,和try联合使用,使用在异常处理机制中
         // 在fianlly语句块中的代码是一定会执行的。
         try {
 ​
         } finally {
             System.out.println("finally....");
         }
 ​
         // finalize()是Object类中的一个方法。作为方法名出现。
         // 所以finalize是标识符。
         // finalize()方法是JVM的GC垃圾回收器负责调用。
         Object obj;
     }
 }
 ​
 // final修饰的类无法继承
 final class A {
     // 常量。
     public static final double MATH_PI = 3.1415926;
 }
 ​
 class B {
     // final修饰的方法无法覆盖
     public final void doSome(){
 ​
     }
 }
 ​