天天看點

java中的面向對象(封裝、繼承和多态)

一些基礎

1.由于是面向對象的思想,在java中不再像cpp中一樣,将其稱之為成員函數和資料成員,相對應的叫法是對象的方法和屬性。

2.java類中定義的屬性叫成員變量,方法中定義的屬性叫局部變量。

  ①java會自動給成員變量賦一個初始值(一般為0),但不會給局部變量賦初始值,編譯時會提醒需要初始化。

  ②局部變量和成員變量同名時,局部變量具有更高的優先級。

3.在java中建立對象是需要new的,例如people是一個類:

people xiaojie = new people();

cpp中 ,例如school是一個類:

school *p = new school(參數)

school anewschool(參數)

都是可以的(不能

school aschool = new school(參數)

注:類隻有無參構造時cpp是不能用

school anewschool()

的,後面在使用anewschool的時候會報錯“表達式必須包含類類型”,隻能用

school anewschool;

4.對象名.屬性/對象名.方法調用,這個和cpp還是相同的。

5.cpp中的class一般也是沒有public等通路控制符修飾的,但java有。且在類中java并不像cpp一樣 public: protected: private: 這樣分塊來寫 而是選擇在麼每個屬性和方法前都加上通路控制符。

6.構造方法,即cpp中的構造函數,用于初始化對象。複習一下構造方法的知識:

構造方法無傳回值類型且和類名相同。無參的構造方法可預設,即系統會自動生成。但一旦自己寫了構造方法,系統就不會補那個無參的構造了。構造方法可以有private、protected、public修飾,但不能由final、static、native、abstract等修飾。

構造時有些數字可能會存在不可能為負或者應該有一個範圍的情況出現,此時可在構造方法中可以加if,如果數字超出範圍,則提醒輸入錯誤,并可自動給該屬性賦一個合理的值并作提示 (當然正确時該屬性的指派要放在else裡面 不然if執行之後不合理的值還是會進行指派)(避免有參構造方法傳值不安全)

7.修飾符順序

<修飾符>[static][final]<變量類型><變量名>

8.方法重載的原則:方法名相同,參數不同(類型、個數、順序隻要有一項不同即可認為不同)。方法的傳回類型和修飾符可以不同。 當然,調用方法要明确,不要出現兩種方法都可以比對的情況,這樣會報錯的。

static關鍵字

Java 中被 static 修飾的成員稱為靜态成員或類成員。它屬于整個類所有,而不是某個對象所有,即被類的所有對象所共享(其實通俗一點說就是!一改就全改了!而不是各個執行個體化出來的對象會在同一變量保有不同的值)。靜态成員可以使用類名直接通路,也可以使用對象名進行通路。當然,鑒于他作用的特殊性用類名直接通路較為多見。

類别 靜态對象 非靜态對象
擁有屬性 是類共同擁有的 是類各對象獨立擁有的
記憶體配置設定 記憶體空間上是固定的 空間在各個附屬類裡面配置設定
配置設定順序 先配置設定靜态對象的空間 繼而再對非靜态對象配置設定空間

A,靜态對象的資料在全局是唯一的,一改都改。如果你想要處理的東西是整個程式中唯一的,弄成靜态是個好方法。 非靜态的東西你修改以後隻是修改了他自己的資料,但是不會影響其他同類對象的資料。

B,引用友善。直接用 類名.靜态方法名 或者 類名.靜态變量名就可引用并且直接可以修改其屬性值,不用get和set方法。

與靜态變量一樣,我們也可以使用 static 修飾方法,稱為靜态方法或類方法。

A、 靜态方法中可以直接調用同類中的靜态成員,但不能直接調用非靜态成員。如果希望在靜态方法中調用非靜态變量,可以通過建立類的對象,然後通過對象來通路非靜态變量。

B、 在普通成員方法中,則可以直接通路同類的非靜态變量和靜态變量

C、 靜态方法中不能直接調用非靜态方法,需要通過對象來通路非靜态方法。

待續:關于靜态初始化塊

封裝

封裝指的就是 對資料的保護(private) 通過getXXX和setXXX去調用

eclipse中自動生成 getter和setter方法:source->generate getter and setter

包:管理java檔案 解決同名檔案沖突問題

package 包名 必須放在java源程式第一行,包名間可以用”.”号隔開、

例如:highschool包中和college包中都有StudentNumber這個類 那麼就用

highschool.StudentNumber

college.StudentNumber

來區分

使用包中的某個類

import college.StudentNumber

注:java中 包的規範命名是全小寫字母拼寫

利用* 例如

import com.college.*

可以加載該包下的所有檔案

通路修飾符 :

private:本類可以調用       預設:本類和同一包中可以調用

protected:本類和同一包中和子類可以調用 public:啥時候都能調用

類的通路修飾符可以是public或預設 ,類成員(屬性和方法)的修飾符可以是以上四種。顯然,類成員的可見性同時受類的可見性的限制。

this關鍵字代表目前對象 this.屬性 this.方法 常用于封裝對象屬性

(為了區分相同的屬性名和參數名)

pubilc void setNumber(int number)
{
    this.number=number;
}
           

繼承

java是單繼承 也就是說一個類隻有一個父類 和cpp不同

繼承使用extends關鍵字:class 子類 extends 父類(cpp中: class 子類:通路控制符 父類)

可重寫(覆寫)父類方法 調用方法時看引用的對象時父類還是子類

(隻有當傳回值類型 方法名 參數類型及個數都與父類繼承的方法相同,才叫方法的重寫 )

方法覆寫的原則:

1.子類的方法不能縮小其對應的父類方法的通路權限。

2.子類方法不能抛出比父類方法更多的異常

3.父類的靜态方法不能被子類覆寫為非靜态方法,反之亦然。

方法的覆寫隻存在于子類和父類中,若在同一個類中,隻能進行重載而不能覆寫。

初始化順序:父類屬性-父類構造方法-子類屬性-子類構造方法

子類的構造過程中必須調用其父類的構造方法(也即會自動補上一個super();) 子類構造方法中若未顯式調用父類的構造方法,則預設調用父類的無參構造方法(若父類沒有,則編譯出錯。也即若父類隻有有參構造而沒有寫無參構造,則必須顯式調用)

注:若想顯式調用父類構造方法,則必須放在構造方法的第一行

final修飾類 則該類不允許被繼承

final修飾方法 則該方法不允許被覆寫(重寫)

final修飾方法的參數 則該參數不希望被方法改變

final修飾基本類型變量 就是常量了

final修飾引用類型變量,該引用變量的值不能被改變,但其引用的對象的成員變量可以改變。

super關鍵字在對象的内部使用,可代表父類對象 用super.屬性 super.方法()來調用父類的屬性和方法。如果super未在直接父類查找到比對成員,則逐層向上到祖先類去尋找。this通路成員先在本類中查找,若未找到,則由父類逐層向上尋找。(均符合最近比對原則)

Object類是java中所有類的父類。Object類中有一些比較重要的方法。

1.toString()方法

例如:people是一個類

people yogi = new people();
System.out.println(yogi);
           

輸出結果會是yogi對象在記憶體中的位址,前面加上people類名和包名

如果想要這樣寫且直接輸出某個yogi的屬性 比如name 則覆寫toString方法

如:

public String toString()
{
    return "yogi's name:"+name;
}
           

當然這樣的覆寫 eclipse也可以完成 source->generate toString

2.equals()方法

比較對象的引用是否指向同一塊記憶體位址

people yogi = new people();

這時的yogi并不是我們建立的對象 其實僅僅是對象在記憶體中的位址 通過操作yogi 來達到操作對象的目的 是以 yogi就是對象的引用(引用了記憶體當中的一塊位址)

我們可以把比較是否同一個 重寫成 比較是否值相等(但記憶體位址不同)

equals()方法的用法:

people1.equals(people2) 若相同 則結果為true 否則為false

也不用自己寫 eclipse中可以source->Generate hashCode() and equals()(比較的屬性可選 也就是說 可以認為如果某屬性值相等 則相等)

(前面的哈希方法如果用不上就删了吧)

實作的話就是用if來判斷各個屬性值是否相等

//傳回值是布爾類型
public boolean equals(Object obj)
{
    //比較位址是否相同 若位址相同則對象一定相同
    if(this == obj)
        return true;
    //如果另一個對象空值 則一定不等
    if(obj == null)
        return false;
    //getClass()可以得到類對象(當使用new時 産生的叫類的對象 而用getClass産生的是類對象)類對象描述的是一個類的代碼資訊 比如說類中的有些什麼屬性、方法和變量名,而類的對象更關注屬性值(資料)的資訊 經常用類對象來判斷類的類型 以下判斷類的類型是否相同 若類型不同 則一定不等
    if(getClass() != obj.getClass())
        return false;
    //将傳進來的對象轉換為people類型(類型相同才能轉 而且隻有類型相同才能比較屬性值是否一樣)
    people other = (people) obj;
    //比較值是否相等(這是隻需要比較name一個屬性時的情況)
    if(name != other.name)
        return false;
    return true;
}
           

有時候看似沒有必要的繼承其實也是有好處的,比如說:可以直接用父類類型開數組 那麼數組元素就可以是各種子類類型了。

多态

多态:①引用多态 ②方法多态 (繼承是多态實作的基礎)

引用多态 父類引用可以指向其子類對象

animal obj = new dog();

方法多态是說建立子類對象的時候調用的是子類的方法(當然子類獨有而父類沒有的方法時不允許用父類的引用建立的對象調用的)

引用類型轉換

子類轉向父類是隐式的、自動的轉換(其實在上面引用多态的時候就有一個向上類型的轉換),而父類轉子類就是強制類型轉換了。不難了解,論方法和屬性,父類含于子類。

instanceof運算符可以解決引用對象的類型,避免類型轉換的安全性問題

例如:關于animal和dog

dog ww = new dog();

animal ani = ww;(自動轉換)

dog w2 = ani;

←這樣就是不行的 雖然ani的其實就和ww是一樣的 但是 還是animal類型 還是要寫成

dog w2 = (dog) ani;

但是這樣也會有問題 因為 比如說再有一個類繼承了animal 叫cat

cat miao = (cat) ani

就會出問題啦 無法将引用類型進行轉換

應該在上面這條語句前加判斷 if(ani instanceof cat)

if括号中的語句會傳回布爾值

方法多态實際指的就是方法重載和方法覆寫

方法重載又稱為靜态多态,因為其執行效果在編譯時就被固定下來了。

方法覆寫又稱動态多态,因為程式運作時可以通過替換對象來動态地改變運作效果。

抽象類 前面使用abstract關鍵字修飾(cpp中為virtual)

抽象類不能被執行個體化,隻能被繼承,且關注子類必須有哪些方法

abstract定義抽象方法 隻需聲明不需實作(寫完()之後直接以;結束 不需要大括号)

抽象類中不一定要有抽象方法,但含有抽象方法的類一定要定義為抽象類。抽象類的子類可以不實作其父類的抽象方法(也即子類依舊是抽象類)

類不能同時用final和abstract修飾,方法不能同時用static和abstract修飾

接口

接口不使用class關鍵字 而使用interface關鍵字

接口由公開的靜态方法和常量組成

文法:

[修飾符] interface 接口名 [extends 副接口,副接口,…](可多繼承)
{
    零到多個常量定義…
    零到多個抽象方法定義…
}
           

接口就是用來被繼承、被實作的,是以一般用public修飾

接口中的屬性是常量,即使定義時不添加public static final修飾符,系統也會自動加上 方法也是一樣 就算不添加public abstract修飾 也會自動加上

一個類可以實作一個或多個接口(彌補隻能繼承一個父類的不靈活)

繼承父類實作接口:(extends關鍵字一定要在implements關鍵字之前)

[修飾符] class 類名 extends 父類 implements 接口,接口…
{
}
           

一般給接口命名時在前面加一個I(大寫的i),以示和class不同。

接口名 對象名 = new 類名();和類名 對象名 = new 類名();起到的作用是一樣的

接口在使用過程中,還經常與匿名内部類配合使用(匿名内部類就是沒有名字的内部類,多關注于功能的實作而不是内部類的名稱)

例如

Iname yogi = new person(){};

(分号結束哦)括号裡面就直接寫在接口裡聲明了的一定要寫的方法。

在使用這種匿名内部類的方式時也可以直接new,也就是

new Iname(){}.方法名();