简介
异样是java程序员无奈防止的一个话题,咱们会有JVM本人的异样也有应用程序的异样,对于不同的异样,咱们的解决准则是不是一样的呢?
一起来看看吧。
异样简介
先上个图,看一下常见的几个异样类型。
所有的异样都来自于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。
本文的代码:
learn-java-base-9-to-20/tree/master/security
本文已收录于 http://www.flydean.com/java-security-code-line-exception/最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!
欢送关注我的公众号:「程序那些事」,懂技术,更懂你!