Java_面向對象

面向對象的三大特徵:封裝、繼承、多態。java

1、封裝

是指將對象的狀態信息都隱藏在對象內部,不容許外部程序直接訪問對象內部信息,而是經過該類所提供的方法來實現對內部信息的操做和訪問。封裝的優勢:ide

  • 隱藏類的實現細節
  • 良好的封裝可以減小耦合
  • 便於修改,提升代碼的可維護性
  • 可進行數據的檢查,有利於保證對象的信息完整性

2、繼承

繼承就是子類繼承父類的特徵和行爲(經過關鍵字extends),使得子類對象(實例)具備父類的實例域和方法,或子類從父類繼承方法,使得子類具備父類相同的行爲。每個子類只有一個直接的父類。this

  • 繼承的特性
    1.子類擁有父類非private的屬性,方法
    2.子類能夠擁有本身的屬性和方法,即子類能夠對父類進行擴展
    3.子類能夠用本身的方式實現父類的方法
    4.Java的繼承是單繼承,可是能夠多重繼承,單繼承就是一個子類只能繼承一個父類,多重繼承就是,例如A類繼承B類,B類繼承C類,因此按照關係就是C類是B類的父類,B類是A類的父類。
  • this 和 super
    this關鍵字:指向本身的引用。構造器中引用該構造器正在初始化的對象;在方法中引用調用該方法的對象
    super關鍵字:用來實現對父類成員的訪問,引用當前對象的父類。子類定義的實例方法中可經過super來訪問父類中被隱藏的實例變量。
    總結:this和super都不能出如今static修飾的方法中;this調用的是同一類中的重載的構造方法,super調用的是其父類的構造方法,且調用時都必須放在第一行。
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 無參構造方法
*/

3、多態

多態是同一個行爲具備多個不一樣表現形式或形態的能力。同一個實現接口,使用不一樣的實例而執行不一樣的操做。
引用變量的兩種類型:編譯時類型--由聲明該變量時使用的類型決定;運行時類型--由實際賦給該變量的對象決定。若是編譯時類型和運行時類型不一致,就可能出現多態。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

4、重載與重寫

重寫:子類包含與父類同名方法的現象,也稱爲方法的覆蓋,重寫發生在子類和父類的同名方法之間。重寫遵循的規則:方法名相同、形參列表相同;子類的返回值類型比父類的更小或相等。;子類方法聲明拋出的異常比父類的更小或相等;子類方法的訪問權限比父類的更大或相等。對象

重載:主要發生在同一個類的多個同名方法之間。重載遵循的規則:參數個數或類型不同;返回類型、修飾符也能夠不同;沒法以返回值類型做爲重載的區分標準。(典型的重載--構造器重載)blog

5、接口與抽象類

共同繼承

  • 接口和抽象類都不能實例化,都位於繼承樹的頂端,用於被其餘類實現和繼承
  • 接口和抽象均可以包含抽象方法,實現接口或繼承抽象類的普通子類都必須實現這些抽象方法。

差異接口

  • 接口裏只能包含抽象方法、靜態方法和默認方法,不能爲普通方法提供方法的實現;抽象類則徹底能夠包含普通方法。
  • 接口只能定義靜態常量,不能定義普通成員變量;抽象類裏既能夠定義普通成員遍歷,也能夠定義靜態常量。
  • 接口裏不包含構造方法;抽象類裏能夠包含構造方法--構造方法並非用於建立對象,而是讓子類調用這些構造方法來完成屬於抽象類的初始化操做。
  • 接口不能包含初始化塊;抽象類能夠包含初始化塊。
  • 一個類最多隻能有一個直接的父類,包括抽象類;但一個類能夠實現多個接口。一個接口可繼承多個接口。

注意:接口裏定義的內部類、接口、枚舉默認都採用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;
    }
}

6、繼承與組合

繼承表達式一種「是(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("我在水裏遊動...");
    }
}

7、初始化塊

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瘋狂講義》

相關文章
相關標籤/搜索