[敏捷JAVA讀書筆記-java基礎部分] 第五章

1、面向對象的三個主要特徵


面向對象的三個基本特徵:封裝、繼承、多態

java的世界中分兩類人:建立類的人,以及使用別人建立的類寫程序的人。

建立類的人應該儘量的隱藏程序實現的細節。就像前面章節中所說:隱藏該隱藏的,暴露該暴露的。

封裝就能夠作到這些~~~

繼承:

一種生產類的方式,經過繼承咱們能夠很好的解決重複代碼的問題,父類有的子類也有,子類有的父類不必定有,子類擴充了父類的屬性和行爲。

多態:

java中的方法綁定可能是晚綁定,除了final和private關鍵字修飾的方法。

晚綁定就是在運行的時候才知道是執行的哪一個對象的方法!這就帶來了一個結果----多態性。

重載和覆蓋:

同一類中若是有函數,函數名相同,參數表不一樣,則構成重載。

子類中有函數,與父類的函數同名,並且參數表也相同構成覆蓋。

轉載孫衛琴mm的文章:

方法重載

有時候,類的同一種功能有多種實現方式,到底採用哪一種實現方式,取決於調用者給定的參數。例如雜技師能訓練動物,對於不一樣的動物有不一樣的訓練方式。java

public void train(Dog dog){編程

//訓練小狗站立、排隊、作算術jvm

ide

}函數

public void train(Monkey monkey){spa

//訓練小猴敬禮、翻筋斗、騎自行車對象

繼承

}接口

再例如某個類的一個功能是比較兩個城市是否相同,一種方式是按兩個城市的名字進行比較,另外一種方式是按兩個城市的名字,以及城市所在國家的名字進行比較。ip

public boolean isSameCity (String city1,String city2){

return city1.equals(city2);

}

public boolean isSameCity(String city1,String city2,String country1,String country2){

return isSameCity(city1, city2) && country1.equals(country2);

}

再例如java.lang.Math類的max()方法可以從兩個數字中取出最大值,它有多種實現方式。

public static int max(int a,int b)

public static int max(long a,long b)

public static int max(float a,float b)

public static int max(double a,double b)

如下程序屢次調用Math類的max()方法,運行時,Java虛擬機先判斷給定參數的類型,而後決定到底執行哪一個max()方法。

//參數均爲int類型,所以執行max(int a,int b)方法

Math.max(1,2);

//參數均爲float類型,所以執行max(float a,float b)方法

Math.max(1.0F, 2.0F);

//參數中有一個是double類型,自動把另外一個參數2轉換爲double類型,

//執行max(double a,double b)方法

Math.max(1.0,2);

對於類的方法(包括從父類中繼承的方法),若是有兩個方法的方法名相同,但參數不一致,那麼能夠說,一個方法是另外一個方法的重載方法。

重載方法必須知足如下條件:

l 方法名相同。

l 方法的參數類型、個數、順序至少有一項不相同。

l 方法的返回類型能夠不相同。

l 方法的修飾符能夠不相同。

在一個類中不容許定義兩個方法名相同,而且參數簽名也徹底相同的方法。由於假如存在這樣的兩個方法,Java虛擬機在運行時就沒法決定到底執行哪一個方法。參數簽名是指參數的類型、個數和順序。

例如如下Sample類中已經定義了一個amethod()方法。

public class Sample{

public void amethod(int i, String s){}

//加入其餘方法

}

下面哪些方法能夠加入到Sample類中,而且保證編譯正確呢?

A)public void amethod(String s, int i){} (能夠)

B)public int amethod(int i, String s){return 0;} (不能夠)

C)private void amethod(int i, String mystring){} (不能夠)

D)public void Amethod(int i, String s) {} (能夠)

E)abstract void amethod(int i); (不能夠)

選項A的amethod()方法的參數順序和已有的不同,因此能做爲重載方法加入到Sample類中。

選項B和選項C的amethod()方法的參數簽名和已有的同樣,因此不能加入到Sample類中。對於選項C,儘管String類型的參數的名字和已有的不同,但比較參數簽名無須考慮參數的具體名字。

選項D的方法名爲Amethod,與已有的不同,因此能加入到Sample類中。

選項E的方法的參數數目和已有的不同,所以是一種重載方法。但因爲此處的Sample類不是抽象類,因此不能包含這個抽象方法。假如把Sample類改成抽象類,就能把這個方法加入到Sample類中了。

再例如,如下Sample類中已經定義了一個做爲程序入口的main()方法。

abstract public class Sample{

public static void main( String[] s){}

//加入其餘方法

}

下面哪些方法能夠加入到Sample類中,而且保證編譯正確呢?

A)abstract public void main(String s, int i); (能夠)

B)public final static int main( String[] s){} (不能夠)

C)private void main(int i, String mystring){} (能夠)

D)public void main( String s) throws Exception{} (能夠)

做爲程序入口的main()方法也能夠被重載。以上選項A、C和D均可以被加入到Sample類中。選項B與已有的main()方法有相同的方法簽名,所以不容許再加入到Sample類中。


 

方法覆蓋

假若有100個類,分別爲Sub1,Sub2…Sub100,它們的一個共同行爲是寫字,除了Sub1類用腳寫字 外,其他的類都用手寫字。能夠抽象出一個父類Base,它有一個表示寫字的方法write(),那麼這個方法到底如何實現呢?從儘量提升代碼可重用性的 角度看,write()方法應該採用適用於大多數子類的實現方式,這樣就能夠避免在大多數子類中重複定義write()方法。所以Base類的write ()方法的定義以下:

public void write(){ //Base類的write()方法

//用手寫字

}

因爲Sub1類的寫字的實現方式與Base類不同,所以在Sub1類中必須從新定義write()方法。

public void write(){ //Sub1類的write()方法

//用腳寫字

}

若是在子類中定義的一個方法,其名稱、返回類型及參數簽名正好與父類中某個方法的名稱、返回類型及參數簽名相匹配,那麼能夠說,子類的方法覆蓋了父類的方法。

覆蓋方法必須知足多種約束,下面分別介紹。

(1)子類方法的名稱、參數簽名和返回類型必須與父類方法的名稱、參數簽名和返回類型一致。例如如下代碼將致使編譯錯誤。

public class Base {

public void method() {…}

}

public class Sub extends Base{

public int method() { //編譯錯誤,返回類型不一致

return 0;

}

}

Java編譯器首先判斷Sub類的method()方法與Base類的method()方法的參數簽名,因爲二者一 致,所以Java編譯器認爲Sub類的method()方法試圖覆蓋父類的方法,既然如此,Sub類的method()方法就必須和被覆蓋的方法具備相同 的返回類型。

如下代碼中子類覆蓋了父類的一個方法,而後又定義了一個重載方法,這是合法的。

public class Base {

public void method() {…}

}

public class Sub extends Base {

public void method(){…} //覆蓋Base類的method()方法

public int method(int a) { //重載method()方法

return 0;

}

}

(2)子類方法不能縮小父類方法的訪問權限。例如如下代碼中子類的method()方法是私有的,父類的method()方法是公共的,子類縮小了父類方法的訪問權限,這是無效的方法覆蓋,將致使編譯錯誤。

public class Base {

public void method() {…}

}

public class Sub extends Base {

private void method() {…} //編譯錯誤,子類方法縮小了父類方法的訪問權限

}

爲何子類方法不容許縮小父類方法的訪問權限呢?這是由於假如沒有這個限制,將會與Java語言的多態機制發生衝突。例如對於如下代碼:

Base base=new Sub(); //base變量被定義爲Base類型,但引用Sub類的實例

base.method();

Java編譯器認爲以上是合法的代碼。但在運行時,根據動態綁定規則,Java虛擬機會調用base變量所引用的 Sub實例的method()方法,若是這個方法爲private類型,Java虛擬機就沒法訪問它。因此爲了不這樣的矛盾,Java語言不容許子類方 法縮小父類中被覆蓋方法的訪問權限。本章第6.6節(多態)對多態作了進一步的闡述。

(3)子類方法不能拋出比父類方法更多的異常,關於異常的概念參見第9章(異常處理)。子類方法拋出的異常必須和父類方法拋出的異常相同,或者子類方法拋出的異常類是父類方法拋出的異常類的子類。

例如,假設異常類ExceptionSub1和ExceptionSub2是ExceptionBase類的子類,則如下的代碼是合法的:

public class Base {

void method()throws ExceptionBase{}

}

public class Sub1 extends Base {

void method()throws ExceptionSub1{}

}

public class Sub2 extends Base {

void method()throws ExceptionSub1,ExceptionSub2{}

}

public class Sub3 extends Base {

void method()throws ExceptionBase{}

}

如下代碼不合法:

public class Base {

void method() throws ExceptionSub1{ }

}

public class Sub1 extends Base {

void method()throws ExceptionBase {} //編譯出錯

}

public class Sub2 extends Base {

void method()throws ExceptionSub1,ExceptionSub2 {} //編譯出錯

}

爲何子類方法不容許拋出比父類方法更多的異常呢?這是由於假如沒有這個限制,將會與Java語言的多態機制發生衝突。例如對於如下代碼:

Base base=new Sub2(); //base變量被定義爲Base類型,但引用Sub2類的實例

try{

base.method();

}catch(ExceptionSub1 e){ … } //僅僅捕獲ExceptionSub1異常

Java編譯器認爲以上是合法的代碼。但在運行時,根據動態綁定規則,Java虛擬機會調用base變量所引用的 Sub2實例的method()方法。假如Sub2實例的method()方法拋出ExceptionSub2異常,因爲該異常沒有被捕獲,將致使程序異 常終止。

(4)方法覆蓋只存在於子類和父類(包括直接父類和間接父類)之間。在同一個類中方法只能被重載,不能被覆蓋。

(5)父類的靜態方法不能被子類覆蓋爲非靜態方法。例如如下的代碼將致使編譯錯誤:

public class Base {

public static void method() { }

}

public class Sub extends Base {

public void method() { } //編譯出錯

}

(6)子類能夠定義與父類的靜態方法同名的靜態方法,以便在子類中隱藏父類的靜態方法。在編譯時,子類定義的靜態方法也必須知足與方法覆蓋相似的約束:方法的參數簽名一致,返回類型一致,不能縮小父類方法的訪問權限,不能拋出更多的異常。例如如下代碼是合法的:

public class Base {

static int method(int a) throws BaseException{ return 0; }

}

public class Sub extends Base{

public static int method(int a) throws SubException { return 0; }

}

子類隱藏父類的靜態方法和子類覆蓋父類的實例方法,這二者的區別在於:運行時,Java虛擬機把靜態方法和所屬的類 綁定,而把實例方法和所屬的實例綁定。下面舉例來解釋這一區別。在例程6-1中,Base類和它的子類即Sub類中都定義了實例方法method()和靜 態方法staticMethod()。

例程6-1 Sub.java

package hidestatic;

class Base{

void method(){ //實例方法

System.out.println("method of Base");

}

static void staticMethod(){ //靜態方法

System.out.println("static method of Base");

}

}

public class Sub extends Base{

void method(){ //覆蓋父類的實例方法method()

System.out.println("method of Sub");

}

static void staticMethod(){ //隱藏父類的靜態方法staticMethod()

System.out.println("static method of Sub");

}

public static void main(String args[]){

Base sub1=new Sub(); //sub1變量被聲明爲Base類型,引用Sub實例

sub1.method(); //打印 method of Sub

sub1.staticMethod(); //打印 static method of Base

Sub sub2=new Sub(); //sub2變量被聲明爲Sub類型,引用Sub實例

sub2.method(); //打印 method of Sub

sub2.staticMethod(); //打印 static method of Sub

}

}

運行Sub類的main()方法,程序將輸出:

method of Sub

static method of Base

method of Sub

static method of Sub

引用變量sub1和sub2都引用Sub類的實例,Java虛擬機在執行sub1.method()和sub2.method()時,都調用Sub實例的method()方法,此時父類Base的實例方法method()被子類覆蓋。

引用變量sub1被聲明爲Base類型,Java虛擬機在執行sub1. staticMethod()時,調用Base類的staticMethod()方法,可見父類Base的靜態方法staticMehtod()不能被子類覆蓋。

引用變量sub2被聲明爲Sub類型,Java虛擬機在執行sub2. staticMethod()時,調用Sub類的staticMethod()方法,Base類的staticMehtod()方法被Sub類的staticMehtod()方法隱藏。

(7)父類的非靜態方法不能被子類覆蓋爲靜態方法。例如如下代碼是不合法的:

public class Base {

void method() { }

}

public class Sub extends Base {

static void method() { } //編譯出錯

}

(8)父類的私有方法不能被子類覆蓋。例如在例程6-2中,子類Sub中定義了一個和父類Base中的方法同名、參 數簽名和返回類型一致,但訪問權限不一致的方法showMe(),父類中showMe()的訪問權限爲private,而子類中showMe()的訪問權 限爲public。儘管這在形式上和覆蓋很類似,但Java虛擬機對此有不一樣的處理機制。子類方法覆蓋父類方法的前提是,子類必須能繼承父類的特定方法, 因爲Base類的private類型的showMe()方法不能被Sub類繼承,所以Base類的showMe()方法和Sub類的showMe()方法 之間並無覆蓋關係。

例程6-2 Sub.java

package privatetest;

class Base {

private String showMe() {

return "Base";

}

public void print(){

System.out.println(showMe()); //到底調用Base類的showMe()仍是Sub類的showMe()?

}

}

public class Sub extends Base {

public String showMe(){

return "Sub";

}

public static void main(String args[]){

Sub sub=new Sub();

sub.print();

}

}

執行以上Sub類的main()方法,會打印出結果「Base」,這是由於print()方法在Base類中定義,所以print()方法會調用在Base類中定義的private類型的showMe()方法。

可是若是把Base類的showMe()方法改成public類型,其餘代碼不變:

public class Base {

public String showMe() {

return "Base";

}

}

再執行以上Sub類的main()方法的代碼,會打印出結果「Sub」,這是由於此時Sub類的showMe()方 法覆蓋了Base類的showMe()方法。所以儘管print()方法在Base類中定義,Java虛擬機仍是會調用當前Sub實例的showMe() 方法。

(9)父類的抽象方法能夠被子類經過兩種途徑覆蓋:一是子類實現父類的抽象方法;二是子類從新聲明父類的抽象方法。例如如下代碼合法:

public abstract class Base {

abstract void method1();

abstract void method2();

}

public abstract class Sub extends Base {

public void method1(){…} //實現method1()方法,而且擴大訪問權限

public abstract void method2(); //從新聲明method2()方法,僅僅擴大訪問權限,但不實現

Tips

狹義的理解,覆蓋僅指子類覆蓋父類的具體方法,即非抽象方法,在父類中提供了方法的默認實現方式,而子類採用不一樣的實現方式。在本書中,爲了敘述方便,把子類實現父類的抽象方法也看作方法覆蓋。

例如如下代碼不合法:

public abstract class Base {

abstract void method1();

abstract void method2();

}

public abstract class Sub extends Base {

private void method1(){…} //編譯出錯,不能縮小訪問權限

private abstract void method2(); //編譯出錯,不能縮小訪問權限

}

(10)父類的非抽象方法能夠被覆蓋爲抽象方法。例如如下代碼合法:

public class Base {

void method(){ }

}

public abstract class Sub extends Base {

public abstract void method(); //合法

}


圖6-2 Sub類繼承Base類

在本書提供的UML類框圖中,在子類中只會顯示子類特有的方法及覆蓋父類的方法,而不會顯示直接從父類中繼承的方 法。例如,圖6-2代表Base類是抽象類(Base名字用斜體字表示),method1()爲抽象方法(method1名字用斜體字表示), method2()和method3()爲具體方法。Sub類是Base類的子類,Sub類實現了Base類的method1()方法,覆蓋了Base類 的method2()方法,直接繼承Base類的method3()方法,此外Sub類還有本身的method4()方法。




2、面向接口編程

萬能的接口啊,偉大的接口啊!~~~
慢慢補齊

3、java垃圾回收機制

其實應該根java的初始化放在一塊兒的。
一、何謂垃圾?
一句話:若是一個對象沒有任何指向他的引用,那麼他就變爲一個垃圾,等待gc的回收
二、什麼時候回收?
不定時的,當你的內存快用完的時候,gc啓動......
三、回收以前幹嗎?
調用對象的 finalize方法,若是有的話。

4、package and classpath

package:等於給類加了一個姓:

class Hello{
......
}

這個類的名字叫Hello

package com

class Hello{
......
}

這個類的名字叫com.Hello

classpath:是一個座標。jvm會從classpath設定的位置開始尋找要找的東西。

好比D:/ 下有一個com包,裏面有個類叫Hello
package com

class Hello{
......
}

若是不設定任何classpath,則默認的classpath爲 . 一個小點 如何執行這個Hello呢? 在d:/下:java com.Hello 或者:在系統的任何地方 java -cp d:/;. com.Hello -cp = -classpath 設定classpath爲d:/,執行com.Hello的時候jvm會根據classpath先到d:/尋找com,找到了,因此執行了~~~:~
相關文章
相關標籤/搜索