精华内容
下载资源
问答
  • java 异常进行忽略by ... 为什么您应该忽略Java中异常以及如何正确进行处理 (Why you should ignore exceptions in Java and how to do it correctly) In this article, I will show how to ignore checke...

    java 中对异常进行忽略

    by Rainer Hahnekamp

    通过Rainer Hahnekamp

    为什么您应该忽略Java中的异常以及如何正确进行处理 (Why you should ignore exceptions in Java and how to do it correctly)

    In this article, I will show how to ignore checked exceptions in Java. I will start by describing the rationale behind it and the common pattern to resolve this issue. Then I will present some libraries for that purpose.

    在本文中,我将展示如何忽略Java中的检查异常。 我将首先描述其背后的原理以及解决此问题的通用模式。 然后,我将为此目的提供一些库。

    已检查和未检查的异常 (Checked and Unchecked Exceptions)

    In Java, a method can force its caller to deal with the occurrence of potential exceptions. The caller can use the try/catch clause, where the try contains the actual code and catch contains the code to execute when the exception occurs.

    在Java中,方法可以强制其调用方处理潜在异常的发生。 调用者可以使用try / catch子句,其中try包含实际代码,而catch包含在发生异常时执行的代码。

    Alternatively, the caller can pass on that burden to its parent caller. This can go upwards until the main method is reached. If the main method also passes on the exception, the application will crash when an exception happens.

    替代地,呼叫者可以将负担转嫁给其父呼叫者 。 这可以向上进行,直到达到主要方法为止。 如果main方法也传递了异常,则发生异常时应用程序将崩溃。

    In the case of an exception, there are many scenarios where the application cannot continue to run and needs to stop. There are no alternative paths. Unfortunately, that means Java forces us to write code for a situation where the application shouldn’t run anymore. Quite useless!

    在例外情况下,在许多情况下应用程序无法继续运行而需要停止。 没有其他选择。 不幸的是,这意味着Java迫使我们为应用程序不再运行的情况编写代码。 没用!

    An option is to minimise that boilerplate code. We can wrap the exception into a RuntimeException, which is an unchecked exception. This has the effect that, even though the application still crashes, we don’t have to provide any handling code.

    一种选择是最小化该样板代码。 我们可以将异常包装到RuntimeException中 ,这是未经检查的异常。 这样的结果是,即使应用程序仍然崩溃,我们也不必提供任何处理代码。

    By no means do we log the exception and let the application continue like nothing has happened. It is possible, but is similar to opening Pandora’s Box.

    我们绝不记录异常,并让应用程序像没有发生一样继续运行。 可能,但是类似于打开Pandora的盒子。

    We call these exceptions, for which we have to write extra code, checked exceptions. The others of the RuntimeException type we call unchecked exceptions.

    我们将这些异常称为必须检查的异常 ,为此我们必须编写额外的代码 其他RuntimeException类型的我们称为未检查的异常

    为什么要检查异常? (Why Checked Exceptions at all?)

    We can find lots of checked exceptions in third-party libraries, and even in the Java Class Library itself. The reason is pretty straightforward. A library vendor cannot predict in which context the developer will use their code.

    我们可以在第三方库中甚至在Java类库本身中找到很多经过检查的异常。 原因很简单。 库供应商无法预测开发人员将在哪种上下文中使用其代码。

    Logically, they don’t know if our application has alternative paths. So they leave the decision to us. Their responsibility is to “label” methods that can potentially throw exceptions. Those labels give us the chance to implement counter-actions.

    从逻辑上讲,他们不知道我们的应用程序是否具有替代路径。 因此,他们将决定权留给了我们。 他们的责任是“标记”可能引发异常的方法。 这些标签使我们有机会实施反措施。

    A good example is the connection to a database. The library vendor marks the connection retrieval method with an exception. If we use the database as a cache, we can send our queries directly to our primary database. This is the alternative path.

    一个很好的例子是与数据库的连接。 库供应商将连接检索方法标记为异常。 如果我们使用数据库作为缓存,则可以将查询直接发送到主数据库。 这是替代路径。

    If our database is not the cache, there is no way the application can continue to run. And it’s OK if the application crashes:

    如果我们的数据库不是缓存,则应用程序无法继续运行。 如果应用程序崩溃,也可以:

    丢失的数据库 (A lost database)

    Let’s put our theoretical example to real code:

    让我们将理论示例应用于实际代码:

    public DbConnection getDbConnection(String username, String password) {  try {    return new DbProvider().getConnection(username, password);  } catch (DbConnectionException dce) {    throw new RuntimeException(dce);  }}

    The database is not used as a cache. In the event of a lost connection, we need to stop the application at once.

    该数据库不用作缓存。 如果连接丢失,我们需要立即停止应用程序。

    As described above, we wrap the DbConnectionException into a RuntimeException.

    如上所述,我们将DbConnectionException包装到RuntimeException中

    The required code is relatively verbose and always the same. This creates lots of duplication and decreases the readability.

    所需的代码相对冗长且始终相同。 这样会造成大量重复,并降低了可读性。

    RuntimeException包装器 (The RuntimeException Wrapper)

    We can write a function to simplify this. It should wrap a RuntimeException over some code and return the value. We cannot simply pass code in Java. The function must be part of a class or interface. Something like this:

    我们可以编写一个函数来简化此过程。 它应该在一些代码上包装一个RuntimeException并返回该值。 我们不能简单地用Java传递代码。 该函数必须是类或接口的一部分。 像这样:

    public interface RuntimeExceptionWrappable<T> {  T execute() throws Exception;} public class RuntimeExceptionWrapper {  public static <T> T wrap(RuntimeExceptionWrappable<T> runtimeExceptionWrappable) {    try {      return runtimeExceptionWrappable.execute();    } catch (Exception exception) {      throw new RuntimeException(exception);    }  }} public class DbConnectionRetrieverJava7 {  public DbConnection getDbConnection(final String username, final String password) {    RuntimeExceptionWrappable<DbConnection> wrappable = new RuntimeExceptionWrappable<DbConnection>() {      public DbConnection execute() throws Exception {        return new DbProvider().getConnection(username, password);      }    };    return RuntimeExceptionWrapper.wrap(wrappable);  }}

    The RuntimeException wrapping has been extracted into its own class. In terms of software design, this might be the more elegant solution. Still, given the amount of code, we can hardly say the situation got better.

    RuntimeException包装已提取到其自己的类中。 在软件设计方面,这可能是更优雅的解决方案。 尽管如此,鉴于代码量,我们很难说情况会好转。

    With Java 8 lambdas, things got easier. If we have an interface with one method only, then we just write the specific code of that method. The compiler does the rest for us. The unnecessary or “syntactic sugar code” to create a specific or anonymous class is not required any more. That’s the basic use case for Lambdas.

    使用Java 8 lambda,事情变得更加容易。 如果我们只有一个方法的接口,那么我们只需编写该方法的特定代码。 编译器为我们完成其余的工作。 不再需要创建特定类或匿名类的不必要的或“语法糖代码”。 这是Lambda的基本用例。

    In Java 8, our example above looks like:

    在Java 8中,上面的示例如下所示:

    @FunctionalInterfacepublic interface RuntimeExceptionWrappable<T> {  T execute() throws Exception;} public class DbConnectionRetrieverJava8 {  public DbConnection getDbConnection(String username, String password) {    return RuntimeExceptionWrapper.wrap(() ->      new DbProvider().getConnection(username, password));  }}

    The difference is quite clear: the code is more concise.

    区别很明显:代码更加简洁。

    Streams&Co.中的例外 (Exceptions in Streams & Co.)

    RuntimeExceptionWrappable is a very generic interface. It is just a function that returns a value. Use cases for that function, or its variations, appear all over. For our convenience, Java’s Class Library has a set of such common Interfaces built-in. They are in the package java.util.function and are better known as the “Functional Interfaces.” Our RuntimeExceptionWrappable is similar to java.util.function.Supplier<;T>.

    RuntimeExceptionWrappable是一个非常通用的接口。 它只是一个返回值的函数。 该功能或其变型的用例随处可见。 为方便起见,Java的类库内置了一组此类通用​​接口。 它们位于java.util.functionjava.util.function并且被称为“功能接口”。 我们的RuntimeExceptionWrappablejava.util.function.Supplier< ; T>相似。

    These interfaces form the prerequisite of the powerful Stream, Optional, and other features which are also part of Java 8. In particular, Stream comes with a lot of different methods for processing collections. Many of these methods have a “Functional Interface” as parameter.

    这些接口构成了强大的Stream,Optional和Java 8的其他功能的先决条件。特别是,Stream附带了许多用于处理集合的不同方法。 这些方法中的许多方法都有“功能接口”作为参数。

    Let’s quickly switch the use case. We have a list of URL strings that we want to map into a list of objects of type java.net.URL.

    让我们快速切换用例。 我们有一个URL字符串列表,我们希望将其映射到java.net.URL类型的对象列表中。

    The following code does not compile:

    以下代码无法编译

    public List<URL> getURLs() {  return Stream    .of(“https://www.hahnekamp.com", “https://www.austria.info")    .map(this::createURL)    .collect(Collectors.toList());} private URL createURL(String url) throws MalformedURLException {  return new URL(url);}

    There is a big problem when it comes to exceptions. The Interfaces defined in java.util.function don’t throw exceptions. That’s why our method createURL doesn’t have the same signature as java.util.function.Function, which is the parameter of the map method.

    当涉及到异常时,存在一个大问题。 java.util.function定义的接口不会引发异常。 这就是为什么我们的方法createURLjava.util.function.Function具有不同的签名的原因,后者是map方法的参数。

    What we can do is to write the try/catch block inside the lambda:

    我们可以做的是在lambda里面写try / catch块:

    public List<URL> getURLs() {  return Stream    .of(“https://www.hahnekamp.com", “https://www.austria.info")    .map(url -> {      try {        return this.createURL(url);      } catch (MalformedURLException e) {        throw new RuntimeException(e);      }    })    .collect(Collectors.toList());}

    This compiles, but doesn’t look nice either. We can now take a step further and write a wrapper function along a new interface similar to RuntimeExceptionWrappable`:

    这样可以编译,但是看起来也不好。 现在,我们可以更进一步,并沿着类似于RuntimeExceptionWrappable`的新接口编写包装器函数:

    @FunctionalInterfacepublic interface RuntimeWrappableFunction<T, R> {  R apply(T t) throws Exception;} public class RuntimeWrappableFunctionMapper {  public static <T, R> Function<T, R> wrap(    RuntimeWrappableFunction<T, R> wrappable) {      return t -> {        try {          return wrappable.apply(t);        } catch(Exception exception) {          throw new RuntimeException(exception);        }      };    }}

    And apply it to our Stream example:

    并将其应用于我们的Stream示例:

    public List<URL> getURLs() {  return Stream    .of(“https://www.hahnekamp.com”, “https://www.austria.info”)    .map(RuntimeWrappableFunctionMapper.wrap(this::createURL))    .collect(Collectors.toList());} private URL createURL(String url) throws MalformedURLException {  return new URL(url);}

    Great! Now we have a solution, where we can:

    大! 现在我们有了一个解决方案,在这里我们可以:

    • run code without catching checked exceptions, and

      运行代码而不捕获检查的异常,并且
    • use exception-throwing lambdas in Stream, Optional, and so on.

      在Stream,Optional等中使用抛出异常的Lambda。

    偷偷抢救 (SneakyThrow to the rescue)

    The SneakyThrow library lets you skip copying and pasting the code snippets from above. Full disclosure: I am the author.

    SneakyThrow库使您可以从上方跳过复制和粘贴代码段。 完全公开:我是作者。

    SneakyThrow comes with two static methods. One runs code without catching checked exceptions. The other method wraps an exception-throwing lambda into one of the Functional Interfaces:

    SneakyThrow带有两种静态方法。 一个运行代码而不捕获检查的异常。 另一种方法将引发异常的lambda包装到一个功能接口中:

    //SneakyThrow returning a resultpublic DbConnection getDbConnection(String username, String password) {  return sneak(() -> new DbProvider().getConnection(username, password));} //SneakyThrow wrapping a functionpublic List<URL> getURLs() {  return Stream    .of(“https://www.hahnekamp.com", “https://www.austria.info")    .map(sneaked(this::createURL))    .collect(Collectors.toList());}

    替代图书馆 (Alternative Libraries)

    投掷功能 (ThrowingFunction)

    //ThrowingFunction returning a resultpublic DbConnection getDbConnection(String username, String password) {  return unchecked(() ->     new DbProvider().getConnection(username, password)).get();} //ThrowingFunction returning a functionpublic List<URL> getURLs() {  return Stream    .of(“https://www.hahnekamp.com", “https://www.austria.info")    .map(unchecked(this::createURL))    .collect(Collectors.toList());}

    In contrast to SneakyThrow, ThrowingFunction can’t execute code directly. Instead, we have to wrap it into a Supplier and call the Supplier afterwards. This approach can be more verbose than SneakyThrow.

    与SneakyThrow相比,ThrowingFunction无法直接执行代码。 相反,我们必须将其包装到供应商中,然后再致电供应商。 这种方法比SneakyThrow更为冗长。

    If you have multiple unchecked Functional Interfaces in one class, then you have to write the full class name with each static method. This is because unchecked does not work with method overloading.

    如果在一个类中有多个未经检查的功能接口,则必须使用每个静态方法编写完整的类名。 这是因为未选中不适用于方法重载。

    On the other hand, ThrowingFunction provides you with more features than SneakyThrow. You can define a specific exception you want to wrap. It is also possible that your function returns an Optional, otherwise known as “lifting.”

    另一方面,ThrowingFunction比SneakyThrow提供更多的功能。 您可以定义要包装的特定异常。 您的函数还可能返回一个Optional,也称为“ lifting”。

    I designed SneakyThrow as an opinionated wrapper of ThrowingFunction.

    我将SneakyThrow设计为ThrowingFunction的自包装。

    瓦夫 (Vavr)

    Vavr, or “JavaSlang,” is another alternative. In contrast to SneakyThrow and ThrowingFunction, it provides a complete battery of useful features that enhance Java’s functionality.

    Vavr或“ JavaSlang”是另一种选择。 与SneakyThrow和ThrowingFunction相比,它提供了一整套有用的功能,这些功能增强了Java的功能。

    For example, it comes with pattern matching, tuples, its own Stream and much more. If you haven’t heard of it, it is definitely worth a look. Prepare to invest some time in order to understand its full potential.

    例如,它带有模式匹配,元组,自己的Stream等。 如果您还没有听说过,那绝对值得一看。 准备投入一些时间以了解其全部潜力。

    //Vavr returning a resultpublic DbConnection getDbConnection(String username, String password) {  return Try.of(() ->   new DbProvider().getConnection(username, password))    .get();} //Vavr returning a functionpublic List<URL> getURLs() {  return Stream    .of(“https://www.hahnekamp.com", “https://www.austria.info")    .map(url -> Try.of(() -> this.createURL(url)).get())    .collect(Collectors.toList());}

    Lombok计划 (Project Lombok)

    Such a list of libraries is not complete without mentioning Lombok. Like Vavr, it offers much more functionality than just wrapping checked exceptions. It is a code generator for boilerplate code and creates full Java Beans, Builder objects, logger instances, and much more.

    如果不提及Lombok,这样的库列表是不完整的。 像Vavr一样,它提供的功能远远超过仅包装已检查的异常。 它是用于样板代码的代码生成器,并创建完整的Java Bean,Builder对象,记录器实例等。

    Lombok achieves its goals by bytecode manipulation. Therefore, we require an additional plugin in our IDE.

    Lombok通过字节码操作实现其目标。 因此,我们在IDE中需要一个附加插件。

    @SneakyThrows is Lombok’s annotation for manipulating a function with a checked exception into one that doesn’t. This approach doesn’t depend on the usage of lambdas, so you can use it for all cases. It is the least verbose library.

    @SneakyThrows是Lombok的注释,用于将具有已检查异常的函数处理为没有的函数。 这种方法不依赖于lambda的用法,因此您可以在所有情况下使用它。 这是最不详细的库。

    Please keep in mind that Lombok manipulates the bytecode which can cause problems with other tooling you might use.

    请记住,Lombok会操纵字节码,这可能会导致您可能使用的其他工具出现问题。

    //Lombok returning a result@SneakyThrowspublic DbConnection getDbConnection(String username, String password) {  return new DbProvider().getConnection(username, password);} //Lombok returning a functionpublic List<URL> getURLs() {  return Stream    .of(“https://www.hahnekamp.com", “https://www.austria.info")    .map(this::createURL)    .collect(Collectors.toList());} @SneakyThrowsprivate URL createURL(String url) {  return new URL(url);}

    进一步阅读 (Further Reading)

    The code is available on GitHub

    该代码可在GitHub上获得

    Originally published at www.rainerhahnekamp.com on March 17, 2018.

    最初于2018年3月17日发布在www.rainerhahnekamp.com上。

    翻译自: https://www.freecodecamp.org/news/why-you-should-ignore-exceptions-in-java-and-how-to-do-it-correctly-8e95e5775e58/

    java 中对异常进行忽略

    展开全文
  • Java:详解Java中异常(Error与Exception)

    万次阅读 多人点赞 2018-06-22 16:54:26
    Java:详解Java中异常(Error与Exception)》 《Java:简述Java中的自定义异常》 《Java:简述throw-throws异常抛出》 《Java:简述try-catch-finally异常捕获》 《Java:简述try-catch-finallyreturn返回》...
    1. Java:详解Java中的异常(Error与Exception)
    2. Java:简述Java中的自定义异常
    3. Java:简述throw-throws异常抛出
    4. Java:简述try-catch-finally异常捕获
    5. Java:简述try-catch-finally中return返回

    一、 异常机制的概述
          异常机制是指当程序出现错误后,程序如何处理。具体来说,异常机制提供了程序退出的安全通道。当出现错误后,程序执行的流程发生改变,程序的控制权转移到异常处理器。

          程序错误分为三种:1.编译错误;2.运行时错误;3.逻辑错误。
          (1)编译错误是因为程序没有遵循语法规则,编译程序能够自己发现并且提示我们错误的原因和位置,这个也是大家在刚接触编程语言最常遇到的问题。
          (2)运行时错误是因为程序在执行时,运行环境发现了不能执行的操作。
          (3)逻辑错误是因为程序没有按照预期的逻辑顺序执行。异常也就是指程序运行时发生错误,而异常处理就是对这些错误进行处理和控制。
     

    二、 异常的结构    
           在 Java 中,所有的异常都有一个共同的祖先 Throwable(可抛出)。Throwable 指定代码中可用异常传播机制通过 Java 应用程序传输的任何问题的共性。


           Throwable: 有两个重要的子类:Exception(异常)和 Error(错误),二者都是 Java 异常处理的重要子类,各自都包含大量子类。异常和错误的区别是:异常能被程序本身可以处理,错误是无法处理。
          Throwable类中常用方法如下:

    1. 返回异常发生时的详细信息
    public string getMessage();
    
    2. 返回异常发生时的简要描述
    public string toString();
    
    3. 返回异常对象的本地化信息。使用Throwable的子类覆盖这个方法,可以声称本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与getMessage()返回的结果相同
    public string getLocalizedMessage();
    
    4. 在控制台上打印Throwable对象封装的异常信息
    public void printStackTrace();

           Error(错误):是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。例如,Java虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。这些错误表示故障发生于虚拟机自身、或者发生在虚拟机试图执行应用时,如Java虚拟机运行错误(Virtual MachineError)、类定义错误(NoClassDefFoundError)等。这些错误是不可查的,因为它们在应用程序的控制和处理能力之 外,而且绝大多数是程序运行时不允许出现的状况。对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况。在 Java中,错误通过Error的子类描述。 

          Exception(异常):是程序本身可以处理的异常。Exception 类有一个重要的子类 RuntimeException。RuntimeException 类及其子类表示“JVM 常用操作”引发的错误。例如,若试图使用空值对象引用、除数为零或数组越界,则分别引发运行时异常(NullPointerException、ArithmeticException)和 ArrayIndexOutOfBoundException。
          Exception(异常)分两大类:运行时异常和非运行时异常(编译异常)。程序中应当尽可能去处理这些异常。
          1.运行时异常:都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。
          2.非运行时异常 (编译异常):是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。

           通常,Java的异常(Throwable)分为可查的异常(checked exceptions)不可查的异常(unchecked exceptions)


           1. 可查异常(编译器要求必须处置的异常):正确的程序在运行中,很容易出现的、情理可容的异常状况。除了Exception中的RuntimeException及RuntimeException的子类以外,其他的Exception类及其子类(例如:IOException和ClassNotFoundException)都属于可查异常。这种异常的特点是Java编译器会检查它,也就是说,当程序中可能出现这类异常,要么用try-catch语句捕获它,要么用throws子句声明抛出它,否则编译不会通过。
          2. 不可查异常(编译器不要求强制处置的异常):包括运行时异常(RuntimeException与其子类)和错误(Error)。RuntimeException表示编译器不会检查程序是否对RuntimeException作了处理,在程序中不必捕获RuntimException类型的异常,也不必在方法体声明抛出RuntimeException类。RuntimeException发生的时候,表示程序中出现了编程错误,所以应该找出错误修改程序,而不是去捕获RuntimeException。

    三、 异常处理的机制
          在 Java 应用程序中,异常处理机制为:抛出异常,捕捉异常。
          1. 抛出异常:当一个方法出现错误引发异常时,方法创建异常对象并交付运行时系统,异常对象中包含了异常类型和异常出现时的程序状态等异常信息。运行时系统负责寻找处置异常的代码并执行。详细信息请查看简述throw-throws异常抛出》。
          2. 捕获异常:在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适 的异常处理器。运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适 的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。详细信息请查看Java:简述try-catch-finally异常捕获》。

          对于错误、运行时异常、可查异常,Java技术所要求的异常处理方式有所不同。
          1. 错误:对于方法运行中可能出现的Error,当运行方法不欲捕捉时,Java允许该方法不做任何抛出声明。因为,大多数Error异常属于永远不能被允许发生的状况,也属于合理的应用程序不该捕捉的异常。
          2. 运行时异常:由于运行时异常的不可查性,为了更合理、更容易地实现应用程序,Java规定,运行时异常将由Java运行时系统自动抛出,允许应用程序忽略运行时异常。
          3. 可查异常:对于所有的可查异常,Java规定:一个方法必须捕捉,或者声明抛出方法之外。也就是说,当一个方法选择不捕捉可查异常时,它必须声明将抛出异常。

            能够捕捉异常的方法,需要提供相符类型的异常处理器。所捕捉的异常,可能是由于自身语句所引发并抛出的异常,也可能是由某个调用的方法或者Java运行时 系统等抛出的异常。也就是说,一个方法所能捕捉的异常,一定是Java代码在某处所抛出的异常。简单地说,异常总是先被抛出,后被捕捉的。
            异常抛出:任何Java代码都可以抛出异常,如:自己编写的代码、来自Java开发环境包中代码,或者Java运行时系统。无论是谁,都可以通过Java的throw语句抛出异常。从方法中抛出的任何异常都必须使用throws子句。
            异常捕获:捕捉异常通过try-catch语句或者try-catch-finally语句实现。
            总体来说,Java规定:对于可查异常必须捕捉、或者声明抛出。允许忽略不可查的RuntimeException和Error。

    三、Java常见异常
          1. RuntimeException子类:    

    序号 异常名称 异常描述
    1 java.lang.ArrayIndexOutOfBoundsException 数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出。
    2 java.lang.ArithmeticException  算术条件异常。譬如:整数除零等。
    3 java.lang.SecurityException  安全性异常
    4 java.lang.IllegalArgumentException 非法参数异常
    5 java.lang.ArrayStoreException  数组中包含不兼容的值抛出的异常 
    6 java.lang.NegativeArraySizeException 数组长度为负异常 
    7 java.lang.NullPointerException 空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等。

          2.IOException

    序号 异常名称 异常描述
    1 IOException 操作输入流和输出流时可能出现的异常
    2 EOFException 文件已结束异常
    3 FileNotFoundException 文件未找到异常

          3. 其他    

    序号 异常名称 异常描述
    1 ClassCastException 类型转换异常类
    2 ArrayStoreException 数组中包含不兼容的值抛出的异常
    3 SQLException 操作数据库异常类
    4 NoSuchFieldException 字段未找到异常
    5 NoSuchMethodException 方法未找到抛出的异常
    6 NumberFormatException 字符串转换为数字抛出的异常
    7 StringIndexOutOfBoundsException 字符串索引超出范围抛出的异常
    8 IllegalAccessException 不允许访问某类异常
    9 InstantiationException

     当应用程序试图使用Class类中的newInstance()方法创建

    一个类的实例,而指定的类对象无法被实例化时,抛出该异常

    10 java.lang.ClassNotFoundException 找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常。

    四、相关的问题
          1. 为什么要创建自己的异常?
          答:当Java内置的异常都不能明确的说明异常情况的时候,需要创建自己的异常。

          2. 应该在声明方法抛出异常还是在方法中捕获异常?
          答:捕捉并处理知道如何处理的异常,而抛出不知道如何处理的异常。

    展开全文
  • java中异常的捕获及处理

    万次阅读 多人点赞 2019-03-16 15:28:13
    一、Java异常简介 什么是异常? 程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常。异常发生时,是任程序自生自灭,立刻退出终止。在Java即,Java在编译或运行或者运行过程...

    一、Java异常简介

    什么是异常?
    程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常。异常发生时,是任程序自生自灭,立刻退出终止。在Java中即,Java在编译或运行或者运行过程中出现的错误

    Java提供了更加优秀的解决办法:异常处理机制。

    异常处理机制能让程序在异常发生时,按照代码的预先设定的异常处理逻辑,针对性地处理异常,让程序尽最大可能恢复正常并继续执行,且保持代码的清晰。
    Java中的异常可以是函数中的语句执行时引发的,也可以是程序员通过throw 语句手动抛出的,只要在Java程序中产生了异常,就会用一个对应类型的异常对象来封装异常,JRE就会试图寻找异常处理程序来处理异常。

    Java异常机制用到的几个关键字:try、catch、finally、throw、throws。

    •  try        -- 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。
    • catch   -- 用于捕获异常。catch用来捕获try语句块中发生的异常。
    • finally  -- finally语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
    • throw   -- 用于抛出异常。
    • throws -- 用在方法签名中,用于声明该方法可能抛出的异常。主方法上也可以使用throws抛出。如果在主方法上使用了throws抛出,就表示在主方法里面可以不用强制性进行异常处理,如果出现了异常,就交给JVM进行默认处理,则此时会导致程序中断执行。

    产生异常的原因:

    • 用户输入了非法数据。
    • 要打开的文件不存在。
    • 网络通信时连接中断,或者JVM内存溢出。

    这些异常有的是因为用户错误引起,有的是程序错误引起的,还有其它一些是因为物理错误引起的。

    三种类型的异常:

    • 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
    • 运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
    • 错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。

    二、Java异常的分类

    异常的根接口Throwable,其下有2个子接口,Error和Exception。

    •  Error:指的是JVM错误,这时的程序并没有执行,无法处理;
    • Exception:指的是程序运行中产生的异常,用户可以使用处理格式处理。

    Java 内置异常类

    Java 语言定义了一些异常类在 java.lang 标准包中。

    标准运行时异常类的子类是最常见的异常类。由于 java.lang 包是默认加载到所有的 Java 程序的,所以大部分从运行时异常类继承而来的异常都可以直接使用。

    Java 根据各个类库也定义了一些其他的异常,下面的表中列出了 Java 的非检查性异常

    异常 描述
    ArithmeticException 当出现异常的运算条件时,抛出此异常。例如,一个整数"除以零"时,抛出此类的一个实例。
    ArrayIndexOutOfBoundsException 用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。
    ArrayStoreException 试图将错误类型的对象存储到一个对象数组时抛出的异常。
    ClassCastException 当试图将对象强制转换为不是实例的子类时,抛出该异常。
    IllegalArgumentException 抛出的异常表明向方法传递了一个不合法或不正确的参数。
    IllegalMonitorStateException 抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程。
    IllegalStateException 在非法或不适当的时间调用方法时产生的信号。换句话说,即 Java 环境或 Java 应用程序没有处于请求操作所要求的适当状态下。
    IllegalThreadStateException 线程没有处于请求操作所要求的适当状态时抛出的异常。
    IndexOutOfBoundsException 指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。
    NegativeArraySizeException 如果应用程序试图创建大小为负的数组,则抛出该异常。
    NullPointerException 当应用程序试图在需要对象的地方使用 null 时,抛出该异常
    NumberFormatException 当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。
    SecurityException 由安全管理器抛出的异常,指示存在安全侵犯。
    StringIndexOutOfBoundsException 此异常由 String 方法抛出,指示索引或者为负,或者超出字符串的大小。
    UnsupportedOperationException 当不支持请求的操作时,抛出该异常。

    下面的表中列出了 Java 定义在 java.lang 包中的检查性异常类

    异常 描述
    ClassNotFoundException 应用程序试图加载类时,找不到相应的类,抛出该异常。
    CloneNotSupportedException 当调用 Object 类中的 clone 方法克隆对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常。
    IllegalAccessException 拒绝访问一个类的时候,抛出该异常。
    InstantiationException 当试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的类对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常。
    InterruptedException 一个线程被另一个线程中断,抛出该异常。
    NoSuchFieldException 请求的变量不存在
    NoSuchMethodException 请求的方法不存在

    异常方法

    下面的列表是 Throwable 类的主要方法:

    序号 方法及说明
    1 public String getMessage()
    返回关于发生的异常的详细信息。这个消息在Throwable 类的构造函数中初始化了。
    2 public Throwable getCause()
    返回一个Throwable 对象代表异常原因。
    3 public String toString()
    使用getMessage()的结果返回类的串级名字。
    4 public void printStackTrace()
    打印toString()结果和栈层次到System.err,即错误输出流。
    5 public StackTraceElement [] getStackTrace()
    返回一个包含堆栈层次的数组。下标为0的元素代表栈顶,最后一个元素代表方法调用堆栈的栈底。
    6 public Throwable fillInStackTrace()
    用当前的调用栈层次填充Throwable 对象栈层次,添加到栈层次任何先前信息中。

    三、异常的使用及执行流程

    1、异常的处理方案

    try...catch、try...catch...finally、try...finally
        try{
            可能会发生的异常
        }catch(异常类型 异常名(变量)){
            针对异常进行处理的代码
        }catch(异常类型 异常名(变量)){
            针对异常进行处理的代码
        }...
        [finally{
            释放资源代码;
        }]

    注意:

    • catch 不能独立于 try 存在。
    • catch里面不能没有内容
    • 在 try/catch 后面添加 finally 块并非强制性要求的。
    • try 代码后不能既没 catch 块也没 finally 块。
    • try里面越少越好。
    • try, catch, finally 块之间不能添加任何代码。
    • finally里面的代码最终一定会执行(除了JVM退出)
    • 如果程序可能存在多个异常,需要多个catch进行捕获。
    • 异常如果是同级关系,catch谁前谁后没有关系
      如果异常之间存在上下级关系,上级需要放在后面

    2、异常的执行流程

    Error与Exception的区别:

    Error(错误)是系统中的错误,程序员是不能改变的和处理的,是在程序编译时出现的错误,只能通过修改程序才能修正。一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。

    Exception(异常)表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。

    在catch捕获异常时,为什么不考虑使用Throwable类型,而只是使用Exception来进行接收?

    Throwable表示的范围要比Exception大。实际上程序使用Throwable来进行处理,没有任何语法问题,但是却会存在逻辑问题。因为此时出现的(或者说用户能够处理的)只有Exception类型,而如果使用Throwable接收,还会表示可以处理Error的错误,而用户是处理不了Error错误的,所以在开发中用户可以处理的异常都要求以Exception类为主。

    异常是一起处理好还是分开处理好?

    根据实际的开发要求是否严格来决定。在实际的项目开发项目工作中,所有的异常是统一使用Exception处理还是分开处理,完全根据开发者的项目开发标准来决定。如果项目开发环境严谨,基本上要求针对每一种异常分别进行处理,并且要详细记录下异常产生的时间以及产生的位置,这样可以方便程序维护人员进行代码的维护。再次注意:处理多个异常时,捕获范围小的异常要放在捕获范围大的异常之前处理。

    throw和throws的区别?

    throw和throws都是在异常处理中使用的关键字,区别如下:

    • throw:指的是在方法中人为抛出一个异常对象(这个异常对象可能是自己实例化或者抛出已存在的);
    • throws:在方法的声明上使用,表示此方法在调用时必须处理异常。

    检查型异常(Checked Exception)与非检查型异常(Unchecked Exception)区别?

    • 所有的检查性异常都继承自java.lang.Exception;所有的非检查性异常都继承自java.lang.RuntimeEx ception。
    • 检查性异常和非检查性异常最主要的区别在于其处理异常的方式:检查性异常必须使用try catch或者throws等关键字进行处理,否则编译器会报错;非检查性异常一般是程序代码写的不够严谨而导致的问题,可以通过修改代码来规避。
    • 常见的运行时异常:空指针异常(NullPointerException)、除零异常(ArithmeticException)、数组越界异常(ArrayIndexOutOfBoundsException)等;
    • 常见的检查性异常:输入输出异常(IOException)、文件不存在异常(FileNotFoundException)、SQL语句异常(SQLException)等。

    assert关键字(了解)

    在Java中,assert关键字是从JAVA SE 1.4 引入的,为了避免和老版本的Java代码中使用了assert关键字导致错误,Java在执行的时候默认是不启动断言检查的(这个时候,所有的断言语句都 将忽略!),如果要开启断言检查,则需要用开关-enableassertions或-ea来开启。

    assert关键字语法很简单,有两种用法:

    1. assert <boolean表达式>
      如果<boolean表达式>为true,则程序继续执行。
      如果为false,则程序抛出AssertionError,并终止执行。
    2. assert <boolean表达式> : <错误信息表达式>
      如果<boolean表达式>为true,则程序继续执行。
      如果为false,则程序抛出java.lang.AssertionError,并输入<错误信息表达式>。

    例如:

    public class Test {
    	public static void main(String[] args) {
    		int a = 10;
    		int b = 2;
    		assert a == 10:"a不等于10";
    		System.out.println("a="+a);
    	}
    }

     执行结果为:

    public class Test {
    	public static void main(String[] args) {
    		int a = 10;
    		int b = 2;
    		assert a == 20:"a不等于20";
    		System.out.println("a="+a);
    	}
    }

    执行结果为:

    四、自定义异常

    在 Java 中你可以自定义异常。如果要自定义异常类,则扩展Exception类即可,因此这样的自定义异常都属于检查异常(checked exception)。如果要自定义非检查异常,则扩展自RuntimeException。

    按照国际惯例,自定义的异常应该总是包含如下的构造函数:

    • 一个无参构造函数
    • 一个带有String参数的构造函数,并传递给父类的构造函数。
    • 一个带有String参数和Throwable参数,并都传递给父类构造函数
    • 一个带有Throwable 参数的构造函数,并传递给父类的构造函数。

    下面是IOException类的完整源代码,可以借鉴。

    package java.io;
    
    public class IOException extends Exception {
        static final long serialVersionUID = 7818375828146090155L;
    
        public IOException() {
    	super();
        }
    
        public IOException(String message) {
    	super(message);
        }
    
        public IOException(String message, Throwable cause) {
            super(message, cause);
        }
    
        public IOException(Throwable cause) {
            super(cause);
        }
    }
    

    finally块和return

    • 首先一个不容易理解的事实:在 try块中即便有return,break,continue等改变执行流的语句,finally也会执行。
    • finally中的return 会覆盖 try 或者catch中的返回值。
    • finally中的return或异常会抑制(消灭)前面try或者catch块中的异常。
    展开全文
  • Java异常面试题(2020最新版)

    万次阅读 多人点赞 2020-02-19 12:15:59
    文章目录Java异常架构与异常关键字Java异常简介Java异常架构1. Throwable2. Error(错误)3. Exception(异常)运行时异常编译时异常4. 受检异常与非受检异常受检异常非受检异常Java异常关键字Java异常处理声明异常...

    大家好,我是CSDN的博主ThinkWon,“2020博客之星年度总评选"开始啦,希望大家帮我投票,每天都可以投多票哦,点击下方链接,然后点击"最大”,再点击"投TA一票"就可以啦!
    投票链接:https://bss.csdn.net/m/topic/blog_star2020/detail?username=thinkwon
    在技术的世界里,ThinkWon将一路与你相伴!创作出更多更高质量的文章!2020为努力奋斗的你点赞👍,️新的一年,祝各位大牛牛气冲天,牛年大吉!😊😊

    文章目录

    Java面试总结汇总,整理了包括Java基础知识,集合容器,并发编程,JVM,常用开源框架Spring,MyBatis,数据库,中间件等,包含了作为一个Java工程师在面试中需要用到或者可能用到的绝大部分知识。欢迎大家阅读,本人见识有限,写的博客难免有错误或者疏忽的地方,还望各位大佬指点,在此表示感激不尽。文章持续更新中…

    序号 内容 链接地址
    1 Java基础知识面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104390612
    2 Java集合容器面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104588551
    3 Java异常面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104390689
    4 并发编程面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104863992
    5 JVM面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104390752
    6 Spring面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397516
    7 Spring MVC面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397427
    8 Spring Boot面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397299
    9 Spring Cloud面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397367
    10 MyBatis面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/101292950
    11 Redis面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/103522351
    12 MySQL数据库面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104778621
    13 消息中间件MQ与RabbitMQ面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104588612
    14 Dubbo面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104390006
    15 Linux面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104588679
    16 Tomcat面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397665
    17 ZooKeeper面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397719
    18 Netty面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/104391081
    19 架构设计&分布式&数据结构与算法面试题(2020最新版) https://thinkwon.blog.csdn.net/article/details/105870730

    Java异常架构与异常关键字

    Java异常简介

    Java异常是Java提供的一种识别及响应错误的一致性机制。
    Java异常机制可以使程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提高程序健壮性。在有效使用异常的情况下,异常能清晰的回答what, where, why这3个问题:异常类型回答了“什么”被抛出,异常堆栈跟踪回答了“在哪”抛出,异常信息回答了“为什么”会抛出。

    Java异常架构

    1. Throwable

    Throwable 是 Java 语言中所有错误与异常的超类。

    Throwable 包含两个子类:Error(错误)和 Exception(异常),它们通常用于指示发生了异常情况。

    Throwable 包含了其线程创建时线程执行堆栈的快照,它提供了 printStackTrace() 等接口用于获取堆栈跟踪数据等信息。

    2. Error(错误)

    定义:Error 类及其子类。程序中无法处理的错误,表示运行应用程序中出现了严重的错误。

    特点:此类错误一般表示代码运行时 JVM 出现问题。通常有 Virtual MachineError(虚拟机运行错误)、NoClassDefFoundError(类定义错误)等。比如 OutOfMemoryError:内存不足错误;StackOverflowError:栈溢出错误。此类错误发生时,JVM 将终止线程。

    这些错误是不受检异常,非代码性错误。因此,当此类错误发生时,应用程序不应该去处理此类错误。按照Java惯例,我们是不应该实现任何新的Error子类的!

    3. Exception(异常)

    程序本身可以捕获并且可以处理的异常。Exception 这种异常又分为两类:运行时异常和编译时异常。

    运行时异常

    定义:RuntimeException 类及其子类,表示 JVM 在运行期间可能出现的异常。

    特点:Java 编译器不会检查它。也就是说,当程序中可能出现这类异常时,倘若既"没有通过throws声明抛出它",也"没有用try-catch语句捕获它",还是会编译通过。比如NullPointerException空指针异常、ArrayIndexOutBoundException数组下标越界异常、ClassCastException类型转换异常、ArithmeticExecption算术异常。此类异常属于不受检异常,一般是由程序逻辑错误引起的,在程序中可以选择捕获处理,也可以不处理。虽然 Java 编译器不会检查运行时异常,但是我们也可以通过 throws 进行声明抛出,也可以通过 try-catch 对它进行捕获处理。如果产生运行时异常,则需要通过修改代码来进行避免。例如,若会发生除数为零的情况,则需要通过代码避免该情况的发生!

    RuntimeException 异常会由 Java 虚拟机自动抛出并自动捕获(就算我们没写异常捕获语句运行时也会抛出错误!!),此类异常的出现绝大数情况是代码本身有问题应该从逻辑上去解决并改进代码。

    编译时异常

    定义: Exception 中除 RuntimeException 及其子类之外的异常。

    特点: Java 编译器会检查它。如果程序中出现此类异常,比如 ClassNotFoundException(没有找到指定的类异常),IOException(IO流异常),要么通过throws进行声明抛出,要么通过try-catch进行捕获处理,否则不能通过编译。在程序中,通常不会自定义该类异常,而是直接使用系统提供的异常类。该异常我们必须手动在代码里添加捕获语句来处理该异常

    4. 受检异常与非受检异常

    Java 的所有异常可以分为受检异常(checked exception)和非受检异常(unchecked exception)。

    受检异常

    编译器要求必须处理的异常。正确的程序在运行过程中,经常容易出现的、符合预期的异常情况。一旦发生此类异常,就必须采用某种方式进行处理。除 RuntimeException 及其子类外,其他的 Exception 异常都属于受检异常。编译器会检查此类异常,也就是说当编译器检查到应用中的某处可能会此类异常时,将会提示你处理本异常——要么使用try-catch捕获,要么使用方法签名中用 throws 关键字抛出,否则编译不通过。

    非受检异常

    编译器不会进行检查并且不要求必须处理的异常,也就说当程序中出现此类异常时,即使我们没有try-catch捕获它,也没有使用throws抛出该异常,编译也会正常通过。该类异常包括运行时异常(RuntimeException极其子类)和错误(Error)。

    Java异常关键字

    try – 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。
    catch – 用于捕获异常。catch用来捕获try语句块中发生的异常。
    finally – finally语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
    throw – 用于抛出异常。
    throws – 用在方法签名中,用于声明该方法可能抛出的异常。

    Java异常处理

    Java 通过面向对象的方法进行异常处理,一旦方法抛出异常,系统自动根据该异常对象寻找合适异常处理器(Exception Handler)来处理该异常,把各种不同的异常进行分类,并提供了良好的接口。在 Java 中,每个异常都是一个对象,它是 Throwable 类或其子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并可以对其进行处理。Java 的异常处理是通过 5 个关键词来实现的:try、catch、throw、throws 和 finally。

    在Java应用中,异常的处理机制分为声明异常,抛出异常和捕获异常。

    声明异常

    通常,应该捕获那些知道如何处理的异常,将不知道如何处理的异常继续传递下去。传递异常可以在方法签名处使用 throws 关键字声明可能会抛出的异常。

    注意

    • 非检查异常(Error、RuntimeException 或它们的子类)不可使用 throws 关键字来声明要抛出的异常。
    • 一个方法出现编译时异常,就需要 try-catch/ throws 处理,否则会导致编译错误。

    抛出异常

    如果你觉得解决不了某些异常问题,且不需要调用者处理,那么你可以抛出异常。

    throw关键字作用是在方法内部抛出一个Throwable类型的异常。任何Java代码都可以通过throw语句抛出异常。

    捕获异常

    程序通常在运行之前不报错,但是运行后可能会出现某些未知的错误,但是还不想直接抛出到上一级,那么就需要通过try…catch…的形式进行异常捕获,之后根据不同的异常情况来进行相应的处理。

    如何选择异常类型

    可以根据下图来选择是捕获异常,声明异常还是抛出异常

    在这里插入图片描述

    常见异常处理方式

    直接抛出异常

    通常,应该捕获那些知道如何处理的异常,将不知道如何处理的异常继续传递下去。传递异常可以在方法签名处使用 throws 关键字声明可能会抛出的异常。

    private static void readFile(String filePath) throws IOException {
        File file = new File(filePath);
        String result;
        BufferedReader reader = new BufferedReader(new FileReader(file));
        while((result = reader.readLine())!=null) {
            System.out.println(result);
        }
        reader.close();
    }
    

    封装异常再抛出

    有时我们会从 catch 中抛出一个异常,目的是为了改变异常的类型。多用于在多系统集成时,当某个子系统故障,异常类型可能有多种,可以用统一的异常类型向外暴露,不需暴露太多内部异常细节。

    private static void readFile(String filePath) throws MyException {    
        try {
            // code
        } catch (IOException e) {
            MyException ex = new MyException("read file failed.");
            ex.initCause(e);
            throw ex;
        }
    }
    

    捕获异常

    在一个 try-catch 语句块中可以捕获多个异常类型,并对不同类型的异常做出不同的处理

    private static void readFile(String filePath) {
        try {
            // code
        } catch (FileNotFoundException e) {
            // handle FileNotFoundException
        } catch (IOException e){
            // handle IOException
        }
    }
    

    同一个 catch 也可以捕获多种类型异常,用 | 隔开

    private static void readFile(String filePath) {
        try {
            // code
        } catch (FileNotFoundException | UnknownHostException e) {
            // handle FileNotFoundException or UnknownHostException
        } catch (IOException e){
            // handle IOException
        }
    }
    

    自定义异常

    习惯上,定义一个异常类应包含两个构造函数,一个无参构造函数和一个带有详细描述信息的构造函数(Throwable 的 toString 方法会打印这些详细信息,调试时很有用)

    public class MyException extends Exception {
        public MyException(){ }
        public MyException(String msg){
            super(msg);
        }
        // ...
    }
    

    try-catch-finally

    当方法中发生异常,异常处之后的代码不会再执行,如果之前获取了一些本地资源需要释放,则需要在方法正常结束时和 catch 语句中都调用释放本地资源的代码,显得代码比较繁琐,finally 语句可以解决这个问题。

    private static void readFile(String filePath) throws MyException {
        File file = new File(filePath);
        String result;
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(file));
            while((result = reader.readLine())!=null) {
                System.out.println(result);
            }
        } catch (IOException e) {
            System.out.println("readFile method catch block.");
            MyException ex = new MyException("read file failed.");
            ex.initCause(e);
            throw ex;
        } finally {
            System.out.println("readFile method finally block.");
            if (null != reader) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    调用该方法时,读取文件时若发生异常,代码会进入 catch 代码块,之后进入 finally 代码块;若读取文件时未发生异常,则会跳过 catch 代码块直接进入 finally 代码块。所以无论代码中是否发生异常,fianlly 中的代码都会执行。

    若 catch 代码块中包含 return 语句,finally 中的代码还会执行吗?将以上代码中的 catch 子句修改如下:

    catch (IOException e) {
        System.out.println("readFile method catch block.");
        return;
    }
    

    调用 readFile 方法,观察当 catch 子句中调用 return 语句时,finally 子句是否执行

    readFile method catch block.
    readFile method finally block.
    

    可见,即使 catch 中包含了 return 语句,finally 子句依然会执行。若 finally 中也包含 return 语句,finally 中的 return 会覆盖前面的 return.

    try-with-resource

    上面例子中,finally 中的 close 方法也可能抛出 IOException, 从而覆盖了原始异常。JAVA 7 提供了更优雅的方式来实现资源的自动释放,自动释放的资源需要是实现了 AutoCloseable 接口的类。

    private  static void tryWithResourceTest(){
        try (Scanner scanner = new Scanner(new FileInputStream("c:/abc"),"UTF-8")){
            // code
        } catch (IOException e){
            // handle exception
        }
    }
    

    try 代码块退出时,会自动调用 scanner.close 方法,和把 scanner.close 方法放在 finally 代码块中不同的是,若 scanner.close 抛出异常,则会被抑制,抛出的仍然为原始异常。被抑制的异常会由 addSusppressed 方法添加到原来的异常,如果想要获取被抑制的异常列表,可以调用 getSuppressed 方法来获取。

    Java异常常见面试题

    1. Error 和 Exception 区别是什么?

    Error 类型的错误通常为虚拟机相关错误,如系统崩溃,内存不足,堆栈溢出等,编译器不会对这类错误进行检测,JAVA 应用程序也不应对这类错误进行捕获,一旦这类错误发生,通常应用程序会被终止,仅靠应用程序本身无法恢复;

    Exception 类的错误是可以在应用程序中进行捕获并处理的,通常遇到这种错误,应对其进行处理,使应用程序可以继续正常运行。

    2. 运行时异常和一般异常(受检异常)区别是什么?

    运行时异常包括 RuntimeException 类及其子类,表示 JVM 在运行期间可能出现的异常。 Java 编译器不会检查运行时异常。

    受检异常是Exception 中除 RuntimeException 及其子类之外的异常。 Java 编译器会检查受检异常。

    RuntimeException异常和受检异常之间的区别:是否强制要求调用者必须处理此异常,如果强制要求调用者必须进行处理,那么就使用受检异常,否则就选择非受检异常(RuntimeException)。一般来讲,如果没有特殊的要求,我们建议使用RuntimeException异常。

    3. JVM 是如何处理异常的?

    在一个方法中如果发生异常,这个方法会创建一个异常对象,并转交给 JVM,该异常对象包含异常名称,异常描述以及异常发生时应用程序的状态。创建异常对象并转交给 JVM 的过程称为抛出异常。可能有一系列的方法调用,最终才进入抛出异常的方法,这一系列方法调用的有序列表叫做调用栈。

    JVM 会顺着调用栈去查找看是否有可以处理异常的代码,如果有,则调用异常处理代码。当 JVM 发现可以处理异常的代码时,会把发生的异常传递给它。如果 JVM 没有找到可以处理该异常的代码块,JVM 就会将该异常转交给默认的异常处理器(默认处理器为 JVM 的一部分),默认异常处理器打印出异常信息并终止应用程序。

    4. throw 和 throws 的区别是什么?

    Java 中的异常处理除了包括捕获异常和处理异常之外,还包括声明异常和拋出异常,可以通过 throws 关键字在方法上声明该方法要拋出的异常,或者在方法内部通过 throw 拋出异常对象。

    throws 关键字和 throw 关键字在使用上的几点区别如下

    • throw 关键字用在方法内部,只能用于抛出一种异常,用来抛出方法或代码块中的异常,受查异常和非受查异常都可以被抛出。
    • throws 关键字用在方法声明上,可以抛出多个异常,用来标识该方法可能抛出的异常列表。一个方法用 throws 标识了可能抛出的异常列表,调用该方法的方法中必须包含可处理异常的代码,否则也要在方法签名中用 throws 关键字声明相应的异常。

    5. final、finally、finalize 有什么区别?

    • final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表示该变量是一个常量不能被重新赋值。
    • finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法finally代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。
    • finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,Java 中允许使用 finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。

    6. NoClassDefFoundError 和 ClassNotFoundException 区别?

    NoClassDefFoundError 是一个 Error 类型的异常,是由 JVM 引起的,不应该尝试捕获这个异常。

    引起该异常的原因是 JVM 或 ClassLoader 尝试加载某类时在内存中找不到该类的定义,该动作发生在运行期间,即编译时该类存在,但是在运行时却找不到了,可能是变异后被删除了等原因导致;

    ClassNotFoundException 是一个受查异常,需要显式地使用 try-catch 对其进行捕获和处理,或在方法签名中用 throws 关键字进行声明。当使用 Class.forName, ClassLoader.loadClass 或 ClassLoader.findSystemClass 动态加载类到内存的时候,通过传入的类路径参数没有找到该类,就会抛出该异常;另一种抛出该异常的可能原因是某个类已经由一个类加载器加载至内存中,另一个加载器又尝试去加载它。

    7. try-catch-finally 中哪个部分可以省略?

    答:catch 可以省略

    原因

    更为严格的说法其实是:try只适合处理运行时异常,try+catch适合处理运行时异常+普通异常。也就是说,如果你只用try去处理普通异常却不加以catch处理,编译是通不过的,因为编译器硬性规定,普通异常如果选择捕获,则必须用catch显示声明以便进一步处理。而运行时异常在编译时没有如此规定,所以catch可以省略,你加上catch编译器也觉得无可厚非。

    理论上,编译器看任何代码都不顺眼,都觉得可能有潜在的问题,所以你即使对所有代码加上try,代码在运行期时也只不过是在正常运行的基础上加一层皮。但是你一旦对一段代码加上try,就等于显示地承诺编译器,对这段代码可能抛出的异常进行捕获而非向上抛出处理。如果是普通异常,编译器要求必须用catch捕获以便进一步处理;如果运行时异常,捕获然后丢弃并且+finally扫尾处理,或者加上catch捕获以便进一步处理。

    至于加上finally,则是在不管有没捕获异常,都要进行的“扫尾”处理。

    8. try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?

    答:会执行,在 return 前执行。

    注意:在 finally 中改变返回值的做法是不好的,因为如果存在 finally 代码块,try中的 return 语句不会立马返回调用者,而是记录下返回值待 finally 代码块执行完毕之后再向调用者返回其值,然后如果在 finally 中修改了返回值,就会返回修改后的值。显然,在 finally 中返回或者修改返回值会对程序造成很大的困扰,C#中直接用编译错误的方式来阻止程序员干这种龌龊的事情,Java 中也可以通过提升编译器的语法检查级别来产生警告或错误。

    代码示例1:

    public static int getInt() {
        int a = 10;
        try {
            System.out.println(a / 0);
            a = 20;
        } catch (ArithmeticException e) {
            a = 30;
            return a;
            /*
             * return a 在程序执行到这一步的时候,这里不是return a 而是 return 30;这个返回路径就形成了
             * 但是呢,它发现后面还有finally,所以继续执行finally的内容,a=40
             * 再次回到以前的路径,继续走return 30,形成返回路径之后,这里的a就不是a变量了,而是常量30
             */
        } finally {
            a = 40;
        }
    	return a;
    }
    

    执行结果:30

    代码示例2:

    public static int getInt() {
        int a = 10;
        try {
            System.out.println(a / 0);
            a = 20;
        } catch (ArithmeticException e) {
            a = 30;
            return a;
        } finally {
            a = 40;
            //如果这样,就又重新形成了一条返回路径,由于只能通过1个return返回,所以这里直接返回40
            return a; 
        }
    
    }
    

    执行结果:40

    9. 类 ExampleA 继承 Exception,类 ExampleB 继承ExampleA。

    有如下代码片断:

    try {
    	throw new ExampleB("b")
    } catchExampleA e){
    	System.out.println("ExampleA");
    } catchException e){
    	System.out.println("Exception");
    }
    

    请问执行此段代码的输出是什么?

    输出:ExampleA。(根据里氏代换原则[能使用父类型的地方一定能使用子类型],抓取 ExampleA 类型异常的 catch 块能够抓住 try 块中抛出的 ExampleB 类型的异常)

    面试题 - 说出下面代码的运行结果。(此题的出处是《Java 编程思想》一书)

    class Annoyance extends Exception {
    }
    class Sneeze extends Annoyance {
    }
    class Human {
    	public static void main(String[] args)
    	throws Exception {
    		try {
    			try {
    				throw new Sneeze();
    			} catch ( Annoyance a ) {
    				System.out.println("Caught Annoyance");
    				throw a;
    			}
    		} catch ( Sneeze s ) {
    			System.out.println("Caught Sneeze");
    			return ;
    		} finally {
    			System.out.println("Hello World!");
    		}
    	}
    }
    

    结果

    Caught Annoyance
    Caught Sneeze
    Hello World!
    

    10. 常见的 RuntimeException 有哪些?

    • ClassCastException(类转换异常)
    • IndexOutOfBoundsException(数组越界)
    • NullPointerException(空指针)
    • ArrayStoreException(数据存储异常,操作数组时类型不一致)
    • 还有IO操作的BufferOverflowException异常

    11. Java常见异常有哪些

    java.lang.IllegalAccessError:违法访问错误。当一个应用试图访问、修改某个类的域(Field)或者调用其方法,但是又违反域或方法的可见性声明,则抛出该异常。

    java.lang.InstantiationError:实例化错误。当一个应用试图通过Java的new操作符构造一个抽象类或者接口时抛出该异常.

    java.lang.OutOfMemoryError:内存不足错误。当可用内存不足以让Java虚拟机分配给一个对象时抛出该错误。

    java.lang.StackOverflowError:堆栈溢出错误。当一个应用递归调用的层次太深而导致堆栈溢出或者陷入死循环时抛出该错误。

    java.lang.ClassCastException:类造型异常。假设有类A和B(A不是B的父类或子类),O是A的实例,那么当强制将O构造为类B的实例时抛出该异常。该异常经常被称为强制类型转换异常。

    java.lang.ClassNotFoundException:找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常。

    java.lang.ArithmeticException:算术条件异常。譬如:整数除零等。

    java.lang.ArrayIndexOutOfBoundsException:数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出。

    java.lang.IndexOutOfBoundsException:索引越界异常。当访问某个序列的索引值小于0或大于等于序列大小时,抛出该异常。

    java.lang.InstantiationException:实例化异常。当试图通过newInstance()方法创建某个类的实例,而该类是一个抽象类或接口时,抛出该异常。

    java.lang.NoSuchFieldException:属性不存在异常。当访问某个类的不存在的属性时抛出该异常。

    java.lang.NoSuchMethodException:方法不存在异常。当访问某个类的不存在的方法时抛出该异常。

    java.lang.NullPointerException:空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等。

    java.lang.NumberFormatException:数字格式异常。当试图将一个String转换为指定的数字类型,而该字符串确不满足数字类型要求的格式时,抛出该异常。

    java.lang.StringIndexOutOfBoundsException:字符串索引越界异常。当使用索引值访问某个字符串中的字符,而该索引值小于0或大于等于序列大小时,抛出该异常。

    Java异常处理最佳实践

    在 Java 中处理异常并不是一个简单的事情。不仅仅初学者很难理解,即使一些有经验的开发者也需要花费很多时间来思考如何处理异常,包括需要处理哪些异常,怎样处理等等。这也是绝大多数开发团队都会制定一些规则来规范进行异常处理的原因。而团队之间的这些规范往往是截然不同的。

    本文给出几个被很多团队使用的异常处理最佳实践。

    1. 在 finally 块中清理资源或者使用 try-with-resource 语句

    当使用类似InputStream这种需要使用后关闭的资源时,一个常见的错误就是在try块的最后关闭资源。

    public void doNotCloseResourceInTry() {
        FileInputStream inputStream = null;
        try {
            File file = new File("./tmp.txt");
            inputStream = new FileInputStream(file);
            // use the inputStream to read a file
            // do NOT do this
            inputStream.close();
        } catch (FileNotFoundException e) {
            log.error(e);
        } catch (IOException e) {
            log.error(e);
        }
    }
    

    问题就是,只有没有异常抛出的时候,这段代码才可以正常工作。try 代码块内代码会正常执行,并且资源可以正常关闭。但是,使用 try 代码块是有原因的,一般调用一个或多个可能抛出异常的方法,而且,你自己也可能会抛出一个异常,这意味着代码可能不会执行到 try 代码块的最后部分。结果就是,你并没有关闭资源。

    所以,你应该把清理工作的代码放到 finally 里去,或者使用 try-with-resource 特性。

    1.1 使用 finally 代码块

    与前面几行 try 代码块不同,finally 代码块总是会被执行。不管 try 代码块成功执行之后还是你在 catch 代码块中处理完异常后都会执行。因此,你可以确保你清理了所有打开的资源。

    public void closeResourceInFinally() {
        FileInputStream inputStream = null;
        try {
            File file = new File("./tmp.txt");
            inputStream = new FileInputStream(file);
            // use the inputStream to read a file
        } catch (FileNotFoundException e) {
            log.error(e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    log.error(e);
                }
            }
        }
    }
    

    1.2 Java 7 的 try-with-resource 语法

    如果你的资源实现了 AutoCloseable 接口,你可以使用这个语法。大多数的 Java 标准资源都继承了这个接口。当你在 try 子句中打开资源,资源会在 try 代码块执行后或异常处理后自动关闭。

    public void automaticallyCloseResource() {
        File file = new File("./tmp.txt");
        try (FileInputStream inputStream = new FileInputStream(file);) {
            // use the inputStream to read a file
        } catch (FileNotFoundException e) {
            log.error(e);
        } catch (IOException e) {
            log.error(e);
        }
    }
    
    

    2. 优先明确的异常

    你抛出的异常越明确越好,永远记住,你的同事或者几个月之后的你,将会调用你的方法并且处理异常。

    因此需要保证提供给他们尽可能多的信息。这样你的 API 更容易被理解。你的方法的调用者能够更好的处理异常并且避免额外的检查。

    因此,总是尝试寻找最适合你的异常事件的类,例如,抛出一个 NumberFormatException 来替换一个 IllegalArgumentException 。避免抛出一个不明确的异常。

    public void doNotDoThis() throws Exception {
        ...
    }
    public void doThis() throws NumberFormatException {
        ...
    }
    
    

    3. 对异常进行文档说明

    当在方法上声明抛出异常时,也需要进行文档说明。目的是为了给调用者提供尽可能多的信息,从而可以更好地避免或处理异常。
    在 Javadoc 添加 @throws 声明,并且描述抛出异常的场景。

    public void doSomething(String input) throws MyBusinessException {
        ...
    }
    

    4. 使用描述性消息抛出异常

    在抛出异常时,需要尽可能精确地描述问题和相关信息,这样无论是打印到日志中还是在监控工具中,都能够更容易被人阅读,从而可以更好地定位具体错误信息、错误的严重程度等。

    但这里并不是说要对错误信息长篇大论,因为本来 Exception 的类名就能够反映错误的原因,因此只需要用一到两句话描述即可。

    如果抛出一个特定的异常,它的类名很可能已经描述了这种错误。所以,你不需要提供很多额外的信息。一个很好的例子是 NumberFormatException 。当你以错误的格式提供 String 时,它将被 java.lang.Long 类的构造函数抛出。

    try {
        new Long("xyz");
    } catch (NumberFormatException e) {
        log.error(e);
    }
    

    5. 优先捕获最具体的异常

    大多数 IDE 都可以帮助你实现这个最佳实践。当你尝试首先捕获较不具体的异常时,它们会报告无法访问的代码块。

    但问题在于,只有匹配异常的第一个 catch 块会被执行。 因此,如果首先捕获 IllegalArgumentException ,则永远不会到达应该处理更具体的 NumberFormatException 的 catch 块,因为它是 IllegalArgumentException 的子类。

    总是优先捕获最具体的异常类,并将不太具体的 catch 块添加到列表的末尾。

    你可以在下面的代码片断中看到这样一个 try-catch 语句的例子。 第一个 catch 块处理所有 NumberFormatException 异常,第二个处理所有非 NumberFormatException 异常的IllegalArgumentException 异常。

    public void catchMostSpecificExceptionFirst() {
        try {
            doSomething("A message");
        } catch (NumberFormatException e) {
            log.error(e);
        } catch (IllegalArgumentException e) {
            log.error(e)
        }
    }
    
    

    6. 不要捕获 Throwable 类

    Throwable 是所有异常和错误的超类。你可以在 catch 子句中使用它,但是你永远不应该这样做!

    如果在 catch 子句中使用 Throwable ,它不仅会捕获所有异常,也将捕获所有的错误。JVM 抛出错误,指出不应该由应用程序处理的严重问题。 典型的例子是 OutOfMemoryError 或者 StackOverflowError 。两者都是由应用程序控制之外的情况引起的,无法处理。

    所以,最好不要捕获 Throwable ,除非你确定自己处于一种特殊的情况下能够处理错误。

    public void doNotCatchThrowable() {
        try {
            // do something
        } catch (Throwable t) {
            // don't do this!
        }
    }
    

    7. 不要忽略异常

    很多时候,开发者很有自信不会抛出异常,因此写了一个catch块,但是没有做任何处理或者记录日志。

    public void doNotIgnoreExceptions() {
        try {
            // do something
        } catch (NumberFormatException e) {
            // this will never happen
        }
    }
    

    但现实是经常会出现无法预料的异常,或者无法确定这里的代码未来是不是会改动(删除了阻止异常抛出的代码),而此时由于异常被捕获,使得无法拿到足够的错误信息来定位问题。

    合理的做法是至少要记录异常的信息。

    public void logAnException() {
        try {
            // do something
        } catch (NumberFormatException e) {
            log.error("This should never happen: " + e);
        }
    }
    

    8. 不要记录并抛出异常

    这可能是本文中最常被忽略的最佳实践。可以发现很多代码甚至类库中都会有捕获异常、记录日志并再次抛出的逻辑。如下:

    try {
        new Long("xyz");
    } catch (NumberFormatException e) {
        log.error(e);
        throw e;
    }
    

    这个处理逻辑看着是合理的。但这经常会给同一个异常输出多条日志。如下:

    17:44:28,945 ERROR TestExceptionHandling:65 - java.lang.NumberFormatException: For input string: "xyz"
    Exception in thread "main" java.lang.NumberFormatException: For input string: "xyz"
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Long.parseLong(Long.java:589)
    at java.lang.Long.(Long.java:965)
    at com.stackify.example.TestExceptionHandling.logAndThrowException(TestExceptionHandling.java:63)
    at com.stackify.example.TestExceptionHandling.main(TestExceptionHandling.java:58)
    

    如上所示,后面的日志也没有附加更有用的信息。如果想要提供更加有用的信息,那么可以将异常包装为自定义异常。

    public void wrapException(String input) throws MyBusinessException {
        try {
            // do something
        } catch (NumberFormatException e) {
            throw new MyBusinessException("A message that describes the error.", e);
        }
    }
    

    因此,仅仅当想要处理异常时才去捕获,否则只需要在方法签名中声明让调用者去处理。

    9. 包装异常时不要抛弃原始的异常

    捕获标准异常并包装为自定义异常是一个很常见的做法。这样可以添加更为具体的异常信息并能够做针对的异常处理。
    在你这样做时,请确保将原始异常设置为原因(注:参考下方代码 NumberFormatException e 中的原始异常 e )。Exception 类提供了特殊的构造函数方法,它接受一个 Throwable 作为参数。否则,你将会丢失堆栈跟踪和原始异常的消息,这将会使分析导致异常的异常事件变得困难。

    public void wrapException(String input) throws MyBusinessException {
        try {
            // do something
        } catch (NumberFormatException e) {
            throw new MyBusinessException("A message that describes the error.", e);
        }
    }
    
    

    10. 不要使用异常控制程序的流程

    不应该使用异常控制应用的执行流程,例如,本应该使用if语句进行条件判断的情况下,你却使用异常处理,这是非常不好的习惯,会严重影响应用的性能。

    11. 使用标准异常

    如果使用内建的异常可以解决问题,就不要定义自己的异常。Java API 提供了上百种针对不同情况的异常类型,在开发中首先尽可能使用 Java API 提供的异常,如果标准的异常不能满足你的要求,这时候创建自己的定制异常。尽可能得使用标准异常有利于新加入的开发者看懂项目代码。

    12. 异常会影响性能

    异常处理的性能成本非常高,每个 Java 程序员在开发时都应牢记这句话。创建一个异常非常慢,抛出一个异常又会消耗1~5ms,当一个异常在应用的多个层级之间传递时,会拖累整个应用的性能。

    • 仅在异常情况下使用异常;
    • 在可恢复的异常情况下使用异常;

    尽管使用异常有利于 Java 开发,但是在应用中最好不要捕获太多的调用栈,因为在很多情况下都不需要打印调用栈就知道哪里出错了。因此,异常消息应该提供恰到好处的信息。

    13. 总结

    综上所述,当你抛出或捕获异常的时候,有很多不同的情况需要考虑,而且大部分事情都是为了改善代码的可读性或者 API 的可用性。

    异常不仅仅是一个错误控制机制,也是一个通信媒介。因此,为了和同事更好的合作,一个团队必须要制定出一个最佳实践和规则,只有这样,团队成员才能理解这些通用概念,同时在工作中使用它。

    异常处理-阿里巴巴Java开发手册

    1. 【强制】Java 类库中定义的可以通过预检查方式规避的RuntimeException异常不应该通过catch 的方式来处理,比如:NullPointerException,IndexOutOfBoundsException等等。 说明:无法通过预检查的异常除外,比如,在解析字符串形式的数字时,可能存在数字格式错误,不得不通过catch NumberFormatException来实现。 正例:if (obj != null) {…} 反例:try { obj.method(); } catch (NullPointerException e) {…}

    2. 【强制】异常不要用来做流程控制,条件控制。 说明:异常设计的初衷是解决程序运行中的各种意外情况,且异常的处理效率比条件判断方式要低很多。

    3. 【强制】catch时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的catch尽可能进行区分异常类型,再做对应的异常处理。 说明:对大段代码进行try-catch,使程序无法根据不同的异常做出正确的应激反应,也不利于定位问题,这是一种不负责任的表现。 正例:用户注册的场景中,如果用户输入非法字符,或用户名称已存在,或用户输入密码过于简单,在程序上作出分门别类的判断,并提示给用户。

    4. 【强制】捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容。

    5. 【强制】有try块放到了事务代码中,catch异常后,如果需要回滚事务,一定要注意手动回滚事务。

    6. 【强制】finally块必须对资源对象、流对象进行关闭,有异常也要做try-catch。 说明:如果JDK7及以上,可以使用try-with-resources方式。

    7. 【强制】不要在finally块中使用return。 说明:try块中的return语句执行成功后,并不马上返回,而是继续执行finally块中的语句,如果此处存在return语句,则在此直接返回,无情丢弃掉try块中的返回点。 反例:

    private int x = 0;
    public int checkReturn() {
        try {
            // x等于1,此处不返回
            return ++x;
        } finally {
            // 返回的结果是2
            return ++x;
        }
    }
    
    1. 【强制】捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类。 说明:如果预期对方抛的是绣球,实际接到的是铅球,就会产生意外情况。

    2. 【强制】在调用RPC、二方包、或动态生成类的相关方法时,捕捉异常必须使用Throwable类来进行拦截。 说明:通过反射机制来调用方法,如果找不到方法,抛出NoSuchMethodException。什么情况会抛出NoSuchMethodError呢?二方包在类冲突时,仲裁机制可能导致引入非预期的版本使类的方法签名不匹配,或者在字节码修改框架(比如:ASM)动态创建或修改类时,修改了相应的方法签名。这些情况,即使代码编译期是正确的,但在代码运行期时,会抛出NoSuchMethodError。

    3. 【推荐】方法的返回值可以为null,不强制返回空集合,或者空对象等,必须添加注释充分说明什么情况下会返回null值。 说明:本手册明确防止NPE是调用者的责任。即使被调用方法返回空集合或者空对象,对调用者来说,也并非高枕无忧,必须考虑到远程调用失败、序列化失败、运行时异常等场景返回null的情况。

    4. 【推荐】防止NPE,是程序员的基本修养,注意NPE产生的场景: 1) 返回类型为基本数据类型,return包装数据类型的对象时,自动拆箱有可能产生NPE。 反例:public int f() { return Integer对象}, 如果为null,自动解箱抛NPE。 2) 数据库的查询结果可能为null。 3) 集合里的元素即使isNotEmpty,取出的数据元素也可能为null。 4) 远程调用返回对象时,一律要求进行空指针判断,防止NPE。 5) 对于Session中获取的数据,建议进行NPE检查,避免空指针。 6) 级联调用obj.getA().getB().getC();一连串调用,易产生NPE。
      正例:使用JDK8的Optional类来防止NPE问题。

    5. 【推荐】定义时区分unchecked / checked 异常,避免直接抛出new RuntimeException(),更不允许抛出Exception或者Throwable,应使用有业务含义的自定义异常。推荐业界已定义过的自定义异常,如:DAOException / ServiceException等。

    6. 【参考】对于公司外的http/api开放接口必须使用“错误码”;而应用内部推荐异常抛出;跨应用间RPC调用优先考虑使用Result方式,封装isSuccess()方法、“错误码”、“错误简短信息”。 说明:关于RPC方法返回方式使用Result方式的理由: 1)使用抛异常返回方式,调用方如果没有捕获到就会产生运行时错误。 2)如果不加栈信息,只是new自定义异常,加入自己的理解的error message,对于调用端解决问题的帮助不会太多。如果加了栈信息,在频繁调用出错的情况下,数据序列化和传输的性能损耗也是问题。

    7. 【参考】避免出现重复的代码(Don’t Repeat Yourself),即DRY原则。 说明:随意复制和粘贴代码,必然会导致代码的重复,在以后需要修改时,需要修改所有的副本,容易遗漏。必要时抽取共性方法,或者抽象公共类,甚至是组件化。 正例:一个类中有多个public方法,都需要进行数行相同的参数校验操作,这个时候请抽取:
      private boolean checkParam(DTO dto) {…}

    展开全文
  • Java中异常的限制

    千次阅读 2016-09-19 22:09:40
    Java中,发现错误的最理想时机是在编译阶段,即运行程序之前。但是,在编译期间,并不能保证所有的错误都被发现,余下的...本文略过了异常处理流程等基本知识,直接讨论Java中异常的限制。  异常的限制,这是指:当
  • JAVA中异常处理机制 任何程序都追求正确有效的运行,除了保证我们代码尽可能的少出错之外,我们还要考虑如何有效的处理异常, 一个良好的异常框架对于系统来说是至关重要的。最近在给公司写采集框架的时候系统的...
  • Java异常

    2017-10-24 20:50:06
    Java异常 Java异常 概述 基本异常 捕获异常 1 try块 2 异常处理程序 创建自定义的异常 异常说明 捕获所有异常 1 栈轨迹 2 重新抛出异常 3 异常链 Java标准异常 使用finally进行清理 异常丢失 异常的限制 构造器 异常...
  • 1. java.lang.nullpointerexception 这个异常大家肯定都经常遇到,异常的解释是"程序遇上了空指针",简单地说就是调用了未经初始化的对象或者是不存在的对象,这个错误经常出现在创建图片,调用数组这些...
  • Java中异常机制以及常见异常

    千次阅读 2012-07-09 10:31:18
    本文重在Java中异常机制的一些概念。写本文的目的在于方便我很长时间后若是忘了这些东西可以通过这篇文章迅速回忆起来。 1. 异常机制 1.1 异常机制是指当程序出现错误后,程序如何处理。具体来说,异常机制提供...
  • java异常

    热门讨论 2014-08-30 20:23:13
    总结java中对于异常的处理
  • java(3)-深入理解java异常处理机制

    万次阅读 多人点赞 2011-01-20 18:44:00
    不过,我亲自体验的“教训”告诉我,这个东西可不是想象的那么简单、听话。不信?那你看看下面的代码,“猜猜”它执行后的结果会是什么?不要往后看答案、也不许执行代码看真正答案哦。如果你的答案是正确,那么这...
  • Java中异常打印输出的常见方法整理

    万次阅读 2018-05-05 11:08:28
    前言Java异常是在Java应用的警报器,在出现异常的情况下,可以帮助我们程序猿们快速定位问题的类型以及位置。但是一般在我们的项目,由于经验阅历等多方面的原因,依然有若干的童鞋在代码没有正确的使用异常...
  • 深入理解Java中异常体系

    万次阅读 多人点赞 2018-07-12 11:03:35
    任何程序都追求正确有效的运行,除了保证我们代码尽可能的少出错之外,我们还要考虑如何有效的处理异常,一个良好的异常框架对于系统来说是至关重要的。最近在给公司写采集框架的时候系统的了解一边,收获颇多,特此...
  • JAVA中异常重点总结

    千次阅读 2018-07-24 20:12:48
    异常: input.hasNextInt() (判断用户通过控制台输入的是不是整数) (Returns true if the next token in this scanner's input can be interpreted as an int value in the default radix using the nextInt ...
  • 第1关:Java 异常处理机制 任务描述 本关任务:完成异常类选择题。 为了完成本关任务,你需要掌握: 1.什么是异常; 2.如何使用异常。 什么是异常 异常:程序在运行过程产生的不正常情况。 程序在运行的时候,...
  • Java自定义异常

    2016-07-22 10:01:26
    前言 在理想的状态下,用户输入数据的格式永远是...在Java中,所有的异常对象都派生于Throwable类,Throwable又分为Error类和Exception类。Error类是描述Java运行时系统的内部错误和资源耗尽错误,遇到这个类型的错误
  • Java中异常处理

    千次阅读 2012-12-10 16:00:04
    对于初学JAVA的菜鸟,往往对异常处理不是很清楚。本文比较全面浅显的介绍下异常处理。 1.异常处理的优势 异常处理最根本的优势是将检测错误(由被调用的方法...所有的Java异常类都直接或间接地继承自Throwable。可
  •  这个异常大家肯定都经常遇到,异常的解释是"程序遇上了空指针",简单地说就是调用了未经初始化的对象或者是不存在的对象,这个错误经常出现在创建图片,调用数组这些操作,比如图片未经初始化,或者图片创建时的...
  • java常见异常注解

    千次阅读 2018-04-26 19:38:15
    1、JAVA异常 异常指不期而至的各种状况,如:...因而,Java异常都是对象,是Throwable子类的实例,描述了出现在一段编码的 错误条件。当条件生成时,错误将引发异常。 在Java,所有的异常都有一个共同的祖先Th...
  • Java常见异常笔记

    2017-02-19 23:04:38
    有关类的异常: java.lang.ClassNotFoundException:找不到类异常,当应用试图根据...java.lang.ClassNotFoundException:指定的类不存在 这里主要考虑一下类的名称和路径是否正确即可。 java.lang.ClassCastException
  • 从异常的概念,分类,使用方法,注意事项和设计等方面全面地介绍了Java异常。 具体代码在我的GitHub可以找到 https://github.com/h2pl/MyTech 喜欢的话麻烦点下星哈 文章首发于我的个人博客: ...
  • JAVA异常整理

    千次阅读 2017-02-17 15:13:33
    一、Java 常见异常种类 Java Exception: 1、Error  2、Runtime Exception 运行...Java异常类层次结构图:  图1 Java异常类层次结构图 在 Java ,所有的异常都有一个共同的祖先 Throwable(可抛出)。
  • 都是Throwable的子类:1.Exception(异常):是程序本身可以处理的异常。 2.Error(错误):是程序无法处理的错误。这些错误表示故障发生于虚拟机自身、或者发生在虚拟机试图执行应用时,一般不需要程序处理。 3....
  • Java异常打印输出常见方法的分析

    万次阅读 多人点赞 2017-04-30 22:10:35
    Java异常是在Java应用的警报器,在出现异常的情况下,可以帮助我们程序猿们快速定位问题的类型以及位置。但是一般在我们的项目,由于经验阅历等多方面的原因,依然有若干的童鞋在代码没有正确的使用异常打印...
  • Java异常日志

    千次阅读 2019-01-31 16:46:42
    1、【强制】Java 类库定义的可以通过预检查方式规避的RuntimeException异常不应该通过catch 的方式来处理,比如:NullPointerException,IndexOutOfBoundsException等等。 说明:无法通过预检查的异常除外,比如,...
  • java中常见的异常类型

    千次阅读 2018-09-19 23:36:59
    1. java.lang.nullpointerexception 这个异常大家肯定都经常遇到,异常的解释是&quot;程序遇上了空指针&quot;,简单地说就是调用了未经初始化的对象或者是不存在的对象,这个错误经常出现在创建图片,调用...
  • Java 异常处理

    千次阅读 2017-10-07 11:18:08
    Java异常的处理机制 抛出异常 捕获异常 异常处理的基本语法 try-catch throw throws finally 问题扩展面试题 异常链 自定义异常 总结异常的概念异常是程序的一些错误,但并不是所有的错误都是异常,并且错误有时候...
  • Java 异常

    2014-07-15 21:49:26
    Java核心知识的miansh
  • Java链式异常

    千次阅读 2016-07-08 16:31:21
    Java链式异常 jdk 1.4 开始 通过链式异常,可以为异常关联另一个异常。第二个一场描述第一个异常的原因(描述当前异常原因的异常) eg:假设某个方法由于试图除零而抛出arithmetiException异常,导致...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 127,845
精华内容 51,138
关键字:

对于java中异常描述正确的是

java 订阅