1. 程式人生 > >Java之封裝,繼承,多型

Java之封裝,繼承,多型

一,前言

​ 今天總結一下關於Java的三大特性,封裝,繼承,多型。其實關於三大特性對於從事程式設計人員來說都是基本的了,畢竟只要接觸Java這些都是先要認識的,接下來就係統總結一下。

二,封裝

​ 先來說說特性之一:封裝

2.1,什麼是封裝

​ 封裝(Encapsulation)是面向物件方法的重要原則,就是把物件的屬性和操作(或服務)結合為一個獨立的整體,並儘可能隱藏物件的內部實現細節。

  • 將類的某些資訊隱藏在類的內部,不允許外部程式進行直接的訪問呼叫。
  • 通過該類提供的方法來實現對隱藏資訊的操作和訪問。
  • 隱藏物件的資訊。
  • 留出訪問的對外介面。

​ 舉個比較通俗的例子,比如我們的USB介面。如果我們需要外設且只需要將裝置接入USB介面中,而內部是如何工作的,對於使用者來說並不重要。而USB介面就是對外提供的訪問介面。

​ 說了這麼多,那為什麼使用封裝?

2.2,封裝的特點

  • 對成員變數實行更準確的控制。
  • 封裝可以隱藏內部程式實現的細節。
  • 良好的封裝能夠減少程式碼之間的耦合度。
  • 外部成員無法修改已封裝好的程式程式碼。
  • 方便資料檢查,有利於保護物件資訊的完整性,同時也提高程式的安全性。
  • 便於修改,體高程式碼的可維護性。

2.3,封裝的使用

  • 使用private修飾符,表示最小的訪問許可權。

  • 對成員變數的訪問,統一提供setXXX,getXXX方法。

    下面請看一個Student實體物件類:

    public class Student implements Serializable {
    
        private Long id;
        private String name;
        private Integer sex;
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getSex() {
            return sex;
        }
    
        public void setSex(Integer sex) {
            this.sex = sex;
        }
    }
    

    分析:對於上面的一個實體物件,我想大家都已經很熟悉了。將物件中的成員變數進行私有化,外部程式是無法訪問的。但是我們對外提供了訪問的方式,就是set和get方法。

    ​ 而對於這樣一個實體物件,外部程式只有賦值和獲取值的許可權,是無法對內部進行修改,因此我們還可以在內部進行一些邏輯上的判斷等,來完成我們業務上的需要。

    ​ 到這裡應該就明白封裝對於我們的程式是多麼重要。下面再來說說繼承的那點事。

    三,繼承

    3.1,什麼是繼承

    ​ 繼承就是子類繼承父類的特徵和行為,使得子類物件(例項)具有父類的例項域和方法,或子類從父類繼承方法,使得子類具有父類相同的行為。當然,如果在父類中擁有私有屬性(private修飾),則子類是不能被繼承的。

    3.2,繼承的特點

    1,關於繼承的注意事項:

    ​ 只支援單繼承,即一個子類只允許有一個父類,但是可以實現多級繼承,及子類擁有唯一的父類,而父類還可以再繼承。

    ​ 子類可以擁有父類的屬性和方法。

    ​ 子類可以擁有自己的屬性和方法。

    ​ 子類可以重寫覆蓋父類的方法。

    2,繼承的特點:

       提高程式碼複用性。
    
       父類的屬性方法可以用於子類。
    
       可以輕鬆的定義子類。
    
       使設計應用程式變得簡單。

    3.3,繼承的使用

    ​ 1,在父子類關係繼承中,如果成員變數重名,則建立子類物件時,訪問有兩種方式。

    a,直接通過子類物件訪問成員變數

    ​ 等號左邊是誰,就優先使用誰,如果沒有就向上找。

    b,間接通過成員方法訪問成員變數

    該方法屬於誰,誰就優先使用,如果沒有就向上找。

    public class FU {
        int numFU = 10;
        int num = 100;
        public void method(){
            System.out.println("父類成員變數:"+numFU);
        }
        public void methodFU(){
            System.out.println("父類成員方法!");
        }
    }
    public class Zi extends FU{
        int numZi = 20;
        int num = 200;
        public void method(){
            System.out.println("子類成員變數:"+numFU);
        }
        public void methodZi(){
            System.out.println("子類方法!");
        }
    }
    public class ExtendDemo {
        public static void main(String[] args) {
            FU fu = new FU();
            // 父類的實體物件只能呼叫父類的成員變數
            System.out.println("父類:" + fu.numFU);   // 結果:10
    
            Zi zi = new Zi();
            System.out.println("呼叫父類:" + zi.numFU); // 結果:10
            System.out.println("子類:" + zi.numZi);   // 結果:20
    
            /** 輸出結果為200,證明在重名情況下,如果子類中存在則優先使用,
             *  如果不存在則去父類查詢,但如果父類也沒有那麼編譯期就會報錯。
             */
            System.out.println(zi.num); // 結果:200
            /**
             * 通過成員方法呼叫成員變數
             */
            zi.method();    // 結果:10
        }
    }

    2,同理:

    ​ 成員方法也是一樣的,建立的物件是誰,就優先使用誰,如果沒有則直接向上找。
    ​ 注意事項:

    ​ 無論是成員變數還是成員方法,如果沒有都是向上父類中查詢,絕對不會向下查詢子類的。

    3,在繼承關係中,關於成員變數的使用:

    ​ 區域性成員變數:直接使用
    ​ 本類成員變數:this.成員變數
    ​ 父類成員變數:super.父類成員變數

    int numZi = 10;
     public void method() {
       int numMethod = 20;
       System.out.println(numMethod);  // 訪問區域性變數
       System.out.println(this.numZi); // 訪問本類成員變數
       System.out.println(super.numFu); // 訪問本類成員變數
    }

    3.4,重寫,過載

    ​ 重寫(override)

    ​ 是子類對父類的允許訪問的方法的實現過程進行重新編寫, 返回值和形參都不能改變。即外殼不變,核心重寫!

    class Animal{
       public void move(){
          System.out.println("動物行走!");
       }
    }
    
    class Dog extends Animal{
       public void move(){
          System.out.println("狗可以跑和走");
       }
    }
    
    public class TestDog{
       public static void main(String args[]){
          Animal a = new Animal(); // Animal 物件
          Animal b = new Dog(); // Dog 物件
          a.move();// 執行 Animal 類的方法
          b.move();//執行 Dog 類的方法
       }
    }

    ​ 重寫的規則:

    ​ 1,引數列表必須與被重寫方法相同。

    ​ 2,訪問許可權不能比父類中被重寫的方法的訪問許可權更低(public>protected>(default)>private)。

    ​ 3,父類成員的方法只能被它的子類重寫。

    ​ 4,被final修飾的方法不能被重寫。

    ​ 5,構造方法不能

    過載(overload)

    ​ 是在一個類裡面,方法名字相同,而引數不同。返回型別可以相同也可以不同。每個過載的方法(或者建構函式)都必須有一個獨一無二的引數型別列表。

    ​ 最常用的地方就是構造器的過載。

    public class Overloading {
        public int test(){
            System.out.println("test1");
            return 1;
        }
        public void test(int a){
            System.out.println("test2");
        }   
        //以下兩個引數型別順序不同
        public String test(int a,String s){
            System.out.println("test3");
            return "returntest3";
        }   
        public String test(String s,int a){
            System.out.println("test4");
            return "returntest4";
        }    
        public static void main(String[] args){
            Overloading o = new Overloading();
            System.out.println(o.test());
            o.test(1);
            System.out.println(o.test(1,"test3"));
            System.out.println(o.test("test4",1));
        }
    }

    ​ 過載規則:

    ​ 1,被過載的方法必須改變引數列表(引數個數或者型別不一樣)。

    ​ 2,被過載的方法可以改變返回型別。

    ​ 3,被過載的方法可以改變訪問修飾符。

    3.5,this,super關鍵字

    super()關鍵字的用法
    1,子類的成員方法中,訪問父類的成員變數。
    ​ 2,子類的成員方法中,訪問父類的成員方法。
    ​ 3,子類的構造方法中,訪問父類的構造方法。

      **this關鍵字用法:**
      1,本類成員方法中,訪問本類的成員變數。
      2,本類成員方法中,訪問本類的另一個成員方法。
          3,本類的構造方法中,訪問本類的另一個構造方法。

    ==注意:==
    this關鍵字同super一樣,必須在構造方法的第一個語句,且是唯一的。
    this與super不能同時存在。

    3.6,構造器

    ​ 繼承關係中,父子類構造方法的訪問特點:
    1,在子類構造方法中有一個預設隱含的super();呼叫,因此一定是先呼叫父類構造方法,再呼叫子類構造方法。
    2,子類構造可以通過super();呼叫父類的過載構造。(過載)
    3,super();的父類呼叫構造方法,必須在子類構造中的第一行,就是第一個;號結束的元素,並且只能呼叫一次。

    3.7,關於繼承的注意事項:

    ​ 1,Java語言是單繼承的,一個子類只能有唯一一個父類
    ​ 2,Java語言可以是多級繼承,一個子類有一個父類,一個父類還可以有一個父類。
    ​ 3,一個子類只有一個父類,但是一個父類可以有多個子類。

    四,多型

    4.1,什麼是多型

    ​ 多型是同一個行為具有多個不同表現形式或形態的能力。

    4.2,多型的特點

    ​ 1,消除型別之間的耦合關係,實現低耦合。

    ​ 2,靈活性。

    ​ 3,可擴充性。

    ​ 4,可替換性。

    4.3,多型的體現形式

    ​ 繼承

    ​ 父類引用指向子類

    ​ 重寫

    ​ 注意:在多型中,編譯看左邊,執行看右邊

    public class MultiDemo {
        public static void main(String[] args) {
            // 多型的引用,就是向上轉型
            Animals dog = new Dog();
            dog.eat();
    
            Animals cat = new Cat();
            cat.eat();
    
            // 如果要呼叫父類中沒有的方法,則要向下轉型
            Dog dogDown = (Dog)dog;
            dogDown.watchDoor();
    
        }
    }
    class Animals {
        public void eat(){
            System.out.println("動物吃飯!");
        }
    }
    class Dog extends Animals{
        public void eat(){
            System.out.println("狗在吃骨頭!");
        }
        public void watchDoor(){
            System.out.println("狗看門!");
        }
    }
    class Cat extends Animals{
        public void eat(){
            System.out.println("貓在吃魚!");
        }
    }
    

    4.4,向上轉型

      **1,格式:父類名稱 物件名 = new 子類名稱();**
      **含義:**右側建立一個子類物件,把它當作父類來使用
      **注意:**向上轉型一定是安全的
      **缺點:**一旦向上轉型,子類中原本特有的方法就不能再被呼叫了。   

# 五,介面

​ 最後,關於介面方面的細節,不同版本之間的區別。

問題描述:

​ 現在介面中需要抽取一個公有的方法,用來解決預設方法中程式碼重複的問題。
​ 但是這個共有的方法不能讓實現類實現,所以應該設定為私有化。

在JDK8之後:

​ 1,default修飾,接口裡允許定義預設的方法,但預設方法也可以覆蓋重寫。
​ 2,接口裡允許定義靜態方法。

**在JDK9之後:**

​ 1,普通私有方法,解決多個預設方法之間程式碼重複的問題。
​ 2,靜態私有化,解決多個靜態方法之間程式碼重複問題。
介面的注意事項:
​ 1,不能通過介面的實現類物件去呼叫介面中的靜態方法。
​ 正確語法:介面名稱呼叫靜態方法

**介面當中的常量的使用:**

​ 1,介面當中定義的常量:可以省略public static final。
​ 2,介面當中定義的常量:必須進行賦值。
​ 3,介面當中定義的常量:常量的名稱要全部大寫,多個名稱之間使用下劃線進行分割。

**使用介面的注意事項:**

​ 1,介面是沒有靜態程式碼塊或者構造方法
​ 2,一個類的直接父類是唯一的,但是一個類可以同時實現多個介面。
​ 3,如果實現類沒有覆蓋重寫介面中所有的抽象方法,那麼實現類就必須是一個抽象類
​ 4,如果實現類中實現多個介面,存在重複的抽象方法,那麼只需要覆蓋重寫一次即可。
​ 5,在Java中,如果實現類的直接繼承父類與實現介面發生衝突時,父類優先順序高於介面。

**介面之間的關係:**

​ 1,多個介面之間是繼承關係。
​ 2,多個父介面當中預設方法如果重複,那麼子介面必須進行預設方法的覆蓋重寫。

# 六,總結

​ 關於Java的特性基本總結完畢,當然還有一些細節沒有完善。其實對於這些Java基礎一定要掌握並熟記,因為這與我們的實際開發密切相關,好的編碼習慣才能鑄就好的產品,才能被社會認可。

​ 以上總結均是自己學習所得,如有不適之處,還請留言(郵箱)指教。

感謝閱讀!