java异常处理方法是什么

71次阅读
没有评论

共计 5576 个字符,预计需要花费 14 分钟才能阅读完成。

这篇文章主要介绍“java 异常处理方法是什么”,在日常操作中,相信很多人在 java 异常处理方法是什么问题上存在疑惑,丸趣 TV 小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”java 异常处理方法是什么”的疑惑有所帮助!接下来,请跟着丸趣 TV 小编一起来学习吧!

异常简介

先上个图,看一下常见的几个异常类型。

所有的异常都来自于 Throwable。Throwable 有两个子类,Error 和 Exception。

Error 通常表示的是严重错误,这些错误是不建议被 catch 的。

注意这里有一个例外,比如 ThreadDeath 也是继承自 Error,但是它表示的是线程的死亡,虽然不是严重的异常,但是因为应用程序通常不会对这种异常进行 catch,所以也归类到 Error 中。

Exception 表示的是应用程序希望 catch 住的异常。

在 Exception 中有一个很特别的异常叫做 RuntimeException。RuntimeException 叫做运行时异常,是不需要被显示 catch 住的,所以也叫做 unchecked Exception。而其他非 RuntimeException 的 Exception 则需要显示 try catch, 所以也叫做 checked Exception。

不要忽略 checked exceptions

我们知道 checked exceptions 是一定要被捕获的异常,我们在捕获异常之后通常有两种处理方式。

第一种就是按照业务逻辑处理异常,第二种就是本身并不处理异常,但是将异常再次抛出,由上层代码来处理。

如果捕获了,但是不处理,那么就是忽略 checked exceptions。

接下来我们来考虑一下 java 中线程的中断异常。

java 中有三个非常相似的方法 interrupt,interrupted 和 isInterrupted。

isInterrupted() 只会判断是否被中断,而不会清除中断状态。

interrupted() 是一个类方法,调用 isInterrupted(true) 判断的是当前线程是否被中断。并且会清除中断状态。

前面两个是判断是否中断的方法,而 interrupt()就是真正触发中断的方法。

它的工作要点有下面 4 点:

如果当前线程实例在调用 Object 类的 wait(),wait(long)或 wait(long,int)方法或 join(),join(long),join(long,int)方法,或者在该实例中调用了 Thread.sleep(long)或 Thread.sleep(long,int)方法,并且正在阻塞状态中时,则其中断状态将被清除,并将收到 InterruptedException。

如果此线程在 InterruptibleChannel 上的 I / O 操作中处于被阻塞状态,则该 channel 将被关闭,该线程的中断状态将被设置为 true,并且该线程将收到 java.nio.channels.ClosedByInterruptException 异常。

如果此线程在 java.nio.channels.Selector 中处于被被阻塞状态,则将设置该线程的中断状态为 true,并且它将立即从 select 操作中返回。

如果上面的情况都不成立,则设置中断状态为 true。

看下面的例子:

public void wrongInterrupted(){
 try{ Thread.sleep(1000);
 } catch (InterruptedException e) { e.printStackTrace();
 }
 }

上面代码中我们捕获了一个 InterruptedException,但是我们仅仅是打印出了异常信息,并没有做任何操作。这样程序的表现和没有发送一异常一样,很明显是有问题的。

根据上面的介绍,我们知道,interrupted() 方法会清除中断状态,所以,如果我们自身处理不了异常的情况下,需要重新调用 Thread.currentThread().interrupt() 重新抛出中断,由上层代码负责处理,如下所示。

public void correctInterrupted(){
 try{ Thread.sleep(1000);
 } catch (InterruptedException e) { Thread.currentThread().interrupt();
 }
 }

不要在异常中暴露敏感信息

遇到异常的时候,通常我们需要进行一定程度的日志输出,从而来定位异常。但是我们在做日志输出的时候,一定要注意不要暴露敏感信息。

下表可以看到异常信息可能会暴露的敏感信息:

除了敏感信息之外,我们还要做好日志信息的安全保护。

在处理捕获的异常时,需要恢复对象的初始状态

如果我们在处理异常的时候,修改了对象中某些字段的状态,在捕获异常的时候需要怎么处理呢?

private int age=30;
 public void wrongRestore(){
 try{
 age=20;
 throw new IllegalStateException( custom exception! 
 }catch (IllegalStateException e){
 System.out.println( we do nothing 
 }
 }

上面的例子中,我们将 age 重置为 20,然后抛出了异常。虽然抛出了异常,但是我们并没有重置 age,最后导致 age 最终被修改了。

整个 restore 的逻辑没有处理完毕,但是我们部分修改了对象的数据,这是很危险的。

实际上,我们需要一个重置:

public void rightRestore(){
 try{
 age=20;
 throw new IllegalStateException( custom exception! 
 }catch (IllegalStateException e){
 System.out.println( we do nothing 
 age=30;
 }
 }

不要手动完成 finally block

我们在使用 try-finally 和 try-catch-finally 语句时,一定不要在 finally block 中使用 return, break, continue 或者 throw 语句。

为什么呢?

根据 Java Language Specification(JLS)的说明,finally block 一定会被执行,不管 try 语句中是否抛出异常。

在 try-finally 和 try-catch-finally 语句中,如果 try 语句中抛出了异常 R,然后 finally block 被执行,这时候有两种情况:

如果 finally block 正常执行,那么 try 语句被终止的原因是异常 R。

如果在 finally block 中抛出了异常 S,那么 try 语句被终止的原因将会变成 S。

我们举个例子:

public class FinallyUsage { public boolean wrongFinally(){
 try{
 throw new IllegalStateException( my exception! 
 }finally {
 System.out.println( Code comes to here! 
 return true;
 }
 }
 public boolean rightFinally(){
 try{
 throw new IllegalStateException( my exception! 
 }finally {
 System.out.println( Code comes to here! 
 }
 }
 public static void main(String[] args) { FinallyUsage finallyUsage=new FinallyUsage();
 finallyUsage.wrongFinally();
 finallyUsage.rightFinally();
 }
}

上面的例子中,我们定义了两个方法,一个方法中我们在 finally 中直接 return, 另一方法中,我们让 finally 正常执行完毕。

最终,我们可以看到 wrongFinally 将异常隐藏了,而 rightFinally 保留了 try 的异常。

同样的,如果我们在 finally block 中抛出了异常,我们一定要记得对其进行捕获,否则将会隐藏 try block 中的异常信息。

不要捕获 NullPointerException 和它的父类异常

通常来说 NullPointerException 表示程序代码有逻辑错误,是需要程序员来进行代码逻辑修改,从而进行修复的。

比如说加上一个 null check。

不捕获 NullPointerException 的原因有三个。

使用 null check 的开销要远远小于异常捕获的开销。

如果在 try block 中有多个可能抛出 NullPointerException 的语句,我们很难定位到具体的错误语句。

最后,如果发生了 NullPointerException,程序基本上不可能正常运行或者恢复,所以我们需要提前进行 null check 的判断。

同样的,程序也不要对 NullPointerException 的父类 RuntimeException, Exception, or Throwable 进行捕捉。

不要 throw RuntimeException, Exception, or Throwable

我们抛出异常主要是为了能够找到准确的处理异常的方法,如果直接抛出 RuntimeException, Exception, 或者 Throwable 就会导致程序无法准确处理特定的异常。

通常来说我们需要自定义 RuntimeException, Exception, 或者 Throwable 的子类,通过具体的子类来区分具体的异常类型。

不要抛出未声明的 checked Exception

一般来说 checked Exception 是需要显示 catch 住,或者在调用方法上使用 throws 做申明的。

但是我们可以通过某些手段来绕过这种限制,从而在使用 checked Exception 的时候不需要遵守上述规则。

当然这样做是需要避免的。我们看一个例子:

private static Throwable throwable;
 private ThrowException() throws Throwable {
 throw throwable;
 }
 public static synchronized void undeclaredThrow(Throwable throwable) {
 ThrowException.throwable = throwable;
 try { ThrowException.class.newInstance();
 } catch (InstantiationException e) { } catch (IllegalAccessException e) { } finally {
 ThrowException.throwable = null;
 }
 }

上面的例子中,我们定义了一个 ThrowException 的 private 构造函数,这个构造函数会 throw 一个 throwable, 这个 throwable 是从方法传入的。

在 undeclaredThrow 方法中,我们调用了 ThrowException.class.newInstance() 实例化一个 ThrowException 实例,因为需要调用构造函数,所以会抛出传入的 throwable。

因为 Exception 是 throwable 的子类,如果我们在调用的时候传入一个 checked Exception,很明显,我们的代码并没有对其进行捕获:

public static void main(String[] args) {
 ThrowException.undeclaredThrow( new Exception( Any checked exception));
 }

怎么解决这个问题呢?换个思路,我们可以使用 Constructor.newInstance() 来替代 class.newInstance()。

try {
 Constructor constructor =
 ThrowException.class.getConstructor(new Class ? [0]);
 constructor.newInstance();
 } catch (InstantiationException e) { } catch (InvocationTargetException e) {
 System.out.println( catch exception! 
 } catch (NoSuchMethodException e) { } catch (IllegalAccessException e) { } finally {
 ThrowException.throwable = null;
 }

上面的例子,我们使用 Constructor 的 newInstance 方法来创建对象的实例。和 class.newInstance 不同的是,这个方法会抛出 InvocationTargetException 异常,并且把所有的异常都封装进去。

所以,这次我们获得了一个 checked Exception。

到此,关于“java 异常处理方法是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注丸趣 TV 网站,丸趣 TV 小编会继续努力为大家带来更多实用的文章!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-17发表,共计5576字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)