快精灵印艺坊 您身边的文印专家
广州名片 深圳名片 会员卡 贵宾卡 印刷 设计教程
产品展示 在线订购 会员中心 产品模板 设计指南 在线编辑
 首页 名片设计   CorelDRAW   Illustrator   AuotoCAD   Painter   其他软件   Photoshop   Fireworks   Flash  

 » 彩色名片
 » PVC卡
 » 彩色磁性卡
 » 彩页/画册
 » 个性印务
 » 彩色不干胶
 » 明信片
   » 明信片
   » 彩色书签
   » 门挂
 » 其他产品与服务
   » 创业锦囊
   » 办公用品
     » 信封、信纸
     » 便签纸、斜面纸砖
     » 无碳复印纸
   » 海报
   » 大篇幅印刷
     » KT板
     » 海报
     » 横幅

突破Java非常处理规则

你可以去读目前 JavaWorld文章 ?C “Java Tip 134: When Catching Exception, Don’t Cast Your Net Too Wide”。这篇文章警告了捕捉java.lang.Exception和java.lang.Throable是不好的。捕捉你能指定的非常对于代码的可维护性是十分重要的。然而这个规则依靠于特别的环境。假如你不打算你的程序崩溃并且保留你的数据结构的安全非常,那么你必须捕捉被抛出的真正的非常。

举个例子,想象你有一个加载了这个接口的服务器应用:



public interface IFoo
{
 /**
 * This method can\\\'t throw any checked exceptions...or can it?
 */
 void bar ();
} // End of interface



  对于给出参数的理由是让我们通知你这样的服务在什么地方,并且不同的IFoo实现能够从外部资源加载上。你写如下代码:



try
{
 IFoo foo = ... // get an IFoo implementation
 foo.bar ();
}
catch (RuntimeException ioe)
{
 // Handle \\\'ioe\\\' ...
}
catch (Error e)
{
 // Handle or re-throw \\\'e\\\' ...
}



  并且你在这个里处理了所有可能的非常。你不需要在这里加上任何捕捉java.io.IOException的非常,因为IFoo实现没有从IFoo.bar()中抛出它,对吗?(事实上,假如你加上了捕捉java.io.IOException非常块,编译器可能会把它作为不可到达的非常而丢弃)

  错误。在我写的EvilFoo类中bar()方式证实了将抛出你传递给类构造器的任何非常:



public void bar ()
{
 EvilThrow.throwThrowable (m_throwthis);
}



  运行Main方式:



public class Main
{
 public static void main (final String[] args)
 {
  // This try/catch block appears to intercept all exceptions that
  // IFoo.bar() can throw; however, this is not true
  try
  {
   IFoo foo = new EvilFoo (new java.io.IOException ("SURPRISE!"));
   foo.bar ();
  }
  catch (RuntimeException ioe)
  {
   // Ignore ioe
  }
  catch (Error e)
  {
   // Ignore e
  }
 }
} // End of class



  你将看到从bar()方式抛出的java.io.IOException非常实例并且没有任何捕捉块:



>java -cp classes Main
Exception in thread "main" java.io.IOException: SURPRISE!
at Main.main(Main.java:23)



  在这里发生了什么?

  主要的观察是通常针对检测非常的Java规则仅仅在编译的时候被执行。在运行的时候,一个JVM不能保证被一个方式抛出的非常是否和在这个方式中声明的抛出非常相匹配。因为调用方式的职责是捕捉和处理所有从调用方式抛出的非常。任何没有被调用方式声明的非常将不予理睬并且拒绝调用栈。

  假如正常行为是编译器执行,那么我怎么创建EvilFoo的?至少有两个方式可以去创建抛出没有声明的非常的Java方式:

   Thread.stop(Throwable)在一些时候不被赞成使用,但是它仍旧被使用并且传递一个Throwable给被调用的Thread。

   分别编译:你能在编译EvilFoo时候不去编译真正声明bar()方式抛出检测非常的IFoo临时版本。

  我用后一种选择:我编译开始定义的EvilThrow类:

public abstract class EvilThrow
{
 public static void throwThrowable (Throwable throwable)
 throws Throwable
 {
  throw throwable;
 }
}

  接下来,我用Byte Code Engineering Library(BCEL)的JasminVisitor分解结果,在汇编代码中删除throwThrowable()方式Throwable的声明,并且用Jasmin assembler 编译新的版本。

  假如你编写捕捉非常的构造器,那么它应该总是捕捉java.lang.Throwable而不仅仅只捕捉java.lang.Exception。这个规则适合你开发治理运行时的应用程序和必须执行可能包含错误甚至恶意代码的外部组件。你要确保捕捉Throwable并且过滤掉错误信息。

  下面示例说明了假如你没有遵循这个建议将发生什么。

Example: Breaking SwingUtilities.invokeAndWait()

  javax.swing.SwingUtilities.invokeAndWait()是在AWT上执行一个线程的有用方式。当一个应用程序线程必须更新图形用户接口并且听从所有Swing线程规则的时候这个方式将被调用。一个没有捕捉Runnable.run()抛出的非常将被捕捉并且被封装在一个InvocationTragetException中重新抛出。

  Sun的J2SE1.4.1假设这样一个未捕捉的非常仅仅是java.lang.Exception的子类。这里是一个SwingUtilities.invokeAndWait()调用java.awt.event.InvocationEvent的一个分析:

public void dispatch() {
 if (catchExceptions) {
  try {
   runnable.run();
  }
  catch (Exception e) {
   exception = e;
  }
 }
 else {
  runnable.run();
 }

 if (notifier != null) {
  synchronized (notifier) {
   notifier.notifyAll();
  }
 }
}

  这段代码的问题是假如runnable.run()抛出一个Throwable,捕捉块又没有并且notifier.notifyAll()从来不会被执行。然后调用应用线程将等待在java.awt.EventQueue.invokeAndWait()里的一个非公共锁对象(lock.wait()将从未执行):

public static void invokeAndWait(Runnable runnable)
throws InterruptedException, InvocationTargetException {

 class AWTInvocationLock {}
 Object lock = new AWTInvocationLock();

 InvocationEvent event =new InvocationEvent(Toolkit.getDefaultToolkit(), runnable, lock,
true);

 synchronized (lock) {
  Toolkit.getEventQueue().postEvent(event);
  lock.wait();
 }

 Exception eventException = event.getException();
 if (eventException != null) {
  throw new InvocationTargetException(eventException);
 }
}

  让EvilFoo实现Runnable接口:

public void run ()
{
 bar ();
}

  然后,在Main中调用它:

SwingUtilities.invokeAndWait (new EvilFoo (new Throwable ("SURPRISE!")));

  正如你看到的,未受信任代码使你的代码进入你没有预备处理的执行路径中的非常被保护起来。





返回类别: 教程
上一教程: Java性能探寻
下一教程: 自己动手编写Eclipse扩展点

您可以阅读与"突破Java非常处理规则"相关的教程:
· Java非常处理--不要忽略或遮掩非常
· 关于java非常处理机制的深入理解
· Java非常处理--尽量不要从try区段中返回(return)
· Java非常处理的陋习展播
· java关于日期的运算等处理方式
    微笑服务 优质保证 索取样品