理解Java异常处理机制

举报
芝麻粒儿 发表于 2021/08/04 23:44:40 2021/08/04
2.1k+ 0 0
【摘要】 推荐资源站:https://zhimalier.com/ 面试常见问题 /*try块中的退出语句笔试面试题解析public class ExceptionTest5{ public void method() { try { System.out.println("进入到try块"); //return; //会先执行finally块再返回 //虚拟机退出 //...

推荐资源站:https://zhimalier.com/

面试常见问题


      /*try块中的退出语句
      笔试面试题解析
      public class ExceptionTest5
      {
       public void method()
       {
       try
       {
       System.out.println("进入到try块");
       //return;
       //会先执行finally块再返回
       //虚拟机退出
       //System.exit(0);
       //不会执行finally块中的语句,直接退出
       }
       catch (Exception e)
       {
       System.out.println("异常发生了!");
       }
       finally
       {
       System.out.println("进入到finally块");
       }
       System.out.println("后续代码");
       }
       public static void main(String[] args)
       {
       ExceptionTest5 test = new ExceptionTest5();
       test.method();
       }
      }
      在加上return语句前,程序输出:
       进入到try块
       进入到finally块
       后续代码
       如果在try块中加入return语句:
       程序执行输出:
       进入到try块
       进入到finally块
      说明try块中有return语句时,仍然会首先执行finally块中的语句,然后方
      法再返回。
        如果try块中存在System.exit(0);语句,那么就不会执行finally块中
      的代码,因为System.exit(0)会终止当前运行的Java虚拟机,程序会在虚拟
      机终止前结束执行。*/
  
 

try..catch..finally相信大家再熟悉不过了,但是它没想象中那么简单!

Java异常的概念和体系结构
Java把异常当作对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类。 
         在Java API中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception。 
         Java异常体系结构呈树状  如图:

Thorwable类所有异常和错误的超类,有两个子类Error和Exception,分别表示错误和异常。 
 其中异常类Exception又分为运行时异常(RuntimeException)和非运行时异常

1、Error与Exception 

      Error(错误):是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。

例如,Java虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)

一般会选择线程终止 

      Exception是程序本身可以处理的异常,这种异常分两大类运行时异常和非运行时异常。 程序中应当尽可能去处理这些异常。
      注意:异常和错误的区别:异常能被程序本身可以处理,错误是无法处理。   

Java中exception的异常分为两大类:

        1.Checked Exception(非Runtime Exception)
  2.Unchecked Exception(Runtime Exception)
运行时异常
  这些异常是不检查异常 RuntimeException类是Exception类的子类,它叫做运行时异常,Java中的所有运行时异常都会直接或者间接地继承自RuntimeException类。

这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。

非运行时异常

Java中凡是继承自Exception,而不继承自RuntimeException类的异常都是非运行时异常。必须处理!

Java异常处理涉及到五个关键字,分别是:try、catch、finally、throw、throws。

1、try语句块,表示要尝试运行代码,try语句块中代码受异常监控,其中代码发生异常时,会抛出异常对象。    

catch语句块会捕获try代码块中发生的异常并在其代码块中做异常处理,catch语句带一个Throwable类型的参数,  表示可捕获异常类型。当try中出现异常时,catch会捕获到发生的异常,并和自己的异常类型匹配,  若匹配,则执行catch块中代码,并将catch块参数指向所抛的异常对象。catch语句可以有多个, 用来匹配多个中的一个异常,一旦匹配上后,就不再尝试匹配别的catch块了。 通过异常对象可以获取异常发生时完整的JVM堆栈信息,以及异常信息和异常发生的原因等。 

 finally语句块是紧跟catch语句后的语句块,这个语句块总是会在方法返回前执行,  而不管是否try语句块是否发生异常。并且这个语句块总是在方法返回前执行。  目的是给程序一个补救的机会。这样做也体现了Java语言的健壮性。 

2、 try、catch、finally三个语句块应注意的问题 
    第一、try、catch、finally三个语句块均不能单独使用,三者可以组成 try...catch...finally、try...catch、   try...finally三种结构,catch语句可以有一个或多个,finally语句最多一个。 
    第二、try、catch、finally三个代码块中变量的作用域为代码块内部,分别独立而不能相互访问。 如果要在三个块中都可以访问,则需要将变量定义到这些块的外面。 
    第三、多个catch块时候,只会匹配其中一个异常类并执行catch块代码,而不会再执行别的catch块, 并且匹配catch语句的顺序是由上到下。 

3、throw、throws关键字 
    throw关键字是用于方法体内部,用来抛出一个Throwable类型的异常。如果抛出了检查异常,  则还应该在方法头部声明方法可能抛出的异常类型。该方法的调用者也
必须检查处理抛出的异常。 如果所有方法都层层上抛获取的异常,最终JVM会进行处理,处理也很简单,就是打印异常消息和堆栈信息。  如果抛出的是Error或RuntimeException,则该方法的调用者可选择处理该异常。有关异常的转译会在下面说明。  throws关键字用于方法体外部的方法声明部分,用来声明方法可能会抛
出某些异常。仅当抛出了检查异常,  该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候,应该继续抛出,  而不是囫囵吞枣一般在catch块中打印一下堆栈信息做个勉强处理。

异常处理的机制为:抛出异常,捕捉异常。

抛出异常:当一个方法出现错误引发异常时,方法创建异常对象并交付运行时系统,异常对象中包含了异常类型和异常出现时的程序状态等异常信息。运行时系统负责寻找处置异常的代码并执行。  

捕获异常:在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适 的异常处理器。运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适 的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。

异常处理的一般结构


      try
       {
      // 可能发生异常的代码
      // 如果发生了异常,那么异常之后的代码都不会被执行
       }
      catch (Exception e)
       {
      // 异常处理代码
       }
      finally
       {
      // 不管有没有发生异常,finally语句块都会被执行
       }
  
 

多个catch结构  一个try后面可以跟多个catch,但不管多少个,最多只会有一个catch块被执行。

异常处理方法

对于非运行时异常(checked exception),必须要对其进行处理,否则无法通过编译。
处理方式有两种:
1.使用try..catch..finally进行捕获;
2.在产生异常的方法声明后面写上throws 某一个Exception类型,如throws Exception,将异常抛出到外面一层去。对非运行时异常的处理详见代码例子:
处理方式1:将异常捕获


      public class ExceptionTest2
      {
      public void method() throws Exception // 将异常抛出,由调用这
      个方法的方法去处理这个异常,如果main方法也将异常抛出,则交给Java虚拟
      机来处理
       {
       System.out.println("Hello World");
      // 抛出异常
      throw new Exception();
       }
      public static void main(String[] args)
       {
       ExceptionTest2 test = new ExceptionTest2();
      try
       {
       test.method();
       }
      catch (Exception e)
       {
       e.printStackTrace();
       }
      finally
       {
       System.out.println("Welcome");
       }
       }
      }
  
 

处理方式2:将异常继续向外抛出


      public class ExceptionTest2
      {
      public void method() throws Exception // 将异常抛出,由调用这
      个方法的方法去处理这个异常,如果main方法也将异常抛出,则交给Java虚拟
      机来处理
       {
       System.out.println("Hello World");
      // 抛出异常
      throw new Exception();
       }
      public static void main(String[] args) throws Exception // 
      main方法选择将异常继续抛出
       {
       ExceptionTest2 test = new ExceptionTest2();
       test.method(); // main方法需要对异常进行处理
      // 执行结果:
      // Hello World
      // Exception in thread "main" java.lang.Exception
      // at com.learnjava.exception.ExceptionTest2.method
      (ExceptionTest2.java:10)
      // at com.learnjava.exception.ExceptionTest2.main
      (ExceptionTest2.java:17)
       }
      }
  
 

对于运行时异常(runtime exception),可以对其进行处理,也可以不处理。推荐不对运行时异常进行处理。

自定义异常

所谓自定义异常,通常就是定义一个类,去继承Exception类或者它的子类。因为异常必须直接或者间接地继承自Exception类。通常情况下,会直接继承自Exception类,一般不会继承某个运行时的异常类。
      自定义异常可以用于处理用户登录错误,用户输入错误提示等。
自定义异常的例子:
自定义一个异常类型:


      public class MyException extends Exception
      {
      public MyException()
       {
      super();
       }
      public MyException(String message)
       {
      super(message);
       }
      }
  
 

异常处理方式一:


      public class ExceptionTest4
      {
      public void method(String str) throws MyException
       {
      if(null == str)
       {
      throw new MyException("传入的字符串参数不能为null!");
       }
      else
       {
       System.out.println(str);
       }
       }
      public static void main(String[] args) throws MyException //异常处理方式1,不断向外抛出
       {
       ExceptionTest4 test = new ExceptionTest4();
       test.method(null);
       }
      }
  
 

异常处理方式二


      public class ExceptionTest4
      {
      public void method(String str) throws MyException
       {
      if (null == str)
       {
      throw new MyException("传入的字符串参数不能为null!");
       }
      else
       {
       System.out.println(str);
       }
       }
      public static void main(String[] args)
       {
      //异常处理方式2,采用try...catch语句
      try
       {
       ExceptionTest4 test = new ExceptionTest4();
       test.method(null);
       }
      catch (MyException e)
       {
       e.printStackTrace();
       }
      finally
       {
       System.out.println("程序处理完毕");
       }
       }
      }
  
 

刚才说过,可以有多个catch块,去捕获不同的异常,真正执行的时候最多只进入一个catch块。
例:多种异常


      public class MyException extends Exception
      {
      public MyException()
       {  super();
       }
      public MyException(String message)
       {
      super(message);
       }
      }
      public class MyException2 extends Exception
      {
      public MyException2()
       {
      super();
       }
      public MyException2(String message)
       {
      super(message);
       }
      }
      public class ExceptionTest4
      {
      public void method(String str) throws MyException, MyException2
       {
      if (null == str)
       {
      throw new MyException("传入的字符串参数不能为null!");
       }
      else if ("hello".equals(str))
       {
      throw new MyException2("传入的字符串不能为hello");
       }
      else
       {
       System.out.println(str);
       }
       }
      public static void main(String[] args)
       {
      // 异常处理方式2,采用try...catch语句
      try
       {
       ExceptionTest4 test = new ExceptionTest4();
       test.method(null);
       }
      catch (MyException e)
       {
       System.out.println("进入到MyException catch块");
       e.printStackTrace();
       }
      catch (MyException2 e)
       {
       System.out.println("进入到MyException2 catch块");
       e.printStackTrace();
       }
      finally
       {
       System.out.println("程序处理完毕");
       }
       }
      }
  
 

我们可以使用多个catch块来捕获异常,这时需要将父类型的catch块放到子类型的catch块之后,这样才能保证后续的catch块可能被执行,否则子类型
的catch块将永远无法到达,Java编译器会报错。如果异常类型是独立的,那么它们的前后顺序没有要求。
例子:
多个catch语句块的顺序


      public class ExceptionTest4
      {
      public void method(String str) throws Exception // 也可以声明Exception,只要声明的可以涵盖所有抛出的异常即可
       {
      if (null == str)
       {
      throw new MyException("传入的字符串参数不能为null!");
       }
      else if ("hello".equals(str))
       {
      throw new MyException2("传入的字符串不能为hello");
       }
      else
       {
       System.out.println(str);
       }
       }
      public static void main(String[] args)
       {
      // 异常处理方式2,采用try...catch语句
      try
       {
       ExceptionTest4 test = new ExceptionTest4();
       test.method(null);
       }
      catch (MyException e)
       {
       System.out.println("进入到MyException catch块");
       e.printStackTrace();
       }
      catch (MyException2 e)
       {
       System.out.println("进入到MyException2 catch块");
       e.printStackTrace();
       }
      catch (Exception e)
       {
      //虽然需要加上,但是这块代码不会被执行,只是为了编译成功
       System.out.println("进入到MyException catch块");
       e.printStackTrace();
      //如果去掉前面两个catch块或其中之一,则发生该异常时就会进入此catch块
      //catch块的匹配是按照从上到下的顺序,所以这个块如果放在最前面就会捕获所有的异常,后面的块永远不会执行,这时候会提示编译错误
       }
      finally
       {
       System.out.println("程序处理完毕");
       }
       }
      }
  
 

练习


      public class TestException {
     	public TestException() {
      	}
     	boolean testEx() throws Exception {
     		boolean ret = true;
     		try {
      			ret = testEx1();
      		} catch (Exception e) {
      			System.out.println("testEx, catch exception");
      			ret = false;
     			throw e;
      		} finally {
      			System.out.println("testEx, finally; return value=" + ret);
     			return ret;
      		}
      	}
     	boolean testEx1() throws Exception {
     		boolean ret = true;
     		try {
      			ret = testEx2();
     			if (!ret) {
      return false;
      			}
      			System.out.println("testEx1, at the end of try");
     			return ret;
      		} catch (Exception e) {
      			System.out.println("testEx1, catch exception");
      			ret = false;
     			throw e;
      		} finally {
      			System.out.println("testEx1, finally; return value=" + ret);
     			return ret;
      		}
      	}
     	boolean testEx2() throws Exception {
     		boolean ret = true;
     		try {
     			int b = 12;
     			int c;
     			for (int i = 2; i >= -2; i--) {
       c = b / i;
       System.out.println("i=" + i);
      			}
     			return true;
      		} catch (Exception e) {
      			System.out.println("testEx2, catch exception");
      			ret = false;
     			throw e;
      		} finally {
      			System.out.println("testEx2, finally; return value=" + ret);
     			return ret;
      		}
      	}
     	public static void main(String[] args) {
      		TestException testException1 = new TestException();
     		try {
      			testException1.testEx();
      		} catch (Exception e) {
      			e.printStackTrace();
      		}
      	}
      }
      /*答案:
      i=2
      i=1
      testEx2, catch exception
      testEx2, finally; return value=false
      testEx1, finally; return value=false
      testEx, finally; return value=false
      注意说明:
      finally语句块不应该出现 应该出现return。上面的return ret最好是其他
      语句来处理相关逻辑。*/
  
 

文章来源: zhima.blog.csdn.net,作者:芝麻粒儿,版权归原作者所有,如需转载,请联系作者。

原文链接:zhima.blog.csdn.net/article/details/51973534

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

作者其他文章

评论(0

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。