Detailed java exception handling mechanism



Detailed java exception handling mechanism

The program is difficult to be perfect, can not help but have a variety of abnormalities. For example, the program itself has a bug, such as when the program prints the printer is out of paper, such as lack of memory. In order to address these anomalies, we need to know the cause of the exception. For some common exceptions, we can also provide some response plans. C language exception handling is achieved by a simple function return values, but the meaning is often representative of the return value is determined by practice. Programmers need to query large amounts of data, it is possible to find the cause of a blur. Object-oriented languages, such as C ++, Java, Python tend to have more complex exception handling mechanism. Here to discuss exception handling mechanism in Java.

Exception Handling

A large part of Java exception handling mechanism from C ++. It allows programmers to skip the problem temporarily unable to process to continue with the subsequent development, or let the program according to the abnormal make smarter treatment .

Java uses some special object to represent an abnormal condition, such object is called an exception object. When an abnormal condition occurs, Java will be based on pre-set, throw (throw) object represents the current situation. The so-called throw is a special way to return . The thread will be suspended, layer by layer quit method is called until it encounters an exception handler (Exception Handler). Exception handler can catch (catch) the exception object, and to determine the next action based on the object, such as:

  • To remind the user
  • Handling Exceptions
  • Continue program
  • exit the program

Exception handler looks, which consists try, catch, finally followed by the program blocks. finally not necessary.

try {
   // try块中放可能发生异常的代码。
   // 如果执行完try且不发生异常,则接着去执行finally块和finally后面的代码(如果有的话)。
   // 如果发生异常,则尝试去匹配catch块。
}
 
catch() {
  // 每一个catch块用于捕获并处理一个特定的异常,或者这异常类型的子类。Java7中可以将多个异常声明在一个catch中。
  // catch后面的括号定义了异常类型和异常参数。如果异常与之匹配且是最先匹配到的,则虚拟机将使用这个catch块来处理异常。
  // 在catch块中可以使用这个块的异常参数来获取异常的相关信息。异常参数是这个catch块中的局部变量,其它块不能访问。
  // 如果当前try块中发生的异常在后续的所有catch中都没捕获到,则先去执行finally,然后到这个函数的外部caller中去匹配异常处理器。
  // 如果try中没有发生异常,则所有的catch块将被忽略。
}
 
catch() {
 
 ...;
 
}
 
finally {
  // finally块通常是可选的。
  // 无论异常是否发生,异常是否匹配被处理,finally都会执行。
  // 一个try至少要有一个catch块,否则, 至少要有1个finally块。但是finally不是用来处理异常的,finally不会捕获异常。
// 如果在try或者catch语句中存在return语句,则return语句会在finally语句执行结束后执行,但是finally并不能改变返回值。
// 如果在finally语句中也有return,那么try和catch中的return语句会丢失,实际会返回finally中的返回值。
 // finally主要做一些清理工作,如流的关闭,数据库连接的关闭等。
}

The monitoring program block exception handler later try. catch brackets have a type of abnormal parameters, representative to be captured. catch catches corresponding types and derived classes. catch behind the block contains the type of the exception for the operation to be performed. try the monitored block may throw more than one type of exception, the exception handler may have a plurality of catch block. block is finally back regardless of whether an exception occurs, the program should be executed.

We put in a try can go wrong, it is necessary to monitor the program, designed to deal with an exception in the catch of the program.

The following is a part of the Java program using the exception handling. Try to read the program is part of a line of text from the file. In the process of reading a file, there may be an IOException occurs:

BufferedReader br = new BufferedReader(new FileReader("file.txt"));
try {
  StringBuilder sb = new StringBuilder();
  String line = br.readLine();
 
  while (line != null) {
    sb.append(line);
    sb.append("\n");
    line = br.readLine();
  }
  String everything = sb.toString();
} 
catch(IOException e) {
  e.printStackTrace();
  System.out.println("IO problem");
}
finally {
  br.close();
}

If we catch IOException e of the class object, the object can be operated. For example, call the object's printStackTrace (), prints the current stack situation. In addition, we also print the prompt "IO problem" to the end.

Whether or not there is an exception, the program will eventually enter the finally block. We close the file in the finally block, empty the resources occupied by the file descriptor.

The type of exception

Java Exception class inherits from Trowable class. Throwable object class can throw (throw).

Orange: unchecked; blue: checked

Throwable objects can be divided into two groups. One set is unchecked exceptions, exception handling mechanism for this group are often not abnormal, comprising:

1.Error category generally refers to an internal Java errors and mistakes such as resource depletion. When the Error (and derived classes) happens, we can not solve the Error in the programming level, so it should exit the program directly.

2.Exception类有特殊的一个衍生类RuntimeException。RuntimeException(及其衍生类)是Java程序自身造成的,也就是说,由于程序员在编程时犯错RuntimeException完全可以通过修正Java程序避免。比如将一个类型的对象转换成没有继承关系的另一个类型,即ClassCastException。这类异常应该并且可以避免。

剩下的是checked异常。这些类是由编程与环境互动造成程序在运行时出错。比如读取文件时,由于文件本身有错误,发生IOException。再比如网络服务器临时更改URL指向,造成MalformedURLException。文件系统和网络服务器是在Java环境之外的,并不是程序员所能控制的。如果程序员可以预期异常,可以利用异常处理机制来制定应对预案。比如文件出问题时,提醒系统管理员。再比如在网络服务器出现问题时,提醒用户,并等待网络服务器恢复。异常处理机制主要是用于处理这样的异常。

抛出异常

在上面的程序中,异常来自于我们对Java IO API的调用。我们也可以在自己的程序中抛出异常,比如下面的battery类,有充电和使用方法:

public class Test
{
  public static void main(String[] args)
  {
    Battery aBattery = new Battery();
    aBattery.chargeBattery(0.5);
    aBattery.useBattery(-0.5);
  }
}
 
class Battery 
{
  /**
   * increase battery
   */
  public void chargeBattery(double p)
  {
    // power <= 1
    if (this.power + p < 1.) {
      this.power = this.power + p;
    }
    else {
      this.power = 1.;
    }
  }
 
  /**
   * consume battery
   */
  public boolean useBattery(double p)
  {
    try {
      test(p);
    }
    catch(Exception e) {
      System.out.println("catch Exception");
      System.out.println(e.getMessage());
      p = 0.0;
    }
 
    if (this.power >= p) {
      this.power = this.power - p;
      return true;
    }
    else {
      this.power = 0.0;
      return false;
    }
  }
 
  /**
   * test usage
   */
  private void test(double p) throws Exception // I just throw, don't handle
  {
    if (p < 0) {
      Exception e = new Exception("p must be positive");
      throw e;
    }
  }
 
  private double power = 0.0; // percentage of battery
}

useBattery()表示使用电池操作。useBattery()方法中有一个参数,表示使用的电量。我们使用test()方法测试该参数。如果该参数为负数,那么我们认为有异常,并抛出。

在test中,当有异常发生时(p < 0),我们创建一个Exception对象e,并用一个字符串作为参数。字符串中包含有异常相关的信息,该参数不是必需的。使用throw将该Exception对象抛出。

我们在useBattery()中有异常处理器。由于test()方法不直接处理它产生的异常,而是将该异常抛给上层的useBattery(),所以在test()的定义中,我们需要throws Exception来说明。

(假设异常处理器并不是位于useBattery()中,而是在更上层的main()方法中,我们也要在useBattery()的定义中增加throws Exception。)

在catch中,我们使用getMessage()方法提取其异常中包含的信息。上述程序的运行结果如下:

catch Exception
p must be positive

异常处理器中,我们会捕捉任意Exception类或者其衍生类异常。这往往不利于我们识别问题,特别是一段程序可能抛出多种异常时。我们可以提供一个更加具体的类来捕捉。

自定义异常

我们可以通过继承来创建新的异常类。在继承时,我们往往需要重写构造方法。异常有两个构造方法,一个没有参数,一个有一个String参数。比如:

class BatteryUsageException extends Exception
{
  public BatteryUsageException() {}
  public BatteryUsageException(String msg) {
    super(msg);
  }
}

我们可以在衍生类中提供更多异常相关的方法和信息。

在自定义异常时,要小心选择所继承的基类。一个更具体的类要包含更多的异常信息,比如IOException相对于Exception。

注意点

  • try块中的局部变量和catch块中的局部变量(包括异常变量),以及finally中的局部变量,他们之间不可共享使用。
  • 每一个catch块用于处理一个异常。异常匹配是按照catch块的顺序从上往下寻找的,只有第一个匹配的catch会得到执行。匹配时,不仅运行精确匹配,也支持父类匹配,因此,如果同一个try块下的多个catch异常类型有父子关系,应该将子类异常放在前面,父类异常放在后面,这样保证每个catch块都有存在的意义。
  • finally块不管异常是否发生,只要对应的try执行了,则它一定也执行。只有一种方法让finally块不执行:System.exit()。因此finally块通常用来做资源释放操作:关闭文件,关闭数据库连接等等。良好的编程习惯是:在try块中打开资源,在finally块中清理释放这些资源。
  • finally块没有处理异常的能力。处理异常的只能是catch块。
  • 空指针异常

产生的原因
  • 调用空对象的方法
  • 获取或者修改一个对象不存在的字段
  • 字符串变量未初始化
  • 接口类型的对象没有,没有具体的类初始化
  • 获取一个空数组的长度
  • 获得或者修改空数组一个位置上的内容
解决方法
  • 从已知的String对象中调用equals()和equalsIgnoreCase()方法,而非未知对象。

    Object unknownObject = null;
    
    //错误方式 – 可能导致 NullPointerException
    if(unknownObject.equals("knownObject")){
      System.err.println("This may result in NullPointerException if unknownObject is null");
    }
    
    //正确方式 - 即便 unknownObject是null也能避免NullPointerException
    if("knownObject".equals(unknownObject)){
      System.err.println("better coding avoided NullPointerException");
    }
  • 当valueOf()和toString()返回相同的结果时,宁愿使用前者。

    igDecimal bd = getPrice();
    System.out.println(String.valueOf(bd)); //不会抛出空指针异常
    System.out.println(bd.toString()); //抛出 "Exception in thread "main" java.lang.NullPointerException"
  • 使用null安全的方法和库 有很多开源库已经为您做了繁重的空指针检查工作。其中最常用的一个的是Apache commons 中的StringUtils。你可以使用StringUtils.isBlank(),isNumeric(),isWhiteSpace()以及其他的工具方法而不用担心空指针异常

//StringUtils方法是空指针安全的,他们不会抛出空指针异常
System.out.println(StringUtils.isEmpty(null));
System.out.println(StringUtils.isBlank(null));
System.out.println(StringUtils.isNumeric(null));
System.out.println(StringUtils.isAllUpperCase(null));
 
Output:
true
true
false
false
  • 避免从方法中返回空指针,而是返回空collection或者空数组。Collections类提供了方便的空List,Set和Map: Collections.EMPTY_LIST,Collections.EMPTY_SET,Collections.EMPTY_MAP。这里是实例。

    public List getOrders(Customer customer){
      List result = Collections.EMPTY_LIST;
      return result;
    }
  • 使用annotation@NotNull 和 @Nullable

  • 避免你的代码中不必要的自动包装和自动解包。

    Person ram = new Person("ram");
    int phone = ram.getPhone();
  • 遵从Contract并定义合理的默认值。

  • 定义数据库中的字段是否可为空。

  • **使用空对象模式(Null Object Pattern)

面试题目

题目一
package defineexception;
public class ExceptionTest3
{
    public void method()
    {
        try
        {
            System.out.println("try");
            return;
        }
        catch(Exception ex)
        {
            System.out.println("异常发生了");
        }
        finally
        {
            System.out.println("finally");
        }
        System.out.println("异常处理后续的代码");
    }
    public static void main(String[] args)
    {
        ExceptionTest3 test =new ExceptionTest3();
        test.method();
    }
}
结果:
try
finally
分析

try块中存在return语句,那么首先也需要将finally块中的代码执行完毕,再执行return语句,而且之后的其他代码也不会再执行了

题目二
package defineexception;
public class ExceptionTest3
{
    public void method()
    {
        try
        {
            System.out.println("try");
            System.exit(0);
        }
        catch(Exception ex)
        {
            System.out.println("异常发生了");
        }
        finally
        {
            System.out.println("finally");
        }
        System.out.println("异常处理后续的代码");
    }
    public static void main(String[] args)
    {
        ExceptionTest3 test =new ExceptionTest3();
        test.method();
    }
}
结果:
try
分析:

先执行try块中的System.exit(0)语句,已经退出了虚拟机系统,所以不会执行finally块的代码

总结

异常处理是在解决问题,同时也是在制造问题。大型项目中,过多、过细的异常处理往往会导致程序变得一团糟。异常处理的设计并不简单,并需要谨慎使用。

Guess you like

Origin www.cnblogs.com/XtsLife/p/12079643.html