面向對象的三大特徵:封裝、繼承、多態。java
是指將對象的狀態信息都隱藏在對象內部,不容許外部程序直接訪問對象內部信息,而是經過該類所提供的方法來實現對內部信息的操做和訪問。封裝的優勢:ide
繼承就是子類繼承父類的特徵和行爲(經過關鍵字extends),使得子類對象(實例)具備父類的實例域和方法,或子類從父類繼承方法,使得子類具備父類相同的行爲。每個子類只有一個直接的父類。this
public class Test003 extends TestAll{ public Test003(){ super("003","me"); System.out.println("Test003 無參構造方法"); } public static void main(String [] args){ new Test003(); } } class TestAll extends Tester{ public TestAll(String date){ System.out.println("TestAll 帶一個參數構造方法,date=" +date); } public TestAll(String date, String who){ this(date); System.out.println("TestAll 帶兩個參數構造方法,date="+ date +" who="+who); } } class Tester{ public Tester(){ System.out.println("Tester 無參構造方法"); } } // 打印結果爲: /* Tester 無參構造方法 TestAll 帶一個參數構造方法,date=003 TestAll 帶兩個參數構造方法,date=003 who=me Test003 無參構造方法 */
多態是同一個行爲具備多個不一樣表現形式或形態的能力。同一個實現接口,使用不一樣的實例而執行不一樣的操做。
引用變量的兩種類型:編譯時類型--由聲明該變量時使用的類型決定;運行時類型--由實際賦給該變量的對象決定。若是編譯時類型和運行時類型不一致,就可能出現多態。code
public class SubClass extends BaseClass { public String book = "READ BOOK"; public void sub(){ System.out.println("子類的普通方法"); } public void test(){ System.out.println("子類test方法 , 《"+this.book+"》"); } public static void main(String [] args){ BaseClass bc = new BaseClass(); // 編譯時類型與運行時類型同樣,不存在多態 System.out.println(bc.book); // 6 bc.base(); //父類普通方法 bc.test(); //父類test方法 , book=10 // bc.sub(); 編譯報錯,父類不能夠調用子類的方法 SubClass sc = new SubClass(); // 編譯時類型與運行時類型同樣,不存在多態 System.out.println(sc.book); //READ BOOK sc.base(); //父類普通方法 ; 子類能夠調用父類的方法 sc.sub(); //子類的普通方法 sc.test(); //子類test方法 , 《READ BOOK》 ; 覆蓋了父類的test方法 BaseClass b = new SubClass(); // 編譯時類型與運行時類型不同,多態發生 System.out.println(((SubClass) b).book); // READ BOOK System.out.println(b.book); // 10 b.base(); //父類普通方法 if(b instanceof BaseClass){ ((SubClass) b).sub(); // 子類的普通方法 ; 強制類型轉換 } b.test(); //子類test方法 , 《READ BOOK》 } } class BaseClass{ public int book = 10; public void base(){ System.out.println("父類普通方法"); } public void test(){ System.out.println("父類test方法 , book=" + this.book); } }
該實例中 將子類的對象直接賦值給父類的引用變量b ,無需任何轉型(向上轉型)【子類是一種特殊的父類】。引用變量在編譯階段只能調用其編譯時類型所具備的方法,但運行時則執行它運行時類型所具備的方法htm
重寫:子類包含與父類同名方法的現象,也稱爲方法的覆蓋,重寫發生在子類和父類的同名方法之間。重寫遵循的規則:方法名相同、形參列表相同;子類的返回值類型比父類的更小或相等。;子類方法聲明拋出的異常比父類的更小或相等;子類方法的訪問權限比父類的更大或相等。對象
重載:主要發生在同一個類的多個同名方法之間。重載遵循的規則:參數個數或類型不同;返回類型、修飾符也能夠不同;沒法以返回值類型做爲重載的區分標準。(典型的重載--構造器重載)blog
共同:繼承
差異:接口
注意:接口裏定義的內部類、接口、枚舉默認都採用public static兩個修飾符,無論定義時是否指定了這倆個修飾符,系統會自動使用public static對它們進行修飾。
public class Test0804 extends Speed implements Transport{ // 抽象類和接口都不能實例化,經過子類的構造方法進行初始化, public Test0804(){ //super(); //能夠隱藏 System.out.println("我是子類的無參構造方法"); } public Test0804(int i) { // 調用父類的構造方法初始化 super(i); System.out.println("我是子類的帶參構造方法"); } public static void main(String [] args){ // 接口 Test0804 t = new Test0804(); System.out.println("我是靜態變量:"+Transport.HEIGHT); Transport.tools(); t.run(); t.use(); System.out.println("----------------分割線-----------------"); // 抽象類 Test0804 t1 = new Test0804(2); Speed.is(); t1.has(); // 經過子類實例對象,調用父類的普通方法 System.out.println("私有變量的值爲:" + t1.getCount()); t1.print(); /* 結果爲: 我是無參構造方法 我是子類的無參構造方法 我是靜態變量:5 我是靜態方法 實現了抽象方法 run 實現了默認方法 use ----------------分割線----------------- 我是帶參構造方法 我是子類的帶參構造方法 抽象類的靜態方法 我是普通方法 count=2 私有變量的值爲:2 實現了抽象類的抽象方法 */ } // 重寫接口的 run 方法 @Override public void run() { System.out.println("實現了抽象方法 run"); } // 重寫接口的 use 方法 @Override public void use() { System.out.println("實現了默認方法 use"); } // 重寫抽象類的 print 方法 @Override public void print() { System.out.println("實現了抽象類的抽象方法"); } } interface Transport{ /* 接口裏不包含構造方法,不能定義普通成員變量,不能爲普通方法提供方法的實現,不能包含初始化塊 private int i =0; //編譯報錯 public Transport(){ // 編譯報錯 } public void run1(){ // 編譯報錯 System.out.println("我是普通方法"); } // 編譯報錯 { System.out.println("初始化塊"); } */ int HEIGHT = 5; // 系統會自動添加 public static final static void tools(){ // 系統會自動添加 public System.out.println("我是靜態方法"); } void run(); // 抽象方法 自動添加 public abstract default void use() { System.out.println("我是默認方法"); } } abstract class Speed{ private int count; // 顯示聲明無參構造方法 public Speed(){ System.out.println("我是無參構造方法"); } public Speed(int count){ System.out.println("我是帶參構造方法"); this.count = count; } public abstract void print(); public static void is(){ System.out.println("抽象類的靜態方法"); } public void has(){ System.out.println("我是普通方法 count="+ this.count); } public int getCount(){ return count; } }
繼承表達式一種「是(is-a)」的關係,而組合表達的是「有(has-a)」的關係。對於繼承而言,子類能夠直接得到父類的public方法,程序使用子類時,將能夠直接訪問該子類從父類那裏繼承的方法;而組合則是把舊類對象做爲新類的成員變量組合進來,用以實現新類的功能,用戶看到的是新類的方法,而不能看到被組合對象的方法。
// ------繼承------ public class InheritTest{ public static void main(String [] args){ Bird b = new Bird(); b.breath(); b.fly(); Fish f = new Fish(); f.breath(); f.swim(); } } class Animal { private void beat(){ System.out.println("心臟跳動..."); } public void breath(){ beat(); System.out.println("吸一口氣,吐一口氣,呼吸中..."); } } //繼承Animal ,直接複用父類的 breath 方法 class Bird extends Animal{ public void fly(){ System.out.println("我在天空飛翔..."); } } //繼承Animal ,直接複用父類的 breath 方法 class Fish extends Animal{ public void swim(){ System.out.println("我在水裏遊動..."); } }
// ------組合------ public class CompositeTest { public static void main(String [] args){ Bird1 b1 = new Bird1(new Animal1()); b1.breath(); b1.fly(); Fish1 f1 = new Fish1 (new Animal1()); f1.breath(); f1.swim(); } } class Animal1 { private void beat(){ System.out.println("心臟跳動..."); } public void breath(){ beat(); System.out.println("吸一口氣,吐一口氣,呼吸中..."); } } // 將原來的父類組合到原來的子類,做爲子類的一個組合成分 class Bird1{ private Animal1 a; public Bird1(Animal1 a){ this.a = a; } // 直接複用 Animal1提供的 breath 方法來實現 Bird1 的 breath 方法 public void breath(){ a.breath(); } public void fly(){ System.out.println("我在天空飛翔..."); } } // 將原來的父類組合到原來的子類,做爲子類的一個組合成分 class Fish1{ private Animal1 a; public Fish1(Animal1 a){ this.a = a; } // 直接複用 Animal1提供的 breath 方法來實現 Fish1 的 breath 方法 public void breath(){ a.breath(); } public void swim(){ System.out.println("我在水裏遊動..."); } }
Java使用構造器對單個對象進行初始化操做,使用構造器先完成整個java對象的狀態初始化,而後將java對象返回給程序。與構造器做用相似的是初始化塊,也能夠對java對象進行初始化操做。
構造器與初始化塊: 從某種程度來看,初始化塊是構造器的補充,初始化塊老是在構造器執行以前執行。
public class InitBlock { public static void main(String [] args){ // 類初始化階段(編譯階段),先執行最頂層父類的靜態初始化塊,直到執行當前類的靜態初始化塊 // 對象初始化階段(執行階段),先執行最頂層的父類初始化塊、構造方法。直到執行到當前類的初始化、構造方法。 new Leaf(); /* 結果爲: Root 的靜態初始化塊 Mid 的靜態初始化塊 Leaf 的靜態初始化塊 Root 的普通初始化塊 Root 的無參構造方法 Mid 的普通初始化塊 Mid 的無參構造方法 Mid 的帶參構造方法, 參數值:This is a test Leaf 的普通初始化塊 執行 Leaf 構造方法 */ } } class Root{ static{ System.out.println("Root 的靜態初始化塊"); } { System.out.println("Root 的普通初始化塊"); } public Root(){ System.out.println("Root 的無參構造方法"); } } class Mid extends Root{ static{ System.out.println("Mid 的靜態初始化塊"); } { System.out.println("Mid 的普通初始化塊"); } public Mid(){ System.out.println("Mid 的無參構造方法"); } public Mid(String msg){ this(); System.out.println("Mid 的帶參構造方法, 參數值:"+msg); } } class Leaf extends Mid{ static{ System.out.println("Leaf 的靜態初始化塊"); } { System.out.println("Leaf 的普通初始化塊"); } public Leaf(){ super("This is a test"); System.out.println("執行 Leaf 構造方法"); } }
注:
當JVM 第一次主動使用某一個類時,系統會在類準備階段爲該類的全部靜態成員變量分配內存;初始化階段則負責初始化這些靜態成員變量,初始化靜態成員變量就是執行類初始化代碼塊或聲明類成員變量時指定的初始值,它們執行順序與源代碼中的排列順序相同
注:講解較詳細的博客 本文大部份內容來自於《JAVA瘋狂講義》