??xml version="1.0" encoding="utf-8" standalone="yes"?> 你觉得自己是一?/span>Java专家吗?是否肯定自己已经全面掌握?/span>Java的异常处理机Ӟ在下面这D代码中Q你能够q速找出异常处理的六个问题吗? ava中的transientQvolatile和strictfp关键?#160;
1 OutputStreamWriter out = ...
2 java.sql.Connection conn = ...
3 try { // ?/span>
4 Statement stat = conn.createStatement();
5 ResultSet rs = stat.executeQuery(
6 "select uid, name from user");
7 while (rs.next())
8 {
9 out.println("IDQ?/span>" + rs.getString("uid") // ?/span>
10 "Q姓名:" + rs.getString("name"));
11 }
12 conn.close(); // ?/span>
13 out.close();
14 }
15 catch(Exception ex) // ?/span>
16 {
17 ex.printStackTrace(); //?/span>Q?/span>?/span>
18 }
作ؓ一?/span>JavaE序员,你至应该能够找Z个问题。但是,如果你不能找出全部六个问题,Ll阅L文?/span>
本文讨论的不?/span>Java异常处理的一般性原则,因ؓq些原则已经被大多数人熟知。我们要做的是分析各U可UCؓ“反例”Q?/span>anti-patternQ的q背优秀~码规范的常见坏习惯Q帮助读者熟悉这些典型的反面例子Q从而能够在实际工作中敏锐地察觉和避免这些问题?/span>
反例之一Q丢弃异?/span>
代码Q?/span>15?/span>-18行?/span>
q段代码捕获了异常却不作M处理Q可以算得上Java~程中的杀手。从问题出现的频J程度和害E度来看Q它也许可以?/span>C/C++E序的一个恶名远播的问题相提q论??不检查缓冲区是否已满。如果你看到了这U丢弃(而不是抛出)异常的情况,可以癑ֈ之九十九地肯定代码存在问题(在极数情况下,q段代码有存在的理由Q但最好加上完整的注释Q以免引起别解)?/span>
q段代码的错误在于,异常Q几乎)L意味着某些事情不对劲了Q或者说臛_发生了某些不d的事情,我们不应该对E序发出的求救信号保持沉默和无动于衷。调用一?/span>printStackTrace不?/span>“处理异常”。不错,调用printStackTrace对调试程序有帮助Q但E序调试阶段l束之后Q?/span>printStackTrace׃应再在异常处理模块中担负主要责Q了?/span>
丢弃异常的情形非常普遍。打开JDK?/span>ThreadDeathcȝ文档Q可以看C面这D说明:“特别圎ͼ虽然出现ThreadDeath是一U?/span>‘正常的情?/span>’Q但ThreadDeathcLError而不?/span>Exception的子c,因ؓ许多应用会捕h有的Exception然后丢弃它不再理睬?/span>”q段话的意思是Q虽?/span>ThreadDeath代表的是一U普通的问题Q但鉴于许多应用会试图捕h有异常然后不予以适当的处理,所?/span>JDK?/span>ThreadDeath定义成了Error的子c,因ؓErrorcM表的是一般的应用不应该去捕获的严重问题。可见,丢弃异常q一坏习惯是如此常见Q它甚至已经影响CJava本n的设计?/span>
那么Q应该怎样Ҏ呢?主要有四个选择Q?/span>
1、处理异常。针对该异常采取一些行动,例如修正问题、提醒某个h或进行其他一些处理,要根据具体的情Ş定应该采取的动作。再ơ说明,调用printStackTrace不上已l?/span>“处理好了异常”?/span>
2、重新抛出异常。处理异常的代码在分析异怹后,认ؓ自己不能处理它,重新抛出异常也不׃ؓ一U选择?/span>
3、把该异常{换成另一U异常。大多数情况下,q是指把一个低U的异常转换成应用的异常(其含义更Ҏ被用户了解的异常Q?/span>
4、不要捕获异常?/span>
l论一Q既然捕获了异常Q就要对它进行适当的处理。不要捕获异怹后又把它丢弃Q不予理睬?/span>
反例之二Q不指定具体的异?/span>
代码Q?/span>15行?/span>
许多时候h们会被这样一U?/span>“妙?/span>”x吸引Q用一?/span>catch语句捕获所有的异常。最常见的情形就是?/span>catch(Exception ex)语句。但实际上,在绝大多数情况下Q这U做法不值得提倡。ؓ什么呢Q?/span>
要理解其原因Q我们必d一?/span>catch语句的用途?/span>catch语句表示我们预期会出现某U异常,而且希望能够处理该异常。异常类的作用就是告?/span>Java~译器我们想要处理的是哪一U异常。由于绝大多数异帔R直接或间接从java.lang.ExceptionzQ?/span>catch(Exception ex)q当于说我们想要处理几乎所有的异常?/span>
再来看看前面的代码例子。我们真正想要捕L异常是什么呢Q最明显的一个是SQLExceptionQ这?/span>JDBC操作中常见的异常。另一个可能的异常?/span>IOExceptionQ因为它要操?/span>OutputStreamWriter。显Ӟ在同一?/span>catch块中处理q两U截然不同的异常是不合适的。如果用两个catch块分别捕?/span>SQLException?/span>IOExceptionp好多了。这是_catch语句应当量指定具体的异常类型,而不应该指定늛范围太广?/span>ExceptioncR?/span>
另一斚wQ除了这两个特定的异常,q有其他许多异常也可能出现。例如,如果׃某种原因Q?/span>executeQueryq回?/span>nullQ该怎么办?{案是让它们l箋抛出Q即不必捕获也不必处理。实际上Q我们不能也不应该去捕获可能出现的所有异常,E序的其他地方还有捕获异常的Z??直至最后由JVM处理?/span>
]]>
]]>
如果用transient声明一个实例变量,当对象存储时Q它的g需要维持。例如:
q里Q如果Tcȝ一个对象写入一个持久的存储区域Qa的内容不被保存,但b的将被保存?#160;
volatile修饰W告诉编译器被volatile修饰的变量可以被E序的其他部分改变。在多线E程序中Q有时两个或更多的线E共享一个相同的实例变量。考虑效率问题Q每个线E可以自׃存该׃n变量的私有拷贝。实际的变量副本在不同的时候更斎ͼ如当q入synchronizedҎ时?#160;
用strictfp修饰cLҎQ可以确保Q点运(以及所有切断)正如早期的Java版本那样准确。切断只影响某些操作的指数。当一个类被strictfp修饰Q所有的Ҏ自动被strictfp修饰?#160;
strictfp的意思是FP-strictQ也是说精Q点的意思。在Java虚拟行Q点运时Q如果没有指定strictfp关键字时QJava的编译器以及q行环境在对点q算的表辑ּ是采取一U近g我行我素的行为来完成q些操作Q以致于得到的结果往往无法令你满意。而一旦用了strictfp来声明一个类、接口或者方法时Q那么所声明的范围内Java的编译器以及q行环境会完全依照Q点规范IEEE-754来执行。因此如果你惌你的点q算更加_Q而且不会因ؓ不同的硬件^台所执行的结果不一致的话,那就L关键字strictfp?#160;
你可以将一个类、接口以及方法声明ؓstrictfpQ但是不允许Ҏ口中的方法以及构造函数声明strictfp关键字,例如下面的代码:
1. 合法的用关键字strictfp
public strictfp class FpDemo1 {
strictfp void f() {}
}
2. 错误的用方?/p>