第五章 面向對象的特點
5.1 繼承
面向對象的重要特點之一就是繼承。類的繼承使得能夠在已有的類的基礎上構造新的類,新類除了具有被繼承類的屬性和方法外,還可以根據需要添加新的屬性和方法。繼承有利于代碼的復用,通過繼承可以更有效地組織程序結構,并充分利用已有的類來完成復雜的任務,減少了代碼冗余和出錯的幾率。
?
繼承通過extends 來表示。
class A extends B 表示類A繼承了類B,B是父類,A叫子類。A類有B類的相應功能,并且還可以自己定義新的功能(方法及屬性)
?
對于父類與子類相同的方法簽名來說,叫方法的覆蓋,或重寫,以代替父類功能,但父類功能不消失。
?
super(參數) 可以調用父類的構造方法。
super.xxx(參數) 可以調用父類的xxx方法。
5.1.1 方法的重寫(覆蓋)
重新編寫與父類同名同特征值的的方法,叫方法的重寫。
重寫后父類方法推動作用。
5.2 封裝
在前邊介紹的類、變量和方法的聲明中都遇到了訪問限定符,訪問限定符用于限定類、成員變量和方法能被其他類訪問的權限,當時我們只是簡單介紹了了其功能,且只使用了public(公有的)和默認(友元的)兩種形式。
在有了包的概念之后,我們將幾種訪問限定總結如下:
1. 默認訪問限定
如果省略了訪問限定符,則系統默認為是 friendly(友元的)限定。擁有該限定的類只能被所在包內的其他類訪問。
2. public 訪問限定
由 public 限定的類為公共類。公共類可以被所有的其他類訪問。使用 public 限定符應注意以下兩點:
1)public 限定符不能用于限定內部類。
2)一個 Java 源程序文件中可以定義多個類,但最多只能有一個被限定為公共類。如果有公共類,則程序名必須與公共類同名。
3.private(私有的)訪問限定
private 限定符只能用于成員變量、方法和內部類。私有的成員只能在本類(this)中被訪問,即只能在本類的方法中由本類的對象引用。
?
4.protected(保護的)訪問限定
protected 限定符也只能用于成員變量、方法和內部類。用 protected 聲明的成員也被稱為受保護的成員,它可以被其子類(包括本包的或其他包的)訪問,也可以被本包內的其他類訪問。
?
綜合上述,以表 5-1 簡要列出各訪問限定的引用范圍。其中“√”表示可訪問,“×”表示不可訪問。
?表 5-1訪問限定的引用域和訪問范圍
? | 同一個類 | 同一個包 | 不同包的子類 | 不同包非子類 |
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
缺省 | √ | √ | × | × |
private | √ | × | × | × |
5.2.1 JavaBean
用Java語言描述的軟件組織模型就是JavaBean,它是Java中一種特殊的類,JavaBean在程序設計過程中,一般用在數據對象中,比如數據庫中的某個數據一條數據。他是在程序設計過程中,傳遞數據對象的。
特點如下:
1)有一個無參的公共構造方法。(public)
2)有屬性,屬性最好都定義為私有的(private)
3)有與屬性相對應的get和set方法。
如定義一上學生的JavaBean
public class StudentBean { ???????? private String name;//一般定義為private ???????? private int age; ???????? public JavaBeanTest() { ???????? } ???????? public int getAge() { ?????????????????? return age; ???????? } ???????? public void setAge(int age) { ?????????????????? this.age = age; ???????? } ???????? public String getName() { ?????????????????? return name; ???????? } ???????? public void setName(String name) { ?????????????????? this.name = name; ???????? } } |
public class TestJavaBean { ? ???????? public static void main(String[] args) { ?????????????????? // TODO Auto-generated method stub ?????????????????? StudentBean st = new StudentBean (); ?????????????????? st.setAge(3); ?????????????????? st.setName("張三"); ???????? } } |
?
?
5.3 抽象
5.2.1 抽象類的定義
在 Java 中所謂的抽象類,即是在類說明中用關鍵字abstract 修飾的類。
一般情況下,抽象類中可以包含一個或多個只有方法聲明而沒有定義方法體的方法。
什么時候用
當遇到這樣一些類,類中的某個或某些方法不能提供具體的實現代碼時,可將它們定義成抽象類。
格式
定義抽象類的一般格式如下:
[訪問限定符]?abstract? class?類名
{
???? //屬性說明????
…………
//抽象方法聲明
?…………
//非抽象方法定義
……………
}
?
其中,聲明抽象方法的一般格式如下:
?[訪問限定符]? abstract?數據類型? 方法名([參數表]);
注意:抽象方法只有聲明,沒有方法體,所以必須以“;”號結尾。
有關抽象方法和抽象類說明如下:
1)所謂抽象方法,是指在類中僅僅聲明了類的行為,并沒有真正實現行為的代碼。也就是說抽象方法僅僅是為所有的派生子類定義一個統一的接口,方法具體實現的程序代碼交給了各個派生子類來完成,不同的子類可以根據自身的情況以不同的程序代碼實現。
2)抽象方法只能存在于抽象類中,正像剛才所言,一個類中只要有一個方法是抽象的,則這個類就是抽象的。
3)構造方法、靜態(static)方法、最終(final)方法和私有(private)方法不能被聲明為抽象的方法。
4)一個抽象類中可以有一個或多個抽象方法,也可以沒有抽象方法。如果沒有任何抽象方法,這就意味著要避免由這個類直接創建對象。
5)抽象類只能被繼承(派生子類)而不能創建具體對象即不能被實例化。 下邊我們舉例說明抽象類的定義。
?
抽象:表示的是可以不完整,不抽象就一定完整
?
package cn.huasheng.chouxiang; ? abstract class DongWu { ? ???????? public String Name; ???????? public int age; ???????? public int sex; ? ???????? public abstract void say(); ???????? public abstract void run(); ???????? ???????? DongWu() {} ? ???????? public void setName(String name) { ?????????????????? this.Name = name; ???????? } } ? class Man extends DongWu { ???????? public void say() { ?????????????????? System.out.println("boy"); ???????? } ???????? public void run() { ? ???????? } ???????? } ? abstract class Woman extends DongWu { ???????? public void say() { ?????????????????? System.out.println("girl"); ???????? } } class Girl extends Woman{ ? ???????? @Override ???????? public void run() { ?????????????????? // TODO Auto-generated method stub ???????? } ? } ? public class PersonTest { ???????? public static void main(String[] arg) { ?????????????????? // DongWu dw = new DongWu();抽象類不可以實例化,用來定義對象,只能被繼承 ?????????????????? Man man1 = new Man(); ?????????????????? new Girl().say(); ?????????????????? man1.say(); ?????????????????? ???????? } ???????? public static void test() ???????? { ?????????????????? ???????? } } |
?
5.4 接口
在 Java 中可以把接口看作是一種特殊的抽象類,它只包含常量和和抽象方法的定義,而沒有變量和方法的實現,它用來表明一個類必須做什么,而不去規定它如何做。因此我們可以通過接口表明多個類需要實現的方法。由于接口中沒有具體的實施細節,也就沒有和存儲空間的關聯,所以可以將多個接口合并起來,由此來達到多重繼承的目的。
?
5.4.1 接口的定義
與類的結構相似,接口也分為接口聲明和接口體兩部分。
定義接口的一般格式如下:
[public] interface 接口名 [extends父接口名列表]? //接口聲明
{
//接口體開始?
//常量數據成員的聲明及定義
數據類型?? 常量名=常數值;
……………
//聲明抽象方法
返回值類型? 方法名([參數列表]) [throw異常列表] ;
…………………
} //接口體結束
對接口定義說明如下:
1) 接口的訪問限定只有 public 和缺省的。
2) interface 是聲明接口的關鍵字,與class 類似。
3) 接口的命名必須符合標識符的規定,并且接口名必須與文件名相同。
4) 允許接口的多重繼承,通過“extends父接口名列表”可以繼承多個接口。
5) 對接口體中定義的常量,系統默認為是“staticfinal”修飾的,不需要指定。
6) 對接口體中聲明的方法,系統默認為是“abstract”的,也不需要指定;對于一些特殊用途的接口,在處理過程中會遇到某些異常,可以在聲明方法時加上“throw 異常列表”,以便捕捉出現在異常列表中的異常。
5.4.2 接口的實現
所謂接口的實現,即是在實現接口的類中重寫接口中給出的所有方法,書寫方法體代碼,完成方法所規定的功能。
定義實現接口類的一般格式如下:
[訪問限定符] [修飾符] class類名 [extends 父類名]? implements 接口名列表
{??
//類體開始標志
[類的成員變量說明]? //屬性說明
[類的構造方法定義]
[類的成員方法定義]? //行為定義
/*重寫接口方法*/
接口方法定義?????? //實現接口方法
}? //類體結束標志下邊我們距離說明接口的實現。
package cn.huasheng.jiekou; class Class1{ ???????? } interface Interface1 { ???????? final int A=10; ???????? int B = 20; ???????? void fun1(); ???????? void fun2(); } interface Interface2 { ???????? final int A=10; ???????? int D = 20; ???????? void fun3(); ???????? void fun4(); } interface Interface3 extends Interface1,Interface2{ ???????? } class Inter1 extends Class1 implements Interface1,Interface2{ ???????? public void fun1() { ?????????????????? System.out.println("fun1"); ???????? } ???????? public void fun2() { ?????????????????? System.out.println("fun2"); ???????? } ???????? public void fun3() { ?????????????????? System.out.println("fun3"); ???????? } ???????? public void fun4() { ?????????????????? System.out.println("fun4"); ???????? } } public class TestInterface{ ???????? public static void main(String [] args){ ?????????????????? Inter1 i = new Inter1(); ?????????????????? i.fun1(); ?????????????????? i.fun2(); ?????????????????? i.fun3(); ?????????????????? i.fun4(); ???????? } } |
5.5 異常
5.5.1 什么是異常
異常:就是不正常,是指程序在運行時出現的不正常情況。其實就是程序中出現的問題。這個問題按照面向對象思想進行描述,并封裝成了對象。因為問題的產生有產生的原因、有問題的名稱、有問題的描述等多個屬性信息存在。當出現多屬性信息最方便的方式就是將這些信息進行封裝。異常就是java按照面向對象的思想將問題進行對象封裝。這樣就方便于操作問題以及處理問題。
?
出現的問題有很多種,比如角標越界,空指針等都是。就對這些問題進行分類。而且這些問題都有共性內容比如:每一個問題都有名稱,同時還有問題描述的信息,問題出現的位置,所以可以不斷的向上抽取。形成了異常體系。網絡突然中斷,文件不存在……
5.5.2 異常處理的5個關鍵字
try ,catch,
finally
throw, throws
異常處理格式
try{
?????????????????? //可能出異常的代碼
} catch(異常類?對象){
?????????????????? //處理該異常類型的語句
}
[finally] {
?????????????????? //一定會執行的代碼
?????????????????? //catch塊使用System.exit(1);除外
}
?
備注:當try語句塊出現異常,程序會自動跳到catch語句塊去找匹配的異常類型,并執行異常處理語句,finally語句塊是異常的統一出口。
5.5.3多異常處理
聲明異常時盡可能聲明具體異常類型,方便更好的處理;
方法聲明幾個異常就對應有幾個catch塊;
若多個catch塊中的異常出現繼承關系,父類異常catch塊放在最后;
在catch語句塊使用Exception類作為異常類型時:
所有子類實例都可以使用父類接收(向上轉型),即所有的異常對象都可以使用Exception接收;
注:在java處理多異常時捕獲小范圍的異常必須放在大范圍異常之前。
?
java7 - 同時捕獲多個異常類型
Java7之前:
try {
???????? inta =1;
???????? intb =0;
???????? intc = a / b;
???????? System.out.println(c);
} catch (NumberFormatException e)
{
??e.printStackTrace();
} catch(ArithmeticException e)
{
???????? e.printStackTrace();
}
?
Java7:將多個異常寫到了同一個catch代碼塊
try {
Integer a = 1;
Integer b = 0;
?
Integer c = a / b;
?
System.out.println(c);
?
} catch (NumberFormatException
???????? |ArithmeticException? e ) {
???????? ?e.printStackTrace();
}
5.5.4異常的分類
?
異常分類:
編譯時被檢查異常; ????? ---> Checked異常
在程序中必須使用try...catch處理;
編譯時不被檢測的異常;??????? ---> Runtime異常
可以不使用try...catch處理,但一旦出現異常就將由JVM處理。
?
Runtime異常
RuntimeException(運行時異常)是指因設計或實現方式不當而導致的問題.
說白了,就是程序員造成的,程序員小心謹慎是完全可以避免的異常.比如,事先判斷對象是否為null就可以避免NullPointerException異常,事先檢查除數不為0就可以避免ArithmeticException異常;
?
特點:
這種異常Java編譯器不會檢查它,也就說程序中出現這類異常的時候,即使不處理也沒有問題,但是一旦出現異常,程序將異常終止,若采用異常處理,則會被相應的程序執行處理。
?
Checked異常
?
除了RuntimeException以及子類,其他的Exception及其子類都是受檢查異常,我們也可以稱為非RuntimeException異常.
特點:
Java編譯器會檢查它,也就說程序中一旦出現這類異常,要么是沒有try-catch語句捕獲,或throws語句沒有聲明拋出它,編譯就不會通過,也就說這種異常,程序要求必須處理。
?
5.5.5聲明異常(throws)
在可能出現異常的方法上聲明拋出可能出現異常的類型:
聲明的時候盡可能聲明具體的異常,方便更好的處理.
當前方法不知道如何處理這種異常,可將該異常交給上一級調用者來處理(非RuntimeException類型的異常)。
方法一旦使用throws聲明拋出方法內可能出現的異常類型,該方法就可以不再過問該異常了;
一個方法調用另一個使用throws聲明拋出的方法,自己要么try...catch, 要么也throws;
?
格式:
public 返回值類型?方法名(參數列表...)throws 異常類A,異常類B... {
????????
}
?
5.5.6拋出異常throw
自行拋出一個異常對象,拋出異常類的對象;
若throw拋出的是Runtime異常:
程序可以顯示使用try...catch來捕獲并處理,也可以不管,直接交給方法調用者處理;
若throw拋出Checked異常:
要么放在try里自己處理,要么放在一個throws聲明的方法里面,交給調用者處理。
?
?
Eg:
???????? publicstatic void main(String[] args) {
?????????????????? try{
?????????????????? ????? fn1(1);
?????????????????? }catch (Exception e) { e.printStackTrace(); }
?????????????????? fn2(2);
???????? }
???????? publicstatic void fn1(int a) throws Exception{
?????????????????? if(a>0) { throw new Exception("fn1 -- a值不合法"); }
???????? }
???????? publicstatic void fn2(int a) {
?????????????????? if(a>0) { throw new RuntimeException("a值不合法"); }
???????? }
?
throws & throw
?
throws用于在方法上聲明該方法不需要處理的異常類型。
throw用于拋出具體異常類的對象。
throws與throw的區別:
thorws用在方法上,后面跟異常類名,可以是多個異常類。
throw用在方法內,后面跟異常對象,只能是一個。
?
5.5.7 finally
?
異常的統一出口:
不管try塊程序是否異常,也不管哪個catch執行,finally塊總會執行。
try語句塊或會執行的catch語句塊使用了JVM系統退出語句例外;//System.exit(1);
try塊必須和 catch塊或和finally同在,不能單獨存在,二者必須出現一個。
不要在finally中使用return 或throw語句,否則將會導致try、catch中的return或throw失效。
?
finally代碼塊只在一種情況下不執行:System.exit(0);
?
Eg:
package reviewDemo;
?
public class Demo19 {
??? public static void main(String[] args) {
?????? try{
?????????? System.out.println(17/0);
?????? }catch(Exception e){
?????????? //e.printStackTrace();
?????????? System.out.println("程序錯誤,請修正!");
?????? }finally{
?????????? System.out.println("這是finally代碼塊!");
?????? }
??? }
}
?
輸出:
程序錯誤,請修正!
這是finally代碼塊!
?
5.5.8、throw和catch同時使用
?
當異常出現在當前方法中,程序只對異常進行部分處理,還有一些處理需要在方法的調用者中才能處理完成,此時還應該再次拋出異常,這樣就可以讓方法的調用者也能捕獲到異常;
?
例如:
public static void buy(String price) throwsException {
???????? try{
?????????????????? if(price!= null)
??????????????????????????? Double.parseDouble(price);
???????? }catch (Exception e) {
?????????????????? e.printStackTrace();
?????????????????? thrownew Exception("價格不能只能是數字組成");
???????? }
}
public static void main(String[] args)? {
???????? try{
?????????????????? buy(null);
???????? }catch (Exception e) {
?????????????????? System.out.println(e.getMessage());
???????? }
}
?
?