1. 程式人生 > >Java三大特性:封裝,繼承與多型

Java三大特性:封裝,繼承與多型

(尊重勞動成果,轉載請註明出處:https://blog.csdn.net/qq_25827845/article/details/84592274冷血之心的部落格)

 

        面向物件的語言有三大特性,即封裝繼承與多型。三大特性是面向物件程式設計的核心,對於初學者務必加強對三大特性的理解與領會。在這篇部落格中,我們通過具體的案例來依次闡述封裝繼承與多型的概念與使用。

 

(一)封裝

封裝的定義

何為封裝?

        把事物抽象成一個類,將事物擁有的屬性和動作隱藏起來,只保留特定的方法與外界聯絡。當內部的邏輯發生變化時,外部呼叫不用因此而修改,它們只調用開放的介面,而不用去關心內部的實現。

封裝的好處

  • 實現了專業的分工,將處理邏輯封裝成一個方法,做到見名知其義
  • 良好的封裝能夠減少耦合
  • 隱藏資訊,實現細節

接下來我們舉兩個封裝的具體案例來進行闡述。

案例一:

package com.pak1;

public class Test {
    public static void main(String[] args) {
        Student student = new Student();
        student.name = "小明";
        student.age = 16;
        student.printStudentAge();

        Student student2 = new Student();
        student2.name = "小白";
        student2.age = 120;
        student2.printStudentAge();

    }
}

class Student {
    String name;
    int age;

    public void printStudentAge() {
        System.out.println(name + "同學的年齡:" + age);
    }
}

輸出如下:

這個時候我們可以看到輸出的小白同學的年齡120明顯不科學,所以我們需要做一些內部邏輯的處理。所以需要進行程式碼封裝,將內部邏輯進行一個隱藏。

封裝之後的程式碼如下:

package com.pak1;

public class Test {
    public static void main(String[] args) {
        Student student = new Student();
        student.setName("小明");
        student.setAge(16);
        student.printStudentAge();

        Student student2 = new Student();
        student.setName("小白");
        student.setAge(120);
        student2.printStudentAge();

    }
}

class Student {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age < 0 || age > 60)
            throw new RuntimeException("年齡設定不合法");
        this.age = age;
    }

    public void printStudentAge() {
        System.out.println(name + "同學的年齡:" + age);
    }
}

輸出結果如下:

我們將Student這個類的name和age私有化,提供了公共的get/set方法才能進行訪問,在get/set方法中我們可以對內部邏輯進行封裝處理,外部的呼叫方不必關心我們的處理邏輯。

        我們再來看另一種封裝,在一個方法中,太多的邏輯將會導致該方法違反了單一性,導致該方法的可讀性變差,這個時候我們需要將可以拆成一塊的方法進行拆分,建立相應的service或者utils來封裝該方法。舉例如下:

案例二:

package com.pak1;

public class TestFZ {
    public static void main(String[] args) {
        int score = 78;
        if (score > 100 || score < 0) {
            System.out.print("對不起,你的分數輸入有錯誤");
        } else if (score > 89) {
            System.out.print("獎勵你一臺手機");
        } else if (score > 79) {
            System.out.print("獎勵你一副耳機");
        } else if (score > 59) {
            System.out.print("獎勵你一朵小紅花");
        } else if (score > 59) {
            System.out.print("獎勵你一朵小紅花");
        }else {
            System.out.print("獎勵你一個耳光");
        }


    }
}

好了,這部分的處理邏輯很複雜,看起來可讀性也很差,這個時候我們需要封裝。封裝之後的程式碼如下:

package com.pak1;

public class TestFZ {
    public static void main(String[] args) {
        int score = 78;
        TestFZ testFZ = new TestFZ();
        testFZ.printReward(score);
    }

    public void printReward(int score) {
        if (score > 100 || score < 0) {
            System.out.print("對不起,你的分數輸入有錯誤");
        } else if (score > 89) {
            System.out.print("獎勵你一臺手機");
        } else if (score > 79) {
            System.out.print("獎勵你一副耳機");
        } else if (score > 59) {
            System.out.print("獎勵你一朵小紅花");
        } else if (score > 59) {
            System.out.print("獎勵你一朵小紅花");
        } else {
            System.out.print("獎勵你一個耳光");
        }
    }
}

這樣我們便把處理各個分數段獎勵的邏輯進行了封裝,增強了可讀性,並且外部介面不需要了解內部如何實現。

(二)繼承

繼承的概念

       繼承是面向物件的最顯著的一個特徵。繼承是從已有的類(父類或者超類)中派生出新的類(子類),新的類能吸收已有類的資料屬性和行為,並能擴充套件新的能力(方法的覆蓋/重寫)。JAVA不支援多繼承,一個類只能有一個父類。父類是子類的一般化,子類是父類的特殊化(具體化)

子類的特點:

  • 子類擁有父類非private的屬性和方法
  • 子類可以新增自己的方法和屬性,即對父類進行擴充套件
  • 子類可以重新定義父類的方法,即方法的覆蓋/重寫

建構函式:

  • 建構函式不能被繼承,子類可以通過super()顯示呼叫父類的建構函式
  • 建立子類時,編譯器會自動呼叫父類的無參建構函式
  • 如果父類沒有定義無參建構函式,子類必須在建構函式的第一行程式碼使用super()顯示呼叫

由於後邊要闡述多型,這裡我們先來介紹一個重要的概念,即方法的覆蓋/重寫

覆蓋/重寫的概念:

        當子類需要修改父類的一些方法進行擴充套件,增大功能,程式設計者常常把這樣的一種操作方法稱為重寫,也叫稱為覆蓋。

可以這麼理解:重寫就是指子類中的方法與父類中繼承的方法有完全相同的返回值型別、方法名、引數個數以及引數型別。這樣,就可以實現對父類方法的覆蓋。如果子類將父類中的方法重寫了,而我們想呼叫父類中的同名方法怎麼辦?此時,通過使用super關鍵就可以實現這個功能,super關鍵字可以從子類訪問父類中的內容,如果要訪問被重寫過的方法,使用“super.方法名(引數列表)”的形式呼叫。

下邊我們給出一個案例,子類擴充套件了父類的行為,並且重寫了父類中的方法。程式碼如下:

package com.pak1;

public class Animal {
    private String name;
    private int age;

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public int getAge() {
        return this.age;
    }

    @Override
    public String toString() {
        return "{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

class Dog extends Animal {
    // 對父類中方法的一種重寫
    public String getName() {
        return super.getName() + "Dog";
    }

    // 狗叫和狗吃飯都是在擴充套件父類中的行為
    public void voice() {
        System.out.println(super.getName() + " 汪");
    }

    public void eat() {
        System.out.println(super.getName() + "吃東西");
    }
}

class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.setName("大狗");
        dog.setAge(3);
        System.out.println(dog.toString());

        dog.eat();
        dog.voice();
        System.out.println(dog.getName()); //執行的是Dog中的getName方法
    }
}

(三)多型

多型的概念

多型的本質是:一個程式中同名的不同方法。在面向物件的程式設計中,多型主要有以下三種方式來實現。

  • 通過子類對父類方法的覆蓋來實現多型
  • 通過一個類中方法的過載來實現多型
  • 通過將子類的物件作為父類的物件實現多型。

覆蓋的概念我們在前面以及介紹了,接下來我們簡單闡述下何為過載

  • 過載是指一個類裡面(包括父類的方法)存在方法名相同,但是引數不一樣的方法,引數不一樣可以是不同的引數個數、型別或順序
  • 如果僅僅是修飾符、返回值、throw的異常 不同,那麼這是2個相同的方法

我們重點闡述第三種實現方法,即通過將子類的物件作為父類的物件實現多型

把不同的子類物件都當作父類來看,可以遮蔽不同子類物件之間的差異,寫出通用的程式碼,做出通用的程式設計,以適應需求的不斷變化。賦值之後,父物件就可以根據當前賦值給它的子物件的特性以不同的方式運作。也就是說,父親的行為像兒子,而不是兒子的行為像父親。(這句話是我理解第三種方法的關鍵,請仔細閱讀理解)

        物件的引用型變數是具有多型性的,因為一個引用型變數可以指向不同形式的物件,即:子類物件作為父類物件來使用。在這裡涉及到了向上轉型和向下轉型

向上轉型:

(時間有限,明天分析)

向下轉型:

(時間有限,明天分析)


舉一個例子,在一個單位中,有職工employee,職工中又有少數是管理者manager,管理者中又有一部分是領導。若小明是管理者manager類的物件,他也可以被看做是employee的物件,即他也可以被看做是一個職工,他同時具備著職工的所有屬性。

public class testDuoTai {
 
	public static void main(String[] args) {
		Employee emp1=new Employee("小明",23, 1000);   //emp1是Employee的物件
        System.out.println(emp1.getInfo());
        
        Employee emp2=new Manager("小明",23, 1000,5000); //注意此處emp2是Manager類的物件
        System.out.println(emp2.getInfo());
	}
 
}
//定義一個父類
class Employee
{
	String name;
	int age;
	float salary;
	
	Employee(){};
	
	Employee(String name,int age,float sal)
	{
		this.name=name;
		this.age=age;
		this.salary=sal;				
	}
	String getInfo()
	{
		return "職工姓名:"+name+"年齡:"+age+"工資:"+salary;
	}
}
//定義一個子類
class Manager extends Employee
{
	float allowance;
	Manager(String name,int age,float sal,float aa)
	{
		this.name=name;
		this.age=age;
		this.salary=sal;
		allowance=aa;
	}
}

輸出結果如下:

接下來我們再來看一個經典的多型講解案例,程式碼如下:

package com.pak2;

class A {
    public String show(D obj) {
        return ("A and D");
    }
    public String show(A obj) {
        return ("A and A");
    }
}

class B extends A {
    public String show(B obj) {
        return ("B and B");
    }
    public String show(A obj) {
        return ("B and A");
    }
}
class C extends B {}
class D extends B {}

public class Test {
    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new B();

        B b = new B();
        C c = new C();
        D d = new D();

        System.out.println("1--" + a1.show(b));
        System.out.println("2--" + a1.show(c));
        System.out.println("3--" + a1.show(d));
        System.out.println("--------------");
        System.out.println("4--" + a2.show(b));
        System.out.println("5--" + a2.show(c));
        System.out.println("6--" + a2.show(d));
        System.out.println("--------------");
        System.out.println("7--" + b.show(b));
        System.out.println("8--" + b.show(c));
        System.out.println("9--" + b.show(d));
    }
}

這段程式碼的輸出是啥呢?大家可以先分析分析,然後再看正確答案。

好了,正確答案如下:

晚上下班回家時間有限,具體分析我們明天見~

(待續........)

 

 

如果對你有幫助,記得點贊哦~歡迎大家關注我的部落格,可以進群366533258一起交流學習哦~

本群給大家提供一個學習交流的平臺,內設菜鳥Java管理員一枚、精通演算法的金牌講師一枚、Android管理員一枚、藍芽BlueTooth管理員一枚、Web前端管理一枚以及C#管理一枚。歡迎大家進來交流技術。