一、概念:html
將類的某些信息隱藏在類內部,不容許外部程序直接訪問,而是經過該類提供的方法來實現對隱藏信息的操做和訪問。java
二、好處:算法
三、封裝的實現步驟編程
須要注意:對封裝的屬性不必定要經過get/set方法,其餘方法也能夠對封裝的屬性進行操做。固然最好使用get/set方法,比較標準。安全
從表格能夠看出從上到下封裝性愈來愈差。ide
1.this關鍵字表明當前對象函數
this.屬性 操做當前對象的屬性學習
this.方法 調用當前對象的方法。測試
2.封裝對象的屬性的時候,常常會使用this關鍵字。this
3.當getter和setter函數參數名和成員函數名重合的時候,可使用this區別。如:
內部類( Inner Class )就是定義在另一個類裏面的類。與之對應,包含內部類的類被稱爲外部類。
那麼問題來了:那爲何要將一個類定義在另外一個類裏面呢?清清爽爽的獨立的一個類多好啊!!
答:內部類的主要做用以下:
1. 內部類提供了更好的封裝,能夠把內部類隱藏在外部類以內,不容許同一個包中的其餘類訪問該類。
2. 內部類的方法能夠直接訪問外部類的全部數據,包括私有的數據。
3. 內部類所實現的功能使用外部類一樣能夠實現,只是有時使用內部類更方便。
內部類可分爲如下幾種:
各個內部類的具體使用請轉移到另外一篇隨文:http://www.cnblogs.com/hysum/p/7101974.html
繼承是類與類的一種關係,是一種「is a」的關係。好比「狗」繼承「動物」,這裏動物類是狗類的父類或者基類,狗類是動物類的子類或者派生類。以下圖所示:
注:java中的繼承是單繼承,即一個類只有一個父類。
子類擁有父類的全部屬性和方法(除了private修飾的屬性不能擁有)從而實現了實現代碼的複用;
A、方法的重寫
子類若是對繼承的父類的方法不滿意(不適合),能夠本身編寫繼承的方法,這種方式就稱爲方法的重寫。當調用方法時會優先調用子類的方法。
重寫要注意:
a、返回值類型
b、方法名
c、參數類型及個數
都要與父類繼承的方法相同,才叫方法的重寫。
重載和重寫的區別:
方法重載:在同一個類中處理不一樣數據的多個相同方法名的多態手段。
方法重寫:相對繼承而言,子類中對父類已經存在的方法進行區別化的修改。
B、繼承的初始化順序
一、初始化父類再初始化子類
二、先執行初始化對象中屬性,再執行構造方法中的初始化。
基於上面兩點,咱們就知道實例化一個子類,java程序的執行順序是:
父類對象屬性初始化---->父類對象構造方法---->子類對象屬性初始化--->子類對象構造方法
下面有個形象的圖:
C、final關鍵字
使用final關鍵字作標識有「最終的」含義。
1. final 修飾類,則該類不容許被繼承。
2. final 修飾方法,則該方法不容許被覆蓋(重寫)。
3. final 修飾屬性,則該類的該屬性不會進行隱式的初始化,因此 該final 屬性的初始化屬性必須有值,或在構造方法中賦值(但只能選其一,且必須選其一,由於沒有默認值!),且初始化以後就不能改了,只能賦值一次。
4. final 修飾變量,則該變量的值只能賦一次值,在聲明變量的時候才能賦值,即變爲常量。
D、super關鍵字
在對象的內部使用,能夠表明父類對象。
一、訪問父類的屬性:super.age
二、訪問父類的方法:super.eat()
super的應用:
首先咱們知道子類的構造的過程中必須調用父類的構造方法。其實這個過程已經隱式地使用了咱們的super關鍵字。
這是由於若是子類的構造方法中沒有顯示調用父類的構造方法,則系統默認調用父類無參的構造方法。
那麼若是本身用super關鍵字在子類裏調用父類的構造方法,則必須在子類的構造方法中的第一行。
要注意的是:若是子類構造方法中既沒有顯示調用父類的構造方法,而父類沒有無參的構造方法,則編譯出錯。
(補充說明,雖然沒有顯示聲明父類的無參的構造方法,系統會自動默認生成一個無參構造方法,可是,若是你聲明瞭一個有參的構造方法,而沒有聲明無參的構造方法,這時系統不會動默認生成一個無參構造方法,此時稱爲父類有沒有無參的構造方法。)
E、Object類
Object類是全部類的父類,若是一個類沒有使用extends關鍵字明確標識繼承另外一個類,那麼這個類默認繼承Object類。
Object類中的方法,適合全部子類!!!
那麼Object類中有什麼主要的方法呢?
一、toString()
a. 在Object類裏面定義toString()方法的時候返回的對象的哈希code碼(對象地址字符串)。
咱們能夠發現,若是咱們直接用System.out.print(對象)輸出一個對象,則運行結果輸出的是對象的對象地址字符串,也稱爲哈希code碼。如:
哈希碼是經過哈希算法生成的一個字符串,它是用來惟一區分咱們對象的地址碼,就像咱們的身份證同樣。
b. 能夠經過重寫toString()方法表示出對象的屬性。
二、equals()
a、equals()----返回值是布爾類型。
b、默認的狀況下,比較的是對象的引用是否指向同一塊內存地址-------對象實例化時,即給對象分配內存空間,該內存空間的地址就是內存地址。使用方法如:dog.equals(dog2);
c、 若是是兩個對象,但想判斷兩個對象的屬性是否相同,則重寫equals()方法。
以Dog類爲例,重寫後的equals()方法以下(固然你能夠根據本身想比較的屬性來重寫,這裏我以age屬性是否相同來重寫equals()方法):
上面有四個判斷,它們的含義分別是:
1.判斷地址是否相同----if (this == obj),相同則返回true
2.判斷對象是否爲空----if (obj == null),爲空則返回false
3.getClass()能夠獲得類對象,判斷類型是否同樣-----if (getClass() != obj.getClass()),不同則返回false
4.判斷屬性值是否同樣----if (age != other.age),不同返回false
5.若是地址相同,對象不爲空,類型同樣,屬性值同樣則返回true
這裏要注意的是,理解obj.getClass()獲得的類對象和類的對象的區別,如下用圖形表示:
能夠看到,對於類對象咱們關心它屬於哪一個類,擁有什麼屬性和方法,好比我和你都是屬於「人」這個類對象;而類的對象則是一個類的實例化的具體的一個對象。好比我和你是兩個不一樣的人。
面向對象的最後一個特性就是多態,那麼什麼是多態呢?多態就是對象的多種形態。
java裏的多態主要表如今兩個方面:
1.引用多態
父類的引用能夠指向本類的對象;
父類的引用能夠指向子類的對象;
這兩句話是什麼意思呢,讓咱們用代碼來體驗一下,首先咱們建立一個父類Animal和一個子類Dog,在主函數裏以下所示:
注意:咱們不能使用一個子類的引用來指向父類的對象,如:。
這裏咱們必須深入理解引用多態的意義,才能更好記憶這種多態的特性。爲何子類的引用不能用來指向父類的對象呢?我在這裏通俗給你們講解一下:就以上面的例子來講,咱們能說「狗是一種動物」,可是不能說「動物是一種狗」,狗和動物是父類和子類的繼承關係,它們的從屬是不能顛倒的。當父類的引用指向子類的對象時,該對象將只是當作一種特殊的父類(裏面有重寫的方法和屬性),反之,一個子類的引用來指向父類的對象是不可行的!!
2.方法多態
根據上述建立的兩個對象:本類對象和子類對象,一樣都是父類的引用,當咱們指向不一樣的對象時,它們調用的方法也是多態的。
建立本類對象時,調用的方法爲本類方法;
建立子類對象時,調用的方法爲子類重寫的方法或者繼承的方法;
使用多態的時候要注意:若是咱們在子類中編寫一個獨有的方法(沒有繼承父類的方法),此時就不能經過父類的引用建立的子類對象來調用該方法!!!
注意: 繼承是多態的基礎。
A、引用類型轉換
瞭解了多態的含義後,咱們在平常使用多態的特性時常常須要進行引用類型轉換。
引用類型轉換:
1. 向上類型轉換(隱式/自動類型轉換),是小類型轉換到大類型。
就以上述的父類Animal和一個子類Dog來講明,當父類的引用能夠指向子類的對象時,就是向上類型轉換。如:
2. 向下類型轉換(強制類型轉換),是大類型轉換到小類型(有風險,可能出現數據溢出)。
將上述代碼再加上一行,咱們再次將父類轉換爲子類引用,那麼會出現錯誤,編譯器不容許咱們直接這麼作,雖然咱們知道這個父類引用指向的就是子類對象,可是編譯器認爲這種轉換是存在風險的。如:
那麼咱們該怎麼解決這個問題呢,咱們能夠在animal前加上(Dog)來強制類型轉換。如:
可是若是父類引用沒有指向該子類的對象,則不能向下類型轉換,雖然編譯器不會報錯,可是運行的時候程序會出錯,如:
其實這就是上面所說的子類的引用指向父類的對象,而強制轉換類型也不能轉換!!
還有一種狀況是父類的引用指向其餘子類的對象,則不能經過強制轉爲該子類的對象。如:
這是由於咱們在編譯的時候進行了強制類型轉換,編譯時的類型是咱們強制轉換的類型,因此編譯器不會報錯,而當咱們運行的時候,程序給animal開闢的是Dog類型的內存空間,這與Cat類型內存空間不匹配,因此沒法正常轉換。這兩種狀況出錯的本質是同樣的,因此咱們在使用強制類型轉換的時候要特別注意這兩種錯誤!!下面有個更安全的方式來實現向下類型轉換。。。。
3. instanceof運算符,來解決引用對象的類型,避免類型轉換的安全性問題。
instanceof是Java的一個二元操做符,和==,>,<是同一類東東。因爲它是由字母組成的,因此也是Java的保留關鍵字。它的做用是測試它左邊的對象是不是它右邊的類的實例,返回boolean類型的數據。
咱們來使用instanceof運算符來規避上面的錯誤,代碼修改以下:
利用if語句和instanceof運算符來判斷兩個對象的類型是否一致。
補充說明:在比較一個對象是否和另外一個對象屬於同一個類實例的時候,咱們一般能夠採用instanceof和getClass兩種方法經過二者是否相等來判斷,可是二者在判斷上面是有差異的。Instanceof進行類型檢查規則是:你屬於該類嗎?或者你屬於該類的派生類嗎?而經過getClass得到類型信息採用==來進行檢查是否相等的操做是嚴格的判斷,不會存在繼承方面的考慮;
總結:在寫程序的時候,若是要進行類型轉換,咱們最好使用instanceof運算符來判斷它左邊的對象是不是它右邊的類的實例,再進行強制轉換。
B、抽象類
定義:抽象類前使用abstract關鍵字修飾,則該類爲抽象類。
使用抽象類要注意如下幾點:
1. 抽象類是約束子類必須有什麼方法,而並不關注子類如何實現這些方法。
2. 抽象類應用場景:
a. 在某些狀況下,某個父類只是知道其子類應該包含怎樣的方法,但沒法準確知道這些子類如何實現這些方法(可實現動態多態)。
b. 從多個具備相同特徵的類中抽象出一個抽象類,以這個抽象類做爲子類的模板,從而避免子類設計的隨意性。
3. 抽象類定義抽象方法,只有聲明,不須要實現。抽象方法沒有方法體以分號結束,抽象方法必須用abstract關鍵字來修飾。如:
四、包含抽象方法的類是抽象類。抽象類中能夠包含普通的方法,也能夠沒有抽象方法。如:
五、抽象類不能直接建立,能夠定義引用變量來指向子類對象,來實現抽象方法。以上述的Telephone抽象類爲例:
1 public abstract class Telephone { 2 public abstract void call();//抽象方法,方法體以分號結束,只有聲明,不須要實現 3 public void message(){ 4 System.out.println("我是抽象類的普通方法"); 5 }//抽象類中包含普通的方法 6 }
1 public class Phone extends Telephone { 2 3 public void call() {//繼承抽象類的子類必須重寫抽象方法 4 // TODO Auto-generated method stub 5 System.out.println("我重寫了抽象類的方法"); 6 } 7 8 }
以上是Telephone抽象類和子類Phone的定義,下面咱們看main函數裏:
運行結果(排錯以後):
C、接口
一、概念
接口能夠理解爲一種特殊的類,由全局常量和公共的抽象方法所組成。也可理解爲一個特殊的抽象類,由於它含有抽象方法。
若是說類是一種具體實現體,而接口定義了某一批類所須要遵照的規範,接口不關心這些類的內部數據,也不關心這些類裏方法的實現細節,它只規定這些類裏必須提供的某些方法。(這裏與抽象類類似)
2.接口定義的基本語法
[修飾符] [abstract] interface 接口名 [extends父接口1,2....](多繼承){
0…n常量 (public static final)
0…n 抽象方法(public abstract)
}
其中[ ]裏的內容表示可選項,能夠寫也能夠不寫;接口中的屬性都是常量,即便定義時不添加public static final 修飾符,系統也會自動加上;接口中的方法都是抽象方法,即便定義時不添加public abstract修飾符,系統也會自動加上。
3.使用接口
一個類能夠實現一個或多個接口,實現接口使用implements關鍵字。java中一個類只能繼承一個父類,是不夠靈活的,經過實現多個接口能夠補充。
繼承父類實現接口的語法爲:
[修飾符] class 類名 extends 父類 implements 接口1,接口2...{
類體部分//若是繼承了抽象類,須要實現繼承的抽象方法;要實現接口中的抽象方法
}
注意:若是要繼承父類,繼承父類必須在實現接口以前,即extends關鍵字必須在implements關鍵字前
補充說明:一般咱們在命名一個接口時,常常以I開頭,用來區分普通的類。如:IPlayGame
如下咱們來補充在上述抽象類中的例子,咱們以前已經定義了一個抽象類Telephone和子類Phone,這裏咱們再建立一個IPlayGame的接口,而後在原來定義的兩個類稍做修改,代碼以下:
1 public interface IPlayGame { 2 public void paly();//abstract 關鍵字能夠省略,系統會自動加上 3 public String name="遊戲名字";//static final關鍵字能夠省略,系統會自動加上 4 }
1 public class Phone extends Telephone implements IPlayGame{ 2 3 public void call() {//繼承抽象類的子類必須重寫抽象方法 4 // TODO Auto-generated method stub 5 System.out.println("我重寫了抽象類的方法"); 6 } 7 8 @Override 9 public void paly() { 10 // TODO Auto-generated method stub 11 System.out.println("我重寫了接口的方法"); 12 } 13 14 }
1 public class train { 2 3 4 public static void main(String[] args) { 5 // TODO Auto-generated method stub 6 IPlayGame i=new Phone();//用接口的引用指向子類的對象 7 i.paly();//調用接口的方法 8 System.out.println(i.name);//輸出接口的常量 9 10 11 12 } 13 }
運行結果:
4.接口和匿名內部類配合使用
接口在使用過程當中還常常和匿名內部類配合使用。匿名內部類就是沒有沒名字的內部類,多用於關注實現而不關注實現類的名稱。
語法格式:
1 Interface i =new interface(){ 2 Public void method{ 3 System.out.println(「利用匿名內部類實現接口1」); 4 } 5 }; 6 i.method();
還有一種寫法:(直接把方法的調用寫在匿名內部類的最後)
1 Interface i =new interface(){ 2 Public void method{ 3 System.out.println(「利用匿名內部類實現接口1」); 4 } 5 }.method();
咱們在多態的學習過程當中認識到抽象類和接口都是實現java多態特性的關鍵部分,二者都包含抽象方法,只關注方法的聲明而不關注方法的具體實現,那麼這二者又有什麼區別呢??咱們在編寫java程序的時候又該如何抉擇呢?
參考博文:http://www.cnblogs.com/felixzh/p/5938544.html
(1)語法層面上的區別
1.一個類只能繼承一個抽象類,而一個類卻能夠實現多個接口。
2.抽象類中的成員變量能夠是各類類型的,而接口中的成員變量只能是public static final類型的;且必須給其初值,因此實現類中不能從新定義,也不能改變其值;抽象類中的變量默認是 friendly 型,其值能夠在子類中從新定義,也能夠從新賦值。
3.抽象類中能夠有非抽象方法,接口中則不能有非抽象方法。
4.接口能夠省略abstract 關鍵字,抽象類不能。
5.接口中不能含有靜態代碼塊以及靜態方法,而抽象類能夠有靜態代碼塊和靜態方法;
(2)設計層面上的區別
1)抽象類是對一種事物的抽象,即對類抽象,而接口是對行爲的抽象。抽象類是對整個類總體進行抽象,包括屬性、行爲,可是接口倒是對類局部(行爲)進行抽象。舉個簡單的例子,飛機和鳥是不一樣類的事物,可是它們都有一個共性,就是都會飛。那麼在設計的時候,能夠將飛機設計爲一個類Airplane,將鳥設計爲一個類Bird,可是不能將 飛行 這個特性也設計爲類,所以它只是一個行爲特性,並非對一類事物的抽象描述。此時能夠將 飛行 設計爲一個接口Fly,包含方法fly( ),而後Airplane和Bird分別根據本身的須要實現Fly這個接口。而後至於有不一樣種類的飛機,好比戰鬥機、民用飛機等直接繼承Airplane便可,對於鳥也是相似的,不一樣種類的鳥直接繼承Bird類便可。從這裏能夠看出,繼承是一個 "是否是"的關係,而 接口 實現則是 "有沒有"的關係。若是一個類繼承了某個抽象類,則子類一定是抽象類的種類,而接口實現則是有沒有、具有不具有的關係,好比鳥是否能飛(或者是否具有飛行這個特色),能飛行則能夠實現這個接口,不能飛行就不實現這個接口。
2)設計層面不一樣,抽象類做爲不少子類的父類,它是一種模板式設計。而接口是一種行爲規範,它是一種輻射式設計。什麼是模板式設計?最簡單例子,你們都用過ppt裏面的模板,若是用模板A設計了ppt B和ppt C,ppt B和ppt C公共的部分就是模板A了,若是它們的公共部分須要改動,則只須要改動模板A就能夠了,不須要從新對ppt B和ppt C進行改動。而輻射式設計,好比某個電梯都裝了某種報警器,一旦要更新報警器,就必須所有更新。也就是說對於抽象類,若是須要添加新的方法,能夠直接在抽象類中添加具體的實現,子類能夠不進行變動;而對於接口則不行,若是接口進行了變動,則全部實現這個接口的類都必須進行相應的改動。
下面看一個網上流傳最普遍的例子:門和警報的例子:門都有open( )和close( )兩個動做,此時咱們能夠定義經過抽象類和接口來定義這個抽象概念:
1 abstract class Door { 2 public abstract void open(); 3 public abstract void close(); 4 }
或者:
1 interface Door { 2 public abstract void open(); 3 public abstract void close(); 4 }
可是如今若是咱們須要門具備報警alarm( )的功能,那麼該如何實現?下面提供兩種思路:
1)將這三個功能都放在抽象類裏面,可是這樣一來全部繼承於這個抽象類的子類都具有了報警功能,可是有的門並不必定具有報警功能;
2)將這三個功能都放在接口裏面,須要用到報警功能的類就須要實現這個接口中的open( )和close( ),也許這個類根本就不具有open( )和close( )這兩個功能,好比火災報警器。
從這裏能夠看出, Door的open() 、close()和alarm()根本就屬於兩個不一樣範疇內的行爲,open()和close()屬於門自己固有的行爲特性,而alarm()屬於延伸的附加行爲。所以最好的解決辦法是單獨將報警設計爲一個接口,包含alarm()行爲,Door設計爲單獨的一個抽象類,包含open和close兩種行爲。再設計一個報警門繼承Door類和實現Alarm接口。
1 interface Alram { 2 void alarm(); 3 } 4 5 abstract class Door { 6 void open(); 7 void close(); 8 } 9 10 class AlarmDoor extends Door implements Alarm { 11 void oepn() { 12 //.... 13 } 14 void close() { 15 //.... 16 } 17 void alarm() { 18 //.... 19 } 20 }
終於寫完了這篇博文,java的這三大特性是面向對象編程的核心和基礎,這裏的概念的理解對實際編程有很是大的幫助,可能內容過於繁冗了,若是對內容有疑問請在下面留言或者私信個人郵箱,謝謝!!