異常
1.定義
異常指的是在程序運行過程中發生的異常事件,通常是由硬件問題或者程序設計問題所導致的。在Java等面向對象的編程語言中異常屬於對象。
Java異常類層次結構圖:
2.異常由來:
編程中對於那些會出現的問題,Java採用異常機制來解決。
最常使用的就是try{}catch{}
本質:java對不正常情況進行描述後的對象體現。
程序可能出現的錯誤或問題:
a、用戶輸入錯誤導致的異常:如用戶不正常使用程序,輸入一些非法參數
b、設備硬件等發生的錯誤:如硬盤損壞等
c、物理限制:如存儲空間不足等
d、代碼錯誤:在程序編寫的方法可能不正確,數組越界,返回錯誤參數等。
3.異常體系
Throwable 類
是 Java 語言中所有錯誤或異常的超類。只有當對象是此類(或其子類之一)的實例時,才能通過 Java 虛擬機或者 Java throw 語句拋出。類似地,只有此類或其子類之一纔可以是 catch 子句中的參數類型。
有兩個重要的子類:Exception(異常)和 Error(錯誤)
Error(錯誤)
是程序無法處理的錯誤,表示運行程序中較嚴重的問題。
這些錯誤表示故障發生於虛擬機自身、或者發生在虛擬機試圖執行應用時,如Java虛擬機運行錯誤(Virtual MachineError)、類定義錯誤(NoClassDefFoundError)等。這些錯誤是不可查的,因爲它們在應用程序的控制和處理能力之 外,而且絕大多數是程序運行時不允許出現的狀況。對於設計合理的應用程序來說,即使確實發生了錯誤,本質上也不應該試圖去處理它所引起的異常狀況。在 Java中,錯誤通過Error的子類描述。
Exception(異常)
是程序本身可以處理的異常。
RuntimeException類是他的一個比較特殊的類。
RuntimeException 類:
RuntimeException 類及其子類表示“JVM 常用操作”引發的錯誤。例如,若試圖使用空值對象引用、除數爲零或數組越界,則分別引發運行時異常(NullPointerException、ArithmeticException)和 ArrayIndexOutOfBoundException。
注意:異常和錯誤的區別:異常能被程序本身處理,錯誤是無法處理。
4.異常分類
1.Java的異常
Java的異常(包括Exception和Error)分爲可查的異常(checked exceptions)和可不查的異常(unchecked exceptions)。
可查異常(編譯器要求必須處置的異常):
程序在運行時,在一定程度上可以預知的異常,並可以採取某種方式進行處理。
除了RuntimeException及其子類以外,其他的Exception類及其子類都屬於可查異常。這種異常的特點是Java編譯器會檢查它,也就是說,當程序中可能出現這類異常,要麼用try-catch語句捕獲它,要麼用throws子句聲明拋出它,否則編譯不會通過。
可不查異常(編譯器不要求強制處置的異常):
包括運行時異常(RuntimeException與其子類)和錯誤(Error)。
這類異常編譯時,編譯器不會去檢查。
2.Exception異常
Exception 這種異常分兩大類運行時異常和非運行時異常(編譯異常)。程序中應當儘可能去處理這些異常。
運行時異常:
RuntimeException及其子類,如NullPointerException(空指針異常)、IndexOutOfBoundsException(下標越界異常)等。這些異常編譯器不檢查,即使不使用try-catch捕獲,或使用throws拋出,編譯器也不會報錯,但會在運行時觸發。
這些異常一般出在邏輯上,在程序設計時應該從邏輯角度儘可能避免這類異常。
非運行時異常 (編譯異常):
除RuntimeException及其子類外的異常,類型上屬於Exception類及其子類。
編譯器會檢查此類一場,如果不處理,程序是無法編譯通過的。
總結:根據編譯器是否檢查來進行劃分。
總的來說,RuntimeException及其子類外的異常編譯器不進行檢查,其他Exception都需要處理。
異常處理
對於運行時異常、錯誤或可查異常,Java技術所要求的異常處理方式有所不同:
a.運行時異常:java允許可以不拋出,不捕獲;運行時由系統由系統拋出。
b.Error,不拋出,不捕獲,因爲Error是java所不允許發生的。
c.可查行異常,必須捕獲或者拋出。
一個方法所能捕捉的異常,一定是Java代碼在某處所拋出的異常。簡單地說,異常總是先被拋出,後被捕捉的。
異常處理有兩種方式:捕捉異常(try-catch),拋出異常(throws)
1.捕捉異常
java提供了特有的語句進行處理:
try
{
需要被檢測的代碼。
}
catch(異常類 變量)
{
處理異常的代碼;(處理方式)
}
finally
{
一定會執行的語句;
}
注意:
1)finally中定義的通常是關閉資源代碼。因爲資源必須釋放。
2)如果在一個功能中,定義了一些必須要執行的代碼,可以用try{}finally{}的方式,將一定執行的代碼放在finally代碼塊中。
3)finally只有一種情況不會執行。當執行到System.exit(0);fianlly不會執行。
public static void main(String[] args) {
int a = 6;
int b = 0;
try {
System.out.println("a/b的值是:"+ a / b);
} catch (ArithmeticException e) {
System.out.println("程序出現異常,變量b不能爲0。");
}
System.out.println("程序正常結束。");
}
}
分析:此例中由try中監控區,檢測到“除數爲0”從而引發ArithmeticException異常,由異常處理器catch捕獲,並處理。
但是,實際上,”除數爲0“ArithmeticException異常是RuntimeException子類,會由系統自動拋出,不需要使用throw語句。
需要注意的是,一旦某個catch捕獲到匹配的異常類型,將進入異常處理代碼。一經處理結束,就意味着整個try-catch語句結束。其他的catch子句不再有匹配和捕獲異常類型的機會。
對於有多個catch子句的異常程序而言,應該儘量將捕獲底層異常類的catch子 句放在前面,同時儘量將捕獲相對高層的異常類的catch子句放在後面。否則,捕獲底層異常類的catch子句將可能會被屏蔽。
小結:
try 塊:用於捕獲異常。其後可接零個或多個catch塊,如果沒有catch塊,則必須跟一個finally塊。
catch 塊:用於處理try捕獲到的異常。
finally塊:無論是否捕獲或處理異常,finally塊裏的語句都會被執行。當在try塊或catch塊中遇到return語句時,finally語句塊將在方法返回之前被執行。在以下4種特殊情況下,finally塊不會被執行:
1)在finally語句塊中發生了異常。
2)在前面的代碼中用了System.exit()退出程序。
3)程序所在的線程死亡。
4)關閉CPU。
2.拋出異常
可以通過throws和throw拋出異常。
1)throws
定義:
如果一個方法可能會出現異常,但沒有能力處理這種異常,可以在方法聲明處用throws子句來聲明拋出異常。例如汽車在運行時可能會出現故障,汽車本身沒辦法處理這個故障,那就讓開車的人來處理。
throws語句用在方法定義時聲明該方法要拋出的異常類型,如果拋出的是Exception異常類型,則該方法被聲明爲拋出所有的異常。多個異常可使用逗號分割。throws語句的語法格式爲:
methodname throws Exception1,Exception2,..,ExceptionN
{
}
方法名後的throws Exception1,Exception2,…,ExceptionN 爲聲明要拋出的異常列表。當方法拋出異常列表的異常時,方法將不對這些類型及其子類類型的異常作處理,而拋向調用該方法的方法,由他去處理。例如:
import java.io.*;
public class Test1 {
public static void method() throws IOException {
File f = new File("r:\\");
}
public static void main(String[] args) {
//此處報錯,Unhandled exception type IOException未處理異常
method();
}
}
原因:IOException是Exception子類,是編譯異常,必須捕獲,method()方法使用throws向上層調用(main函數)拋出了,但是main函數未處理,所以造成了錯誤。
解決:使用try-catch捕獲並處理。
Throws拋異常的原則:
1)不可查異常(unchecked exception)RuntimeException和Error異常可以不拋出,但是可查異常(checked exception)必須被拋出或者被捕獲,否則編譯會報錯。
2)僅當拋出了異常,該方法的調用者才必須處理或者重新拋出該異常。當方法的調用者無力處理該異常的時候,應該繼續拋出,而不是囫圇吞棗。
public class Test1 {
void method() throws IOException {
File f = new File("r:\\");
}
// 編譯錯誤,必須捕獲或者聲明IOException
void method1() {
method();
}
// 合法,聲明拋出了IOException
void method2() throws IOException {
method();
}
// 合法,聲明拋出Exception,IOException是Exception的子類
void method3() throws Exception {
method();
}
// 合法,捕獲了IOException
void method4() {
try {
method();
} catch (IOException e) {
}
}
// 不合法,又拋出了一個IOException,必須拋出或者再次捕獲
void method5() {
try {
method();
} catch (IOException e) {
throw new IOException();
}
}
// 合法,再次拋出的新的異常
void method6() throws Exception {
try {
method();
} catch (IOException e) {
throw new Exception();
}
}
}
3)在子父類覆蓋時:
a,子類拋出的異常必須是父類的異常的子類或者子集。
b,如果父類或者接口沒有異常拋出時,子類覆蓋出現異常,只能try不能拋。
class T1 {
void method() throws IOException {
}
}
class T2 extends T1{
//錯誤,子類拋出的異常不是父類拋出異常的子類
void method()throws Exception {
}
}
2)throw拋出異常
在函數體內使用,即拋出一個一場對象。形如:
throw new Throwable();
該對象是人爲拋出的,雖然和程序運行產生的不同,但是仍是需要拋出或者捕獲的。
Throwable
Throwable中的方法:
1.getMessage():獲取異常信息,返回字符串。
2.toString():獲取異常類名和異常信息,返回字符串。
3.printStackTrace():獲取異常類名和異常信息,以及異常出現在程序中的位置,返回值void。
4.printStackTrace(PrintStreams):通常用該方法將異常內容保存在日誌文件中,以便查閱。
自定義異常:
/***
* 自定義異常
*/
class Demo{
//除法,拋出我的自定義異常
int div(int a, int b) throws MyException{
if(b==0){
throw new MyException("除數不能爲負數!");
}
return a/b;
}
}
public class Test3 {
public static void main(String[] args) {
int a = 10;
int b = 0;
try {
Demo d = new Demo();
int s = d.div(a, b);
} catch (MyException e) {
// TODO: handle exception
e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
class MyException extends Exception {
/***
* 自定義異常的構造方法,信息的傳遞通過有參構造方法,
* 父類通過有參構造方法傳遞的參數來打印消息
*/
public MyException(String MyMessage) {
super(MyMessage);
}
}
小測試:
import java.util.*;
/***
* 老師使用電腦講課。
*
* 描述電腦: 1、電腦運行 2、電腦重啓 描述電腦問題: 1、電腦藍屏了 2、電腦起火了
*
* 描述老師: 1、老師使用電腦 2、老師講課。
*
* 描述老師可能出現的問題:
* 1、老師不能繼續講課了。
* 2.電腦藍屏,重新啓動
* a.多次重啓後,成功
* b.多次藍屏重啓後,電腦着火了
* 3.一次啓動成功,老師開始講課
*/
//藍屏異常
class BlueScreenException extends Exception {
public BlueScreenException(String message) {
super(message);
}
}
//電腦着火
class FireBreakException extends Exception {
public FireBreakException(String message) {
super(message);
}
}
//電腦類
class Computer {
// 隨機產生一個數,來模擬電腦啓動
int i;
//啓動
void run() throws BlueScreenException, FireBreakException {
i = (int) (Math.random()*3+1);
if(1==i)
{
System.out.println("電腦正常啓動");
} else if(2==i)
{
throw new BlueScreenException("電腦藍屏了!");//藍屏異常
} else
{
throw new FireBreakException("電腦着火了!");//着火異常
}
}
//重啓
void reset() {
System.out.println("電腦重新啓動");
try {
run();
} catch (BlueScreenException e) {
System.out.println(e.getMessage());
reset();
} catch (FireBreakException e) {
System.out.println("多次重啓,着火了!");
}
}
}
//無法上課的異常
class BreakTeachingException extends Exception{
public BreakTeachingException(String message){
super(message);
}
}
//老師類
class Teacher{
private Computer c = new Computer();
void teach() throws BreakTeachingException{
try {
c.run();
System.out.println("老師開始上課!");
} catch (BlueScreenException e) {
System.out.println(e.getMessage());
c.reset();
} catch (FireBreakException e) {
throw new BreakTeachingException("老師無法上課,原因"+e.getMessage());
}
}
}
public class Test4 {
public static void main(String[] args) {
Teacher t = new Teacher();
try {
t.teach();
} catch (BreakTeachingException e) {
e.printStackTrace();
System.out.println("暫停上課,電腦着火了!");
}
}
}
一次啓動成功,老師開始講課:
一次啓動,電腦着火:
多次藍屏重啓後成功
多次藍屏重啓後電腦着火
包
確保類的唯一性,使得同類名的類可以同時存在,使用包進行劃分。
權限問題:
規則
1、包必須寫在程序的第一行。因爲要先有包,才知道類文件的存放地方。
2、類的全稱:包名.類名。
3、編譯定義了包的程序文件時,在編譯時要指定包的存儲目錄。
如:javac –d c:\mypack類名.java
jar包
創建jar包
jar -cvf mypack.jar packa packb
查看jar包
jar -tvf mypack.jar [>定向文件]
解壓縮
jar -xvf mypack.jar
自定義jar包的清單文件
jar –cvfm mypack.jar mf.txt packa packb
單個包打成jar包:
進入cmd:
編譯:
javac -d . Demo.java和javac -d f:\JavaDemo Demo.java的不同在於指定的目錄不同。
jar包生成:
運行:把生成的jar包放到D盤下,設置classpath路徑,並運行。
多個不同包下的類建jar包:
同理:javac –d . Demo.java分別編譯
建好後: