第11课时:异常处理

一、异常处理机制

1、使用 try...catch 捕获异常

try

{

          // 业务实现代码

          ...

}

catch(Exception e)

{

          // 异常处理程序

}

           如果执行 try 块里的业务逻辑代码时出现异常,系统会自动生成一个一场对象,该异常对象被提交给 Java 运行时环境,这个过程被称为抛出 (throw) 异常。

           当 Java 运行时环境收到异常对象时,会寻找能处理该异常的 catch 块,如果找到合适的 catch 块并把该异常对象交给该 catch 块处理,那个过程被称为捕获 (catch) 异常;如果 Java 运行时环境找不到捕获异常的 catch 块,则运行时环境终止,Java 程序也将退出。

 

2、异常类的继承体系

try

{

          // 业务实现代码            // 如果这里出现异常,系统生成异常对象 ex

          ...

}

catch(ExceptionClass1 e1)  // ex instanceof ExceptionClass1 == true , 程序进入该 catch 块,并且不会向下执行

{

          // 异常处理程序

}

 

catch(ExceptionClass2 e2)  // ex instanceof ExceptionClass2 == true

{

          // 异常处理程序

}

……

 


第11课时:异常处理_第1张图片
            Java 将所有非正常情况分为两种:异常(Exception)和错误(Error),它们都是继承 Throwable 父类。

            Error 错误,一般是指虚拟机相关的问题,如系统崩溃、虚拟机处错误、动态连接失败等,这种错误无法恢复或不可能捕获,将导致应用程序中断。通常应用程序无法处理这些错误,因此应用程序不应该试图使用 catch 块来捕获 Error 对象。在定义该方法时,也无须在其 throws 子句中声明该方法可能抛出 Error 及其任何子类。

            异常捕获的原则:先处理小异常,再处理大异常。

3、访问异常信息

            所有异常对象都包含了如下几个常用方法:

  • getMessage()
  • printStackTrace()
  • printStackTrace(PrintStream s)
  • getStackTrace

4、使用 finally 回收资源

     有时程序在 try 块里打开了一些物理资源(例如数据库连接、网络连接和磁盘文件等),这些物理资源都被必须显式回收。

      finally 块总会被执行。

try

{

          // 业务实现代码            // 如果这里出现异常,系统生成异常对象 ex

          ...

}

catch(ExceptionClass1 e1)  // ex instanceof ExceptionClass1 == true , 程序进入该 catch 块,并且不会向下执行

{

          // 异常处理程序

}

 

catch(ExceptionClass2 e2)  // ex instanceof ExceptionClass2 == true

{

          // 异常处理程序

}

……

finally

{

          // 资源回收块

          ……

}

          finally 块是一定会被执行的程序。

示例:

 public static void main(String[] args)
 {
  FileInputStream fis = null;
  try
  {
   fis = new FileInputStream("a.txt"); 
  }
  catch (IOException ioe)
  {
   System.out.println(ioe.getMessage());
   //return语句强制方法返回
   //return ;
   //使用exit来退出虚拟机
   System.exit(1);
  }
  finally
  {
   //关闭磁盘文件,回收资源
   if (fis != null)
   {
    try
    {
     fis.close();
    }
    catch (IOException ioe)
    {
     ioe.printStackTrace();
    }
   }
   System.out.println("程序已经执行了finally里的资源回收!");
  }
 }

           上面程序中的 try 块后面加了 finally 块,用于回收在 try 块中打开的物理资源。注意程序的 catch 块中 ① 处有一条 return 语句,该语句强制方法返回。通常情况下,方法执行到 return 语句的地方,程序将立即结束该方法,但现在不会,虽然 return 也强制方法立即结束,但一定会先执行 finally 块里的代码。

            如果注释掉 ① 处的 return 语句,取消 ② 处的注释,即在异常处理的 catch 块中使用 System.exit(1) 来退出虚拟机,则 finally 块中的代码不会被执行。

            除非在 try 块、catch 块中调用了退出虚拟机的方法,否则不管在 try 块、catch 块中执行怎样的代码,出现怎样的情况,异常处理的 finally 块总会被执行。

            一般不要在 finally 块中使用如:return 或 throw 等导致方法终止的语句,一旦在 finally 块中使用了 return 或 throw 语句,将会导致 try 块、catch 块中的 return、throw 语句失效。示例:

 public static void main(String[] args) throws Exception
 {
  boolean a = test();
  System.out.println(a);
 }
 public static boolean test()
 {
  try
  {
   return true;
  }
  finally
  {
   return false;
  }
 }

            程序最后运行的结果是 false

 

5、异常处理的嵌套

            在 try 块、catch 块或者 finally 块中再次包含了一个完整的异常处理流程的情形被称为异常处理的嵌套。

 

二、Checked 异常和 Runtime 异常体系

      Java 的异常被分为两大类:Checked 异常和 Runtime 异常(运行时异常)。所有 RuntimeExceptiom 类及其子类的实例被称为 Runtime 异常;不是 RuntimeException 类及其子类的异常实例则被称为 Checked 异常。

      只有 Java 语言提供了 Checked 异常,其它语言都没有提供 Checked 异常。Java 认为 Checked 异常都是可以被处理(修复)的异常,所以 Java 程序必须显式处理 Checked 异常。如果程序没有处理 Checked 异常,该程序在编译时就会发生错误,无法编译通过。

      Checked 异常体现了 Java 的设计哲学:没有完善错误处理的代码根本就不会被执行。

      对于 Checked 异常处理方式有两种:

  • 当前方法明确知道如何处理该异常,程序应该使用 try……catch 块来捕获该异常,然后在对应的 catch 块中修补该异常。
  • 当前方法不知道如何处理这种异常,应该在定义该方法时声明抛出该异常

      Runtime 异常则更为灵活,Runtime 异常无须显式声明抛出,如果程序需要捕捉 Runtime 异常,也可以使用 try……catch 块来捕获 Runtime 异常。

1、使用 throws 声明抛出异常

      使用 throws 声明抛出异常的思路是:当前方法不知道应该如何处理这种类型的异常,该异常应该由上一级调用者处理,如果 main 方法也不知道应该如何处理这种异常,也可以使用 throws 声明抛出异常,将该异常交给 JVM 处理。JVM 对异常的处理是:打印异常跟踪栈信息,并终止程序运行。

      一旦方法使用了 throws 语句声明抛出该异常,程序就无需再使用 try……catch 块来捕获该异常。

  public static void main(String[] args) throws IOException
  {
          FileInputStream fis = new FileInputStream("a.txt"); 
  }

      如果某段代码中调用了一个带 throws 声明的方法,这表明该方法希望它的调用者来处理该异常。也就是说,代码要么放在 try 块中显式捕获该异常,要么这段代码处于另一个带 throws 声明抛出的方法中。

示例:

 public static void main(String[] args)
 {
  try
  {
   method1();
  }
  catch (FileNotFoundException e)
  {
   e.printStackTrace();
  }
 }
 
 public static void method1() throws FileNotFoundException
 {
  method2();
 }
 
 public static void method2() throws FileNotFoundException
 {
  FileInputStream fis = new FileInputStream("a.txt");
 }

 

      使用 throws 声明抛出异常时有下列限制:

  • 如果某段代码调用了一个带 throws 声明的方法,被调用者的异常类型必须和调用者的 catch 块或者 throws 声明抛出的异常类型相同,或者是调用者异常的子类。
  • 当方法重写时,子类方法中声明抛出的异常应该是父类方法声明抛出异常类型的子类。
  • 子类方法(被调用者)中不允许比父类方法(调用者)声明抛出的异常更多

      示例:调用者声明抛出的异常是被调用者声明抛出异常的子类,不符合要求


第11课时:异常处理_第2张图片
          子类方法声明抛出了比父类方法更大的异常:

 
第11课时:异常处理_第3张图片
              子类方法(被调用者)中不允许比父类方法(调用者)声明抛出的异常更多,不符合要求


第11课时:异常处理_第4张图片

三、使用 throw 抛出异常

1、抛出异常

      当程序出现错误时,系统会自动抛出异常;除此之外,Java 也允许程序自行抛出异常,使用 throw 语句完成自行抛出异常。

      异常是一个相对的概念,例如:除数为 0,这样的异常大家都知道。但有些异常是业务相关的,例如,统计大学生年龄的时候,如果年龄小于1岁,或者大于100岁,这样都是显然不符合现实世界逻辑的,但计算机并不能理解这样的现实逻辑,这样就要我们定义一个自定义异常,并将该异常通过 throw 语句抛出。throw 抛出异常的语法格式是:

      throw ExceptionInstance;

      如果 throw 语句抛出的异常是 Checked 异常,则该 throw 语句要么处于 try 块里,显式捕获该异常,要么放在一个带 throws 声明抛出的方法中,即把该异常交给该方法的调用者处理;如果 throw 语句抛出的是 Runtime 异常,则该语句无须放在 try 块里,也无须放在带 throws 声明抛出的方法中;程序既可以显式使用 try……catch 来捕获,并处理该异常,也可以完全不理会该异常,把该异常交给方法的调用者来处理。

示例:

 public static void main(String[] args)
 {
  try
  {
   //调用带throws声明的方法,必须显式捕获该异常
   //否则,必须在main方法中再次声明抛出
   throwChecked(-3);   
  }
  catch (Exception e)
  {
   System.out.println(e.getMessage());
  }
  //调用抛出Runtime异常的方法既可以显式捕获该异常,
  //也可不理会该异常
  throwRuntime(3);
 }
 public static void throwChecked(int a)throws Exception
 {
  if (a > 0)
  {
   //自行抛出Exception异常
   //该代码必须处于try块里,或处于带throws声明的方法中
   throw new Exception("a的值大于0,不符合要求");
  }
 }
 public static void throwRuntime(int a)
 {
  if (a > 0)
  {
   //自行抛出RuntimeException异常,既可以显式捕获该异常
   //也可完全不理会该异常,把该异常交给该方法调用者处理
   throw new RuntimeException("a的值大于0,不符合要求");
  }
 }

2、自定义异常类

     用户自定义异常都应该继承 Exception 基类,如果希望自定义 Runtime 异常,则应该继承 RuntimeException 基类。定义异常类时通常需要提供两种构造器:一个是无参的构造器;另一个是带一个字符串参数的构造器,这个字符串将作为该异常对象的详细说明(也就是一场对象 getMessage 方法的返回值)。

3、catch 和 throw 同时使用

     try

     {

            ...

     }

     catch(Exception e)

     {

           throw new MyException("自定义错误信息。");

     }

4、异常链

 

四、Java 的异常跟踪栈

五、异常处理规则

1、不要过度使用异常

2、不要使用过于庞大的 try 块

3、避免使用 Catch All 语句

4、不要忽略捕获到的异常

 

========================================================================

 

作业:

一、

1、定义一个 People 类,有name字段,age字段,要求0 < age < 150

2、定义一个 Student 类, 有 name 字段,age 字段,要求 0 < age < 22

3、自定义一个异常类ageException,继承自 Exception,当people或者student的age不符合要求时,要抛出这个异常类。(throw)

4、定义一个异常类AgeRuntimeException,继承自 RuntimeException,当people或者student的age不符合要求时,要抛出这个异常类。(throw)

实例化People和student,分别赋age字段值 33,44。然后打印异常信息。

 

二、

使用 FileInputStream 打开一个文件,并打印文件内容,使用 try、catch、finally,使用finally来关闭文件流。

你可能感兴趣的:(jvm,虚拟机,网络应用)