------Java培訓、Android培訓、iOS培訓、.Net培訓、期待與您交流!
一、多態
就是一個抽象的種類可以對應多種屬於它這類的具體事物,多態就是事物存在的多種體現形態。例如:動物中有貓、狗,貓這個對象對應的類型是貓類型(貓 x=new 貓());同時貓也是動物中的一種,也可以把貓稱爲動物,可以這樣表示:動物 y=new 貓();那麼動物就是貓和狗這些具體事物中向上抽取出來的父類型,父類的引用可以指向子類型對象。
多態的體現就是父類的引用指向或接收自己的子類對象。例如:動物 y=new 貓(),表示父類類型的變量y引用指向了子類型的對象。類與類之間必須有繼承或實現 關係,並且 子類中複寫了父類中的方法,這時纔可以使用多態。
多態的出現提高了程序的可擴展性和後期可維護性。例如需要一個父類的不確定的子類對象作爲參數傳遞給一個函數時,定義時可以將這個父類的引用作爲形式參數傳遞給這個函數,而在調用時傳入一個子類對象。但在多態中父類引只能訪問父類中的成員,也就是父類引用不能調用子類中特有的方法,如代表動物的y不能調用貓對象的抓老鼠的功能。
在父類的引用指向子類對象(例:動物 y=new 貓())的過程中,是貓類型向上轉型,提升爲動物類型。而當我們需要用父類的引用調用子類的特有功能時,就需要將父類的引用向下轉型,轉型成子類類型,例如把上面的動物類型向下轉換爲貓類型(貓 c=(貓)y),這樣就可以調用抓老鼠的功能了。但注意不要將父類對象轉成子類類型。我們能轉換的是父類引用指向了自己的子類對象時,該引用可以向上提升,也可以向下轉換,多態自始自終都是子類對象在做着變化。
下面就以貓狗爲例,介紹一下多態的使用。
//定義父類Animal,表示動物
abstract class Animal
{
abstract void eat();
}
//定義Cat類,表示貓類,繼承Animal類
class Cat extends Animal
{
//複寫父類中的方法
public void eat()
{
System.out.println("吃魚");
}
//定義特有抓老鼠方法。
public void catchMouse()
{
System.out.println("抓老鼠");
}
}
//定義表示狗的Dog類,繼承Animal類
class Dog extends Animal
{
//複寫父類中的方法
public void eat()
{
System.out.println("吃骨頭");
}
//定義特有看家方法
public void guardHome()
{
System.out.println("看家");
}
}
class DuoTaiDemo
{
public static void main(String[] args)
{
//父類Animal引用指向子類Cat類對象
Animal a=new Cat();
//調用共性方法
a.eat();
Animal b=new Dog();
//將父類引用向下轉換爲Dog類型
Dog d=(Dog)b;
//調用特有方法。
d.guardHome();
//在調用function方法時,將子類對象作爲實際參數傳給函數。
function(new Cat());
}
//定義父類引用爲形式參數,調用時傳入其子類對象
public static void function(Animal a)
{
a.eat();
}
}
多態中成員的特點:(1)非靜態成員函數的特點:
在編譯時期:參閱引用型變量所屬的類中是否有調用的方法。如果有,編譯通過,如果沒有,編譯失敗。
在運行時期:參閱子類對象所屬的類中是否有調用的方法。這就是說,子類複寫了父類中的函數,在多態中,父類引用調用這個函數時,調用的是子 類中的方法。
概括:成員函數在調用時,編譯看左邊,運行看右邊。
(2)成員變量的特點
無論編譯和運行,都參考左邊(引用變量所屬的類)。如:多態中的父類引用訪問成員變量時,如果父類和子類有同名的成員變量,那麼被調用的是父 類中的成員變量。
(3)靜態成員函數的特點
無論編譯和運行,都參考左邊。因爲靜態函數可以通過類名實現調用,在內存中靜態函數隨着類的加載而存在於靜態方法區中,不需要創建對象。
下面通過一個小程序瞭解一下多態的應用,如下:
/*
需求:
電腦運行實例,
電腦運行基於主板。
*/
//定義一個接口,表示插口規則
interface PCI
{
public abstract void open();
public abstract void close();
}
//定義表示主板的類
class MainBoard
{
//定義核心功能,運行
public void run()
{
System.out.println("mainboard run ");
}
//定義擴展功能,利用PCI
public void usePCI(PCI p)//PCI p = new NetCard()//接口型引用指向自己的子類對象。
{
if(p!=null)
{
p.open();
p.close();
}
}
}
//定義網卡,實現PCI,符合規則
class NetCard implements PCI
{
//複寫PCI的功能
public void open()
{
System.out.println("netcard open");
}
public void close()
{
System.out.println("netcard close");
}
}
//定義聲卡,實現PCI,符合規則
class SoundCard implements PCI
{
public void open()
{
System.out.println("SoundCard open");
}
public void close()
{
System.out.println("SoundCard close");
}
}
class DuoTaiDemo
{
public static void main(String[] args)
{
MainBoard mb = new MainBoard();
//主板運行
mb.run();
//將網卡對象作爲實際參數傳入,實現上網功能。
mb.usePCI(new NetCard());
//將聲卡對象作爲實際參數傳入,實現發音功能。
mb.usePCI(new SoundCard());
}
}
Object類是類層次結構的根類,每個類都使用Object作爲超類(父類),所有對象(包括數組)都實現這個類的方法,所以該類中的定義的是所有對象都具備的功能。
Object中定義了用於判斷對象是否相等的equals(Object obj)方法,注意在建立對象,自定義比較方法時,只要沿襲Object類中的功能,建立自己特有的比較內容,這就是覆蓋,注意傳入的參數類型一定要是Object類型,否則不是覆蓋,而是重載。
例如:
//定義一個類,系統會默認爲Object的子類
class Demo
{
private int num;
Demo(int num)
{
this.num = num;
}
//複寫equals方法,用於定義比較內容
public boolean equals(Object obj)//Object obj = new Demo();
{
//判斷傳入的實際參數是否是本類類型
if(!(obj instanceof Demo))
return false;
//向下轉換
Demo d = (Demo)obj;
return this.num == d.num;
}
}
三、內部類
當一個類定義在另一個類的裏面,裏面哪個類就稱爲內部類,也叫內置類、嵌套類。當描述事物時,有時事物的內部還有事物,這時就可以用內部類描述內部的事物。如人可以定義成一個類,那麼人的器官屬於人這個類,又有自己特有的功能,這時就可以用內部類描述器官,定義在人這個類中。
在編譯時,如果類中有內部類,生成的.class文件會含有這樣的文件:Test$1.class。編譯器將會把內部類翻譯成用$分割外部類名和內部類名的常規類文件。
內部類可以直接訪問外部類中的成員,包括私有。是因爲內部類中持有了一個外部類的引用(外部類名.this)。而外部類要訪問內部類,則必須建立內部類的對象。
外部其他類如何訪問內部類,根據內部類的不同定義與在外部類中的不同位置,有下面幾種不同的訪問格式。
(1)當內部類定義在外部類的成員位置上,並且非私有,則在外部其他類中,可以直接建立內部類對象。
格式:外部類名.內部類名 變量名=外部類對象.內部類對象。例如:
<pre name="code" class="java">class Outer
{
//在外部類的成員位置上定義一個內部類
class Inner
{
void method()
{
System.out.println("method run");
}
}
}
class InnerDemo
{
public static void main(String[]args)
{
//建立內部類的對象
Outer.Inner oi=new Outer().new Inner();
//調用內部類的方法
oi.method();
}
}
當內部類在外部類的成員位置上時,可以被成員修飾符修飾。例如:
private:將內部類在外部類中進行封裝。
static:被static修飾的內部類就具有靜態的特性,但只能訪問外部類中的靜態成員,出現了訪問權限。
在外部其他類中,訪問static內部類的非靜態成員的格式爲:new 外部類名.內部類名().方法名();而訪問static內部類的靜態成員格式爲:外部類名.內部類名.方法名()。例如:
class Outer
{
//在外部類的成員位置上定義一個靜態內部類
static class Inner
{
//定義靜態方法
static void open()
{
System.out.println("open");
}
void method()
{
System.out.println("method run");
}
}
}
class InnerDemo
{
public static void main(String[]args)
{
//調用靜態內部內部類的非靜態方法
new Outer.Inner().method();
//調用靜態內部內部類的靜態方法
Outer.Inner.method();
}
}
當內部類中定義了靜態成員時,該內部類也必須定義成靜態內部類。當外部類中的靜態方法訪問內部類時,內部類必須是靜態內部類。在實際的開發中,內部類通常被定義成私有的。(2)內部類也可以定義在局部:內部類可以定義在外部類的方法中,創建這個類型的對象時,僅使用一次,那麼可在這個方法中定義內部類。定義在局部的內部類不可以被成員修飾符(如public、private、static等)修飾,因爲它的作用於被限定在了聲明這個局部類的代碼塊中。定義在局部的內部類可以直接訪問外部類中的成員,因爲還持有外部類中的引用。要特別注意的是,內部類不能訪問它所在的局部中非最終變量,只能訪問被final修飾的局部變量。例如:
class Outer
{
int x = 3;
void method(final int a)
{
//內部類只能訪問final修飾的局部變量
final int y = 4;
//在局部定義一個內部類
class Inner
{
void function()
{
System.out.println(a);
System.out.println(y);
}
}
//在方法內部調用內部類的方法。
new Inner().function();
}
}
class InnerClassDemo3
{
public static void main(String[] args)
{
Outer out = new Outer();
out.method(7);
out.method(8);
}
}
執行結果爲:被傳入的參數是一個常量,但當方法被調用完後,傳入的常量和方法都從棧內存中釋放了,所以當再次調用這個方法時,就可以重新傳入被final修飾的變量。
匿名內部類:
就是內部類的簡寫格式。定義一個匿名內部類,這個內部類必須繼承一個類或者實現一個接口,格式:new 父類或接口(){定義子類的內容}。可以把匿名內部類理解爲一個帶內容的匿名子類對象。注意匿名內部類中定義的方法少於3個。
匿名類的出現簡化了代碼書寫,但匿名內部類只能調用父類或接口中有的方法,不能調用自己的特有方法,不能做強轉動作,如果繼承的父類或接口中方法較多時,使用內部類閱讀性會很差,調用效率低,所以匿名內部類中定義的方法一般少於3個。
以一個匿名內部類的小程序,來表明其使用。例如:
abstract class InnerDemo
{
abstract void show();
}
class Outer
{
public void function()
{
//定義一個匿名內部類
InnerDemo d = new InnerDemo()
{ //複寫父類方法
void show()
{
System.out.println("show");
}
//定義特有方法
void run()
{
System.out.println("run");
}
};
d.show();
d.run();//編譯失敗;不能調用特有方法。
}
}
四、異常
就是程序在運行時出現不正常情況。就像人都會生病、路上有時候會堵車,問題是現實生活中一個具體的事物,可以通過Java類的形式進行描述,並封裝成對象。其實就是Java對不正常情況進行描述後的對象體現。在程序運行時各方面都可能會出現各種問題,例如用戶輸入一些非法參數、設備出現問題、存儲空間不足、代碼錯誤無法運行等。例如:
class ExceptionDemo
{
public static void main(String[]args)
{
int x=5;
int y=0;
int z=div(x,y);//由於y=0,無法運算,程序出現異常
}
public static int div(int x,int y)
{
return x/y;
}
}
Java中問題劃分爲嚴重的問題和非嚴重的問題。對於嚴重的問題,Java通過Error類進行描述。對Error一般不編寫針對性的代碼對其進行處理;對於非嚴重的問題,Java通過Exception類進行描述。對於Exception可以使用針對性的處理方式進行處理。無論Error或者Exception都有一些共性內容。比如:出現的不正常情況的信息,引發的原因等。把共性內容向上抽取,最後構成了Java的異常體系:
Throwable
|----Error
|----Exception
Error和Exception有很多子類,它們的子類名都是以父類名作爲後綴。
異常體系中的所有類以及建立的對象都具備可拋性,也就是說可以被throw和throws操作,只有異常體系具備這個特點。
Exception的大部分異常在編譯時,如果沒有處理(沒有拋沒有try),編譯會失敗。該異常被標示,代表着可以被處理;而在Exception有一個特殊的子類RuntimeException以及它的子類在編譯時不需要處理,編譯器不檢查。該異常發生時,不處理,讓程序停止,這時需要對代碼進行修正。
異常的處理:
Java中提供了特有的語句對異常進行處理,格式如下:
try
{
需要被檢測的代碼;
}
catch(異常類 變量)
{
處理異常的代碼;(處理方式)
}
finally
{
一定要執行的語句;
}
finally中的定義的通常是關閉系統資源的語句。如果要定義一些必須要執行的語句,可以用try{}finally{}格式,將語句放在finally代碼中。但如果程序前面前面執行到System.exit(0),程序會結束,fianlly不會被執行。
throw和throws:throw定義在函數內,用與拋出異常對象;throws定義在函數上,用於拋出異常類,可以拋出多個異常類,用逗號隔開。當函數內容有throw拋出異常對象,並未處理,必須要在函數上聲明,否則編譯失敗。但是函數內如果拋出的是RuntimeException異常,函數上可以不用聲明。
當在函數中出現了throw拋出異常對象,要麼在內部用try catch語句進行處理,要麼在函數上聲明讓方法調用者去處理。一般情況下函數中出現異常,並未處理,函數上需要聲明,通過throws的關鍵字聲明該功能可有會出現的異常類型。而調用者需要進行處理。可以繼續拋出或者trycatch處理;如果在函數中拋出RuntimeException異常對象。函數上不用聲明,編譯通過,而調用者也不需要進行處理。這是因爲不需要讓調用者處理,當該異常發生時,希望程序停止。因爲程序運行時出現了無法繼續運算的狀況,程序停止後,對代碼進行檢查修正。
catch中對捕獲到的異常對象的常見操作:
String getMessage();獲取異常的信息
String toString(); 獲取異常類名和異常信息
void printStackTrace();獲取異常類名和異常信息,以及異常出現在程序中的位置。JVM默認的異常處理機制,就是在調用printStackTrace方法,打印異常的堆棧的跟蹤信息。
void printStackTrace(PrintStream s)//將異常內容保存在日誌文件中,以便查閱。
多異常的處理:聲明異常時,建議聲明更爲具體的異常,這樣處理的也可以更具體。對方聲明幾個異常,就對應有幾個catch代碼塊,如果多個catch代 碼塊中的異常出現繼承關係,把處理父類異常的catch代碼塊放在最下面。否則會報錯,因爲其餘的catch語句執行不到。
下面通過一段代碼,看一下Java中對異常的處理。如下:
class Demo
{
int div(int a,int b)throws ArithmeticException,ArrayIndexOutOfBoundsException//聲明瞭該功能中存在的異常。
{
int[] arr = new int[a];
System.out.println(arr[4]);
return a/b;
}
}
class ExceptionDemo2
{
public static void main(String[] args) //throws Exception
{
Demo d = new Demo();
//檢測異常
try
{
int x = d.div(5,0);
System.out.println("x="+x);
}
//處理異常
catch (ArithmeticException e)
{
System.out.println(e.toString());
System.out.println("被零除了!!");
}
catch (ArrayIndexOutOfBoundsException e)
{
System.out.println(e.toString());
System.out.println("角標越界啦!!");
}
}
}
自定義異常:因爲項目中會出現特有的問題,而這些問題並未被java所描述並封裝對象。所以對這些特有的問題可以按照java中的面向對象思想。將特有的問題,進行自定義的異常封裝。定義類繼承Exception或者RuntimeException,這樣做是爲了讓該類也具備可拋性和操作異常的共性方法。這就叫做自定義異常。
在自定義的異常類中,要定義自定義的信息,可以使用父類已經定義好的功能。把異常信息傳遞給父類的構造函數。因爲父類中已經把異常信息的操作都完成了。所以子類只要在構造時,將異常信息通過super語句傳遞給父類即可,那麼就可以通過getMessage()等方法獲取自定義的異常信息。在自定義異常時,如果該異常發生時,無法再繼續運算,那麼可以讓自定義的異常類繼承RuntimeException。
下面的代碼就是表示一個自定義的異常類:
class MyException extends Exception
{
MyException(String msg)
{ //把信息傳遞給父類。
super(msg);
}
}
Java中的異常處理方式,可以將問題進行封裝,將正常流程語句和問題處理代碼分離,便於閱讀。在處理異常時,如果catch代碼塊中處理不了異常,但該異常並不屬於 該功能出現的異常,可以將異常轉換後,再拋出和該功能相關的異常。或者異常可以處理,但需要將異常產生後和本功能相關的問題提供出去,讓調用者知道,並處理。也可 以將捕獲異常處理後,轉換新的異常。例如:用ATM機給別人轉賬,當ATM機出現故障,可以去其他的地方轉,也可以告訴對方轉賬不成功。還要注意的是當子類覆蓋父類方
法涉及到異常時,子類拋出的異常必須是父類的異常的子類或子集,而父類或者接口沒有異常拋出時,子類的方法內容中有異常,只能trycatch處理,不能拋。下面是一個處理異常的練習,如下:
/*
畢老師用電腦上課。
但電腦可能出現的問題:電腦藍屏,電腦冒煙。
可是當電腦冒煙發生後,出現講課進度無法繼續。
出現了講師的問題:課時計劃無法完成。
*/
//定義電腦藍屏異常
class LanPingException extends Exception
{
LanPingException(String message)
{
super(message);
}
}
//定義電腦藍屏異常
class MaoYanException extends Exception
{
MaoYanException(String message)
{
super(message);
}
}
//定義老師講課異常
class NoPlanException extends Exception
{
NoPlanException(String msg)
{
super(msg);
}
}
class Computer
{
private int state = 3;
//電腦運行
public void run()throws LanPingException,MaoYanException
{
if(state==2)
throw new LanPingException("藍屏了");
if(state==3)
throw new MaoYanException("冒煙了");
System.out.println("電腦運行");
}
//電腦重啓
public void reset()
{
state = 1;
System.out.println("電腦重啓");
}
}
class Teacher
{
private String name;
private Computer comp;
Teacher(String name)
{
this.name = name;
comp = new Computer();
}
public void teach()throws NoPlanException
{
try
{
comp.run();
}
catch (LanPingException e)
{
comp.reset();
}
catch (MaoYanException e)
{
test();
throw new NoPlanException("課時無法繼續"+e.getMessage());
}
System.out.println("講課");
}
public void test()
{
System.out.println("練習");
}
}
class ExceptionTest
{
public static void main(String[] args)
{
Teacher t = new Teacher("畢老師");
try
{
t.teach();
}
catch (NoPlanException e)
{
System.out.println(e.toString());
System.out.println("換老師或者放假");
}
}
}
-------------Java培訓、Android培訓、iOS培訓、.Net培訓、期待與您交流! -------