java程式設計--孫鑫java無難事Lesson4《內部類、異常處理》
1.內部類的特性和使用方法
概念總述:
在一個類中定義另外一個類,這個類就叫做內部類或內建類 (inner class) 。
內部類可以讓我們將邏輯上相關的一組類組織起來,並由外部類(outer class)來控制內部類的可見度。
當我們建立一個inner class時,其對象就擁有了與外部類對象之間的一種關係,這是通過一個特殊的this reference形成的,使得內部類對象可以隨意的訪問外部類中所有的成員。
詳細內容:
(1)外部類的main靜態方法中無法直接存取內部類,錯誤資訊如下:
//*********************************************************************************
Outer.java:30: 錯誤: 無法從靜態上下文中引用非靜態 變數 this
Inner inner=new Inner();
(2)引用內部類時需要一個外部類的執行個體來引用
否則錯誤資訊如下:
//*********************************************************************************
Outer.Inner inner=new Outer.Inner();
inner.print();//error
Outer.java:46: 錯誤: 需要包含Outer.Inner的封閉執行個體
Outer.Inner inner=new Outer.Inner();
(3)內部類訪問外部類的成員、中介層類的成員、和本地變數
測試代碼及結果如下:
//*********************************************************************************
class Outer //外部類
{
private int index=100;//外部類成員
//內部類Inner類不管嵌套層次多深,都可以訪問外部類成員
void fn( final int a)
{
//ok 類的範圍限定在if語句中 仍然可以訪問外部類成員
final int b=0;//需要初始化
if(true)
{
class Middle //中間類
{
private int index=70;////中間類成員
class Inner //內部類
{
private int index=50;//內部類成員
void print()
{
int index=30;
System.out.println(index);//30
System.out.println(this.index);//50
System.out.println(Middle.this.index);//70
System.out.println(Outer.this.index);//100
System.out.println(a);//ok 聲明為final時
System.out.println(b);//ok 聲明為final時
}
}
}
}
}
(4)在方法中定義的內部類,如果要存取方法中定義的本地變數或方法的參數,則變數必須被聲明final,否則出錯,錯誤資訊如下:
//*********************************************************************************
Outer.java:38: 錯誤: 從內部類中訪問本地變數a; 需要被聲明為最終類型a=5;
Outer.java:39: 錯誤: 從內部類中訪問本地變數b; 需要被聲明為最終類型b=6;
(5)private內部類
內部類聲明為private時無法訪問,否則錯誤資訊如下:
//*********************************************************************************
Outer.java:133: 錯誤: Outer.Inner可以在Outer中訪問private Outer.Inner inner=outer.new Inner();
內部類還可以聲明為protected、abstract或final。
(6)static內部類
內部類可以聲明為static的,但此時就不能再使用外部類的非static的成員變數和非static的成員方法,否則錯誤資訊如下:
//*********************************************************************************
Outer.java:102: 錯誤: 無法從靜態上下文中引用非靜態 變數 this
System.out.println(Outer.this.index);//error
(7)非static的內部類中的成員不能聲明為static的,只有在頂層類或static的內部類中才可聲明static成員,
否則錯誤資訊如下:
//*********************************************************************************
Outer.java:110: 錯誤: 內部類Outer.Inner中的靜態聲明非法
static void print()
修飾符 'static' 僅允許在常量變數聲明中使用
(8)內部類繼承
內部類無法直接繼承,類定義如下:
//*********************************************************************************
class Car
{
class Wheel{}
}
class PlaneWheel extends Car.Wheel
{
public static void main(String[] args) {
PlaneWheel pw=new PlaneWheel();
System.out.println("Hello World!");
}
}
則錯誤資訊如下:
Car.java:9: 錯誤: 需要包含Car.Wheel的封閉執行個體
class PlaneWheel extends Car.Wheel
解決方案:
class Car
{
class Wheel{}
}
class PlaneWheel extends Car.Wheel
{
//產生外部類對象
PlaneWheel(Car car)
{
car.super();
}
public static void main(String[] args)
{
Car car=new Car();
PlaneWheel pw=new PlaneWheel(car);
}
}
(9)匿名內部類
匿名內部類測試代碼1如下:
//*********************************************************************************
interface Animal
{
void eat();
void sleep();
}
class Zoom
{
//隱藏實現細節 通過介面訪問內部類的方法
private class Tiger implements Animal
{
public void eat()
{
System.out.println("Tiger eat");
}
public void sleep()
{
System.out.println("Tiger sleep!");
}
}
Animal getanimal()
{
//介面無法直接執行個體化對象 需要使用匿名內部類 實現介面中的方法
return new Animal()
{
public void eat()
{
System.out.println("Animal eat");
}
public void sleep()
{
System.out.println("Animal sleep!");
}
};
}
}
class Test
{
public static void main(String[] args)
{
Zoom zoom=new Zoom();
Animal an=zoom.getanimal();
an.eat();
an.sleep();
}
}
//*********************************************************************************
//輸出結果
Animal eat
Animal sleep!
//*********************************************************************************
匿名內部類測試代碼2如下:
//*********************************************************************************
class A
{
void fn1(){}
}
abstract class B
{
abstract void fn2();
}
class C extends A
{
//使用匿名內建類 返回內建類對象
B getB()
{
return new B()
{
public void fn2(){};//實現抽象類別B的方法
};
}
}
class Test
{
static void method1(A a)
{
a.fn1();
}
static void method2(B b)
{
b.fn2();
}
public static void main(String[] args)
{
C c=new C();
method1(c);
method2(c.getB());
}
}
(10)利用內部類解決方案重名問題
測試代碼如下:
//*********************************************************************************
interface Machine
{
void run();
}
class Person
{
public void run()
{
System.out.println("run");
}
}
//需要繼承 但是繼承的同時方法與介面中方法重名 利用內部類解決
class Robot extends Person
{
private class MachineHeart implements Machine
{
public void run()
{
System.out.println("heart run");
}
}
Machine getmachine()
{
return new MachineHeart();
}
}
class Test
{
public static void main(String[] args)
{
Robot robot =new Robot();
Machine m=robot.getmachine();
m.run();
robot.run();
}
}
//*********************************************************************************
//輸出結果
heart run
run
2.異常處理
概念總述:
Java程式在執行過程中如出現異常,會自動產生一個異常類對象,該異常對象將被提交給Java運行時系統,這個過程稱為拋出(throw)異常。
當Java運行時系統接收到異常對象時,會尋找能處理這一異常的代碼並把當前異常對象交給其處理,這一過程稱為捕獲(catch)異常。
如果Java運行時系統找不到可以捕獲異常的方法,則運行時系統將終止,相應的Java程式也將退出。
詳細內容:
(1)構造一個除0異常,捕獲異常資訊如下:
//*********************************************************************************
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Excep.division(Excep.java:5)
at ExcepTest.main(Excep.java:14)
(2)捕獲異常的位置--try catch語句塊
try語句中發生異常後的語句將不予執行,但是catch語句後的語句予以執行。異常處理代碼放置在catch語句中。
(3)拋出異常而沒有捕獲異常時,錯誤資訊如下:
//*********************************************************************************
Excep.java:53: 錯誤: 未報告的異常錯誤Exception; 必須對其進行捕獲或聲明以便拋出
System.out.println( excep.division(5,0));
(4)捕獲異常順序--特殊到一般
已經捕獲了Exception,再次捕獲ArithmeticException時錯誤資訊如下:
//*********************************************************************************
Excep.java:51: 錯誤: 已捕獲到異常錯誤ArithmeticException
catch( ArithmeticException e)
(5)RuntimeException運行時異常並不需要顯示的捕獲
對於RuntimeException,通常不需要我們去捕獲,這類異常由Java運行系統自動拋出並自動處理。
(6)捕獲異常後可以繼續拋出異常
//*********************************************************************************
public int division(int a ,int b) throws ArithmeticException
{
//在函數內部捕獲異常
try
{
return a/b;
}
catch (ArithmeticException e)
{
e.printStackTrace();//輸出異常資訊
throw e;//再次拋出異常 程式跳轉
}
}
//捕獲異常資訊如下:
//*********************************************************************************
java.lang.ArithmeticException: / by zero
at Excep.division(Excep.java:33)
at ExcepTest.main(Excep.java:75)
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Excep.division(Excep.java:33)
at ExcepTest.main(Excep.java:75)
(7)捕獲異常後可以重新拋出新的異常
//*********************************************************************************
public int division(int a ,int b) throws Exception
{
//在函數內部捕獲異常
try
{
return a/b;
}
catch (ArithmeticException e)
{
e.printStackTrace();//輸出異常資訊後
throw new Exception("can not div zero!");//再次拋出新異常
}
}
//*********************************************************************************
捕獲異常資訊如下:
F:\java\JavaLesson\Lesson4>java ExcepTest
java.lang.ArithmeticException: / by zero
at Excep.division(Excep.java:33)
at Excep.fn1(Excep.java:45)
at ExcepTest.main(Excep.java:58)
java.lang.Exception: can not div zero!
at Excep.division(Excep.java:38)
at Excep.fn1(Excep.java:45)
at ExcepTest.main(Excep.java:58)
(8)自訂異常
從Exception派生子類如下:
//*********************************************************************************
class DivisorIsMinusException extends Exception
{
DivisorIsMinusException(String str)
{
super(str);
}
}
自訂異常類的使用如下:
public int division(int a ,int b) throws ArithmeticException ,DivisorIsMinusException
{
if(b<0)
throw new DivisorIsMinusException("Divisor can't be minus!");
return a/b;
}
int fn1(int a,int b) throws ArithmeticException ,DivisorIsMinusException
{
return division(a,b);
}
//*********************************************************************************
//異常捕獲資訊如下:
F:\java\JavaLesson\Lesson4>java ExcepTest
DivisorIsMinusException: Divisor can't be minus!
at Excep.division(Excep.java:52)
at Excep.fn1(Excep.java:57)
at ExcepTest.main(Excep.java:77)
(9)finally執行機會
不管異常發生沒有,finally均會執行,適合在此處做一些清理和資源釋放工作
finally語句執行不受return語句限制,但是使用exit退出時不會執行.
在Test.java中執行如下測試代碼:
//*********************************************************************************
try
{
System.out.println( excep.fn1(5,-2));
return;//finally語句仍會執行
}
catch( ArithmeticException e)
{
System.out.println( e.toString() );
}
//捕獲自訂異常
catch (DivisorIsMinusException ex)
{
ex.printStackTrace();
System.exit(-1);//finally語句不會執行
}
//捕獲一般異常
catch (Exception e)
{
e.printStackTrace();//列印異常資訊和位置
}
finally
{
System.out.println( "finally!");
}
//*********************************************************************************
//運行結果
F:\java\JavaLesson\Lesson4>java ExcepTest
DivisorIsMinusException: Divisor can't be minus!
at Excep.division(Excep.java:52)
at Excep.fn1(Excep.java:57)
at ExcepTest.main(Excep.java:77)
finally!
(10)子類覆蓋父類的方法中拋出異常時受限制
如果父類中的方法拋出多個異常,則子類中的覆蓋方法要麼拋出相同的異常,要麼拋出異常的子類,但不能拋出新的異常(註:構造方法除外)。
以下測試代碼,子類試圖覆蓋父類方法的同時拋出新的異常.
//*****************************************************
class Excep
{
public int division(int a ,int b) throws Exception
{
try
{
return a/b;
}
catch (ArithmeticException e)
{
e.printStackTrace();//輸出異常資訊後
}
}
}
class ChildExcep extends Excep
{
//error 子類覆蓋父類方法時拋出異常受限制
public int division(int a ,int b) throws FileNotFoundException
{
return a/b;
}
}
//*********************************************************************************
以上代碼編譯會出錯,錯誤資訊如下:
Excep.java:63: 錯誤: ChildExcep中的division(int,int)無法覆蓋Excep中的division(in
t,int) public int division(int a ,int b) throws FileNotFoundException
被覆蓋的方法未拋出FileNotFoundException
(11)我們可以在方法聲明時,聲明一個不會拋出的異常,Java編譯器就會強迫方法的使用者對異常進行處理。這種方式通常應用於abstract base class和interface中。