《
Robust Java
》
作者:
Stephen Stelting
本書詳細的講述了異常的各個方面的知識,兼帶介紹了測試和調試的基本方法。是了解
Java
異常的極佳教材。
異常概念
從某種意義上講,異常就是這樣一種消息:它傳送一些系統問題、故障及未按規定執行的動作的相關信息。異常包含信息,以將信息從應用程序的一部分發送到另一部分。
異常本身表示消息,指發送者傳給接受者的數據
“
負荷
”
。首先,異?;陬惖念愋蛠韨鬏斢杏眯畔?。很多情況下,只需異常對象的類即能識別故障本因并更正問題。其次,異常還帶有可能有用的數據(如屬性)。異常消息或故障本質原因等數據屬于這個范疇。
在處理異常時,消息必須有接受者;否則將無法處理產生異常的底層問題。
異常類層次結構
所有異常有一個共同祖先
Throwable
(可拋出)。
Throwable
指定代碼中可用異常傳播機制通過
Java
應用程序傳輸的任何問題的共性。
Throwable
有兩個重要的子類:
Exception
(異常)和
Error
(錯誤),二者都是
Java
異常處理的重要子類,各自都包含大量子類。
“
異常
”
是應用程序中可能的可預測、可恢復的問題。
Java API
文檔記錄給出的定義是:
“
合理應用程序可能需要捕獲的情況。
”
一般地,大多數異常表示輕度到中度的問題。
異常一般在特定環境下產生的,通常出現于代碼的特定方法或操作中。
“
錯誤
”
表示運行應用程序中的較嚴重問題。
Java API
文檔記錄給出的定義是
:“
是
Throwable
的一個子類,代表嚴重問題,合理應用程序不應試圖捕獲它。大多數此類錯誤屬反常情況。
”
大多數錯誤與代碼編寫者執行的操作無關,而表示代碼運行時
JVM
(
Java
虛擬機)出現的問題。例如,當
JVM
不再有繼續執行操作所需的內存資源時,將出現
OutOfMemoryError
。
Exception
有一個重要的子類
RuntimeException
。
RuntimeException
及其子類表示
“JVM
常用操作
”
引發的錯誤。例如,若試圖使用空值
對象引用、除數為零,或數組越界,則將分別引發運行時異常(
NullPointerException
、
ArithmeticException
)和
ArrayIndexOutOfBoundException
。
異常的處理或聲明選項
在處理潛在故障時,有兩個選項。在調用可能引發異常的方法時,可以捕獲異常,也可以聲明該方法拋出異常。也就是說的
“
處理(
handle
)或聲明(
declare
)
”
規則。
處理異常:
try
、
catch
和
finally
若要捕獲異常,則必須在代碼中添加異常處理器塊。
import java.io.*;
public class EchoInputTryCatchFinally{
??? public static void main(String [] args) {
??? ??? System.out.println("Enter text to echo");
??? ??? InputStreamReader isr = new InputStreamReader(System.in);
??? ??? BufferedReader inputReader = new BufferedReader(isr);
??? ??? try{
??? ??? ??? String inputLine = inputReader.readLine();
??? ??? ??? System.out.println("READ: "+inputLine);
??? ??? }
??? ??? catch(IOException exc){
??? ??? ??? System.out.println("Exceptin encountered: "+exc);
??? ??? }
??? ??? finally{
??? ??? ??? System.out.println("My job here is done.");
??? ??? }
??? }
}
1.try
塊
在將一個或多個語句放入
try
塊時,則表示這些語句可能拋出異常。編譯器知道可能要發生異常,于是用一個特殊結構評估塊內所有語句。
2.catch
塊
當
問題出現時,一種選擇是定義代碼塊來處理問題,
catch
的目的便在與此。
catch
塊是
try
塊所產生異常的接受者。基本原理為:一旦生成異常,則
try
塊的執行中止,
JVM
將查找相應的
catch
塊。在
try-catch
結構中,可能有多個
catch
塊;此時,異常將交由第一個匹配的
catch
塊
處理。
3.finally
塊
還可以定義這樣
一個代碼塊,無論試圖運行
try
塊代碼的結果如何,該塊一定運行。
finally
塊作用便在于此。在常見的所有環境中,
finally
塊都將運行。無論
try
塊是否運行完,無論是否產生異常,也不論異常是否在
catch
塊得到處理,
finally
塊都將運行。
???
try-catch-finally
的規則
·
必須在
try
之后添加
catch
塊??扇我饨硬唤?/span>
finally
塊。
·
必須遵守塊順序:如代碼同時使用
catch
和
finally
塊,則必須將
catch
塊放在
try
塊之后。
· try
塊與相應的
catch
或
finally
之間可能不存在語句。
· catch
塊與特定異常類的類型相關。
·
一個
try
塊可能有多個
catch
塊。若如此,將執行第一個匹配塊。有一個經驗法則:要按從最具體到最一般的順序組織處理塊。
·
除下列情況,總將執行
finally
作為結束:
??? ??? a.JVM
過早中止(調用
System.exit(int)
)
??? ??? b.
在
finally
塊中拋出一個未處理的異常。
??? ??? c.
計算機斷電、失火,或遭遇病毒攻擊。
·
可嵌套
try-catch-finally
結構
·
在
try-catch-finally
結構中,可重新拋出異常。
聲明異常
若要聲明異常,則必須將其添加到方法簽名塊的結束位置,即輸入部分之后。以下是一個異常聲明方法的示例:
public void errorProneMethod(int input) throws java.io.IOException {
??? ...
}
這樣,聲明的異常將傳給方法調用者,而且也通知了編譯器;該方法的任何調用者必須遵守處理或聲明規則。
聲明異常的規則
·
必須聲明方法可能拋出的任何可檢測異常(
checked exception
)。
·
非檢測異常(
unchecked exception
)不是必須的,可聲明,也可不聲明。
·
調用方法必須遵守任何可檢測異常的處理或聲明規則。若覆蓋一個方法,則不能聲明與覆蓋方法不同的異常。聲明任何異常必須被覆蓋方法所聲明異常的同類或子類。
可檢測異常和非可檢測異常
Java
的可檢測異常和非檢測異常涇渭分明。可檢測異常經編譯器驗證,對于聲明拋出異常的任何方法,編譯器將強制執行處理或聲明規則。
非檢測異常不遵循處理或聲明規則。在產生此類異常時,不一定非要采取任何適當操作,編譯器不會檢查是否已解決這樣一個異常。有兩個主要類定義非檢測異常:
RuntimeException
和
Error
。
為
什么
Error
子類屬于非檢測異常?這是因為無法預知它們的產生時間。若
Java
應用程序內存不足,則隨時可能出現
OutOfMemoryError
;起
因一般不是應用程序中的特殊調用,而是
JVM
自身的問題。另外,
Error
類一般表示應用程序無法解決的嚴重問題,故將這些類視為非檢測異常。
RuntimeException
類也屬于非檢測異常,一個原因是普通
JVM
操作引發的運行時異常隨時可能發生。與
Error
不同,此類異常一般有特定操作引發,但這些操作在
Java
應用
程序會頻繁出現。另一個原因是:它們表示的問題不一定作為異常處理??梢栽?/span>
try-catch
結構中處理
NullPointerException
,但若
在使用引用前測試空值,則更簡單經濟。
異常處理技術和實踐
選擇處理或聲明
一個經驗法則是:
“
盡可能去處理異常,如果沒有能力處理就聲明異常。
”
從本質上將,僅當方法缺少自我處理異常的信息、上下文或資源時,才將異常信息傳給調用者。
標準異常處理選項
對于處理器代碼塊捕獲的大多數錯誤情況,一般可以采用以下
9
種響應方式:
·
記錄異常和相關信息
·
要求用戶或應用程序輸入信息
·
使用默認值或替換數據
·
將控制轉移到應用程序的其他部分
·
將異常轉換為其他形式
·
忽略問題
·
重試操作
·
采取替換或恢復操作
·
使系統作好停止準備
記錄異常和相關信息
為有效處理異常,日志記錄(
logging
)是最有效的工具之一。這有利于系統的長期開發和維護,有助于完成系統恢復、測試和調試等任務。一般要記錄信息,可使用下列方法:
??? ·
標準輸出或標準錯誤流
??? ·
自定義記錄類
??? · Java
記錄
API
1.
標準輸出或標準錯誤流
對于簡單的記錄任務,可使用標準輸出或錯誤流:
System.out
、
System.err
。
System.out.println("User error: replace user and continue");
System.err.println("Press any key. Go on. I dare ya.");
因為
System
類允許通過方法調用
System.setOut(PrintStream)
和
System.setErr(PrintStream),
將輸出重定向到另一個目標位置,所以能夠滿足基本文件輸出等的需要。
此類功能一般適用于簡單應用程序的本地記錄。而對于較復雜的應用程序,使用這些功能工作量過大。此外,一些
Java
代碼類型運行在服務器的
JVM
中,不允許將輸出定向到這些位置。
2.
自定義記錄類
一些應用程序要求記錄更靈活,更易配置。此時,應為系統開發記錄資源(即開發一個類)來接受應用程序范圍內的調用,并將它們記錄到一個中心位置。這樣的類常實現為靜態資源或單模式(
Singleton
),以確保通用于系統,而不需要其他對象包含其引用。
import java.io.*;
import java.util.*;
public class CustomLogger {
??? private static final String DEFAULT_FILE="exceptions.log";
??? private static final String FILE_KEY="application.logfile";
??? private static CustomLogger instance = new CustomLogger();
??? private static PrintWriter outputLog;
???
??? private CustomLogger() {
??? ??? String filename = System.getProperty(FILE_KEY,DEFAULT_FILE);
??? ??? try{
??? ??? ??? outputLog = new PrintWriter(new FileWriter(filename,true));
??? ??? }
??? ??? catch (IOException exc) {
??? ??? ??? exc.printStackTrace();
??? ??? }
??? }
??? public static CustomLogger getInstance() {
??? ??? return instance;
??? }
??? public void log(String message) {
??? ??? logMessage(new Date() + " " + message);
??? }
??? public void log(Throwable error) {
???
??? StringBuffer message = new StringBuffer(new Date() + " ERROR: "+
error.getClass().getName()+ System.getProperty("line.separator"));
??? ??? message.append(error);
??? ??? logMessage(message.toString());
??? }
??? private void logMessage(String message) {
??? ??? outputLog.println(message);
??? ??? outputLog.flush();
??? }
}
3.Java
記錄
API
在
JDK1.4
中,
Sun
引入了記錄
API
,以更靈活有效地記錄錯誤、消息和通知。一般地,使用
java.util.logging
包中的幾個類即可執行簡單的記錄任務。例如,若要將消息發送到標準輸出,則代碼如下所示:
Logger logException = Logger.getLogger("basic.exception.example");
logExcetption.log(Level.WARNING, "Unable to find configuration file.");
也可以方便地設置易于閱讀的日志文件:
Logger logException = Logger.getLogger("exception.example");
try{
??? Handler fileOut = new FileHandler("exc.err",true);
??? fileOut.setFormatter(new SimpleFormatter());
??? logException.addHadndler(fileOut);
}
catch (IOException ex) {
??? //....
}
logException.log(Level.WARNING,"Exception generated");
要求用戶或應用程序輸入信息
對
于與最終用戶
“
接近
”
的問題,最好讓用戶來確定響應應用程序相應問題的方式。這一般通過
GUI
管理,使用戶作出選擇或輸入信息。對于簡單故障,用戶可能僅
需確定適當的操作序列;而對于較復雜的問題,則可能要添加附加或替換信息。很多
GUI
使用對話框來提示用戶作出決策,在
AWT
中,可使用
java.awt.Dialog
類;在
Swing API
中,一般使用
javax.swing.JDialog
或
javax.swing.JOptionPane
。下面的示例代碼顯示了在出現錯誤時如何要
求用戶輸入信息:
private void showConfigErrorMessage() {
??? String msg = "Unable to load user preferences file: create new file?";
??? String title = "Application Error";
??? int result = JOptionPane.showConfirmDialog(null,msg,title,JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
??? if(result == JOptionPane.YES_OPTION) {
??? ??? creatNewConfigFile();
??? }
??? else{
??? ??? loadDefaultSettings();
??? }
}
使用默認值或替換數據
如
果使用替換或默認值,那么,即使在執行操作時遇到異常,程序仍可繼續運行。在通信示例中。另一臺服務器(或同一臺服務器上的不同端口)可能是一個合適的替
換方案。若有可識別的任何標準值,則可以在應用程序中包含默認值。在最簡單的情況下,可以將這些值作為應用程序中的常量(靜態最終值):
private static final String ALTERNATE_HOST = "denver";
private static final int ALTERNATE_PORT = 5280;
更復雜一些的選項是允許默認值存儲在配置文件中,并在運行時加載到應用程序。
private static final String ALTERNATE_HOST = "denver";
private static final String HOST_KEY = "client.app.host";
public void sendData(String host,int port,Serializable information, String propertyFile){
??? if(communication ==null){
??? ??? communication = new ClientNetWork();
??? }
??? try{
??? ??? communication.connect(host,port);
??? ??? conmunication.sendData(information);
??? }
??? catch( IOException exc){
??? ??? Properties serverProps = new Properties();
??? ??? try{
??? ??? ??? FileInputStream input = FileInputStream(propertyFile);
??? ??? ??? serverProps.load(input);
??? ??? ??? String serverName = serverProps.getProperty(HOST_KEY,ALTERNATE_HOST);
??? ??? ??? sendData(serverName,port,information);
??? ??? }
??? ??? catch(IOException exc2) {
??? ??? ??? exc.printStackTrace();
??? ??? }
??? }
}
將控制轉移到應用程序的其他部分
還有一種將異常處理職責傳給系統另一部分的非終極方式:不是從異常產生方法完全委托,而是開發一個異常處理類,使用它來集中處理應用程序代碼。
private void saveOrderInfo(OrderInfo info){
??? try{
??? ??? OrderService.getInstance().saveOrderInfoToFile(info);
??? }
??? catch(IOException exc)
??? {
??? ??? handleFileSaveError(info);
??? }
}
將異常轉換為其他形式
有
時,最好將異常轉化為其他形式,以將異常更改為對應用程序更有意義的形式,提供更適當的上下文以準確描述錯誤。由于可將原始異常包裝到另一個異常中(從
JDK1.4
開始),故不會失去原始異常的上下文或數據。下例顯示解決
saveOrderInfo
方法產生的
IOException
的另一種方法:
catch
塊將異常轉化為
OrderException
拋給方法調用者。
private void saveOrderInfo(OrderInfo info) throws OrderException{
??? try {
??? ??? OrderService.getInstance().saveOrderInfoToFile(info);
??? }
??? catch(java.io.IOException exc) {
??? ??? throw new OrderException("File I/O error when saving ORderInfo",exc);
??? }
}
忽略問題
注
意,不能濫用這種方法,否則,代碼將出現嚴重問題。確實存在這樣的情況,即一個異常對應用程序的所有其他部分沒有任何影響。例如,在調用
I/O
流的關閉方
法時產生的異常便屬于這種類型。該方法可拋出表示問題的
IOException
,但應用程序除了記錄事件之外,一般什么也不做。
//Declare member variable sourceFile in the class
private FileReader souceFile;
//...
??? try{
??? ??? sourceFile.close();
??? }
??? catch (java.io.IOException exc) {
??? ??? //...
??? }
重試操作
有些情況下,最適當的操作是在等一段時間后重試操作。在試圖連接到服務器或數據庫時,有時會發生異常,因為服務器的信息量過大。此時,最好再等一段時間,再嘗試連接。
try{
??? clientNetwork.connect("www.talk-about-tech.com",5280);
}
catch (java.io.IOException exc)
{
??? try {
??? ??? Thread.sleep(10000);
??? }
??? catch (InterruptedException exc2) {}
??? try{
??? ??? clientNetwork.connect("www.talk-about-tech.com",5280);
??? }
??? catch (java.io.IOException exc)
??? {
??? ??? //...
??? }
}
采取替換或恢復操作
有時,可采取補償方式來處理異常。例如,若不能連接服務器,則可以在本地緩存數據,并在之后某一段時間將數據發送到服務器。理想情況下,恢復操作將允許應用程序正常運行。
try{
??? svr.connect("www.talk-about-tech.ocm",5280);
}
catch (IOException exc)
{
??? enableFileCache();
}
使系統作好停止準備
如果某一異常確實是應用程序的關鍵錯誤,則需要采取步驟,使系統作好停止準備。這種情況下,要確保該應用程序不致使系統的其他部分出現故障,也不會使數據處于不一致狀態,這就需要完成以下幾件事情:
??? ·
若有打開的文件,則關閉它
??? ·
若有基于連接的資源,則采用普通方式關閉
??? ·
若有需要保持一致的信息,則一定要保存
??? ·
根據需要,通知應用程序、客戶端或子系統應用程序將結束。
處理異常時提倡的事情
盡可能地處理異常
要盡量處理處理異常,如果條件確實不允許,無法在自己的代碼中完成處理,就考慮聲明異常。如果人為避免在代碼中處理異常,僅做聲明,則是一種錯誤和懶惰的實踐。
具體問題具體解決
異常的部分優點在于能為不同類型的問題提供不同的處理操作。有效異常處理的關鍵是識別特定故障場景,并開發解決此場景的特定相應行為。為了充分利用異常處理能力,需要為特定類型的問題構建特定的處理器塊。
記錄可能影響應用程序運行的異常
至少要采取一些永久方式,記錄下可能影響應用程操作的異常。理想情況下,當然是在第一時間解決引發異常的基本問題。不過,無論采用什么處理操作,一般總應記錄下潛在的關鍵問題。別看這個操作非常簡單,但可以幫助您用很少的時間來跟蹤應用程序復雜的問題起因。
根據情況將異常轉換為業務上下文
構建異常成本不高,所以,若要通知一個應用程序特有的問題,有必要將應用程序轉換為不同形式。若用業務特定狀態表示異常,則代碼更易維護。
J2SE 1.4
的異常有所增強,允許包裝原始異常,故不會失去問題的原始上下文。
處理異常是忌諱的事項
一般不要忽略異常
在異常處理塊中,一項最危險的舉動是
“
不加通告
”
地處理異常。如下所示:
try {
??? Class.forName("business.domain.Customer");
}
catch (ClassNotFoundException exc) {}
經常能夠在代碼中看到類似的代碼塊。有人總喜歡在編寫代碼簡單快速地編寫空處理塊,并
“
自我安慰地
”
宣稱準備在
“
后期
”
添加恢復代碼,但這個
“
后期
”
最終成了
“
無期
”
。
這種做法有什么壞處?如果異常對應用程序的其他部分確實沒有任何負面影響,這未嘗不可。但事實往往并非如此,異常會擾亂應用程序的狀態;此時,這樣的代碼塊無異于掩耳盜鈴。
不要使用覆蓋式異常處理塊
另一個危險的處理實踐式覆蓋式處理器(
blanket handler
)。該代碼的基本結構如下所示:
try{
??? //...
}
catch (Exception e) {
??? //...
}
使用覆蓋式異常處理塊有以下兩個前提之一:
·
代碼中只有一類問題。
???
這可能正確,但即便如此,也不應該使用覆蓋式處理,捕獲更具體的異常形式有利無弊。
·
單個恢復操作始終適用。
???
這幾乎絕對錯誤。幾乎沒有哪個方法能放之四海而皆準,能應付出現的任何問題。
分析一下這樣編寫代碼將發生的情況。只要方法不斷拋出預期的異常集,則一切正常。但是,如果方法拋出了未預料到的異常,則無法看到要采取的操作。當覆蓋式處理器對新異常類型執行千篇一律的任務時,只能間接看到異常處理結果。如果代碼沒有打印或記錄語句,則根本看不到結果。
更糟的是,當代碼發生變化時,覆蓋式處理器將繼續作用于所有新的異常類型,并以相同方式處理所有類型。一般地,在修改代碼時,這是不智之舉
——
要考慮方法更改所產生的影響,并相應更改異常處理塊。
一般不要將特定異常轉換為更通用的異常
將特定異常轉換為更通用異常是一種錯誤做法。一般而言,這將取消異常起初拋出時產生的上下文,在將異常傳到系統的其他位置時,將更難處理。見下例:
try{
??? //error-prone code
}
catch (IOException e) {
??? String msg = "If you didn't have a problem before, you do now!";
??? throw new Exception(msg);
}
因
為沒有原始異常的信息,所以處理器塊無法確定問題的起因,也不知如何更正問題。更糟的是,該方法必須聲明它拋出
Exception
,實際上,這形同虛設,
對該方法的
Java
文檔讀者沒有任何幫助作用。對于拋出通用
Exception
對象的方法,應執行哪種恢復操作?一般而言,若要重新拋出異常,要有
3
個理
由:
??? ·
重新拋出同一異常,以采取一些操作,然后傳送異常作進一步處理。
??? ·
包裝或重新拋出更具體的異常類型,以縮小后續處理器的問題類型。
??? ·
包裝或重新拋出不同類型的異常,轉換為適當上下文,提供給不同應用程序子系統的處理器。在轉換為業務異常時,一般會這樣做。
不要處理能構避免的異常
對
于有些異常類型,實際上根本不必處理。一般地,要秉持這樣一個理念:為不可避免的錯誤使用異常。雖然可用異常來處理形形色色的各種問題,但不一定非要這么
做。處理或聲明異常必然會影響代碼的運行效率。也就是說,若能避免某些問題類型,則成本比使用異常來解決要低。諸如處理空指針等問題。
高級異常處理概念
自定義異常
使用子定義異常有什么好處呢?創建自定義異常是為了表示應用程序的一些錯誤類型,為代碼可能發生的一個或多個問題提供新的含義??梢燥@示代碼多個位置之間的錯誤的相似性,也可區分代碼運行時可能出現的相似問題的一個或多個錯誤,或給出應用程序中一組錯誤的特定含義。
創建和使用自定義異常并不難。遵循以下
3
個步驟即可。
1.
定義異常類
一般要定義新類來表示自定義異常。多數情況下,只需創建已有異常類的子類。
public class CustomerExistsException extends Exception {
??? public CustomerExistException() { }
??? public CustomerExistException(String message) {
??? ??? super(message);
??? }
}
至少要繼承
Throwable
或
Throwable
的子類。經常要定義一個或多個構造函數,以在對想中存儲錯誤消息。在繼承任何異常時,將自動繼承
Throwable
類的一些標準特性,如:
??? ·
錯誤消息
??? ·
棧跟蹤
??? ·
異常包裝
若要在異常中添加附加信息,則可以為類添加一些變量和方法:
public class CustomerExistsException extends Exception
{
??? private String customerName;
??? public CustomerExistsException() {}
??? public CustomerExistsException(String message) {
??? ??? super(message);
??? }
??? public CustomerExistsException(String message, String customer){
??? ??? super(message);
??? ??? customerName = customer;
??? }
??? public String getCustomerName() {
??? ??? return customerName;
??? }
}
由本例可知,可修改
CustomerExistsException
類,以支持其他屬性。例如,可將
customerName
字符串(引發異常的記錄的客戶名)與異常聯系起來。
2.
聲明方法拋出自定義異常
這實際上時
“
處理或聲明
”
規則的
“
聲明
”
部分。為了使用自定義異常,必須通知調用代碼的類:要準備處理這個異常類型。為此,聲明一個或多個方法拋出異常:
public void insertCustomer(Customer c) throws CustomerExistsException {
??? //...
}
3.
找到故障點,新建異常并加上關鍵子
throw
最后一步實際上是創建對象,并通過系統傳送該對象。為此,需要了解代碼將在方法的哪個位置出現故障。根據情況,可能要使用以下部分或所有條件,來指示代碼中的故障點。
??? (1)
外部問題
??? ??? ·
應用程序中產生的異常
??? ??? ·
其他方法返回的故障代碼
??? (2)
內部問題
??? ??? ·
應用程序狀態不一致
??? ??? ·
應用程序中的處理問題
子類
在
構建子類時,可以覆蓋父類的方法,此時,必須遵守一些嚴格的規則。必須復制方法的簽名:方法名、輸入和輸出都要匹配。不能使覆蓋的方法比父類更私有。還要
注意,方法不能拋出父類方法未聲明的異常。也就是說,在覆蓋方法時,可拋出與父類的方法相同的異?;虍惓5淖蛹H绻诟割惙椒ㄖ形炊x,則不能拋出不同
的異常。
接口和抽象類的異常聲明
抽象方法只提供方法簽名,不真正定義任何代碼;接口實際上全部由抽象方法組成。為這類方法定義異常時,實際上是在設置一種期望,描述最終實現的方法可能出現的錯誤。
public interface CommunicationServer {
??? public void processClient() throws ServerConnectException;
??? public void listen();
}
Java
核心語言中的異常
基本數據類型的常見問題
1.
不允許在布爾和數值類型間轉換
在
Java
中,布爾變量不能轉換為其他任何基本數據類型。也就是說,不能聲明這樣一個方法,它返回整型,并將結果視為布爾值。
2.
數值數據類型的數據截斷
在兩種情況下,可能出現這個問題:收縮轉換和數學運算。
強制轉換:
int i = 2048;
byte b = (byte)i;
數學運算:
int i,j,k;
i = 10000;
j = 400000;
k = i * j;
3.
數值數據類型的精度丟失
例:
double c= 0.025;
double d= 21.003;
double e=c*d;
System.out.println("c ="+c+", d="+d);
System.out.println("c*d="+e);
在
本例中,輸出的乘積是
0.5250750000000001
。這個問題出現的是舍入錯誤??刹捎脦追N方案來消除這個問題。如果要在操作小數值時確保精度,
則使用
BigDecimal
類。如果僅要求格式符合輸出要求,則可用
java.lang.format
包中的類來處理舍入。
4.
數學運算和
ArithmeticException
除零錯誤。
一般建議
·
預先了解應用程序的數據要求,并制定相應規劃。選項有:
??? a.
使用
BigDecimal
或
BigInteger
。
??? b.
選擇一種足夠大、足夠精確,可以滿足需要的存儲類型。
·
在使用
/
或%運算符時,檢查操作數中的
0
值。
·
在顯示值時,使用滿足需要的策略。選項有:
??? a.
原始基本類型用于基本需要。
??? b. BigDecimal
或
BigInteger
用于擴展精度。
??? c. NumberFormat
用于準確格式控制。