Java中的封裝、繼承、多態


封裝

如何理解面向對象這篇文章中,提到所謂的封裝就是“功能都給你做好了,你不必去理解它是怎么寫出來的,直接使用即可。”。但你得清楚一點,那就是這句話是相對於使用者來說的,而作為開發者,封裝就得我們自己來干。

那么作為開發者,我們應該如何去封裝呢?其實你應該反過來問,他們應該如何去使用,這樣一想會簡單很多,作為使用者,自然是希望越簡單越好,也就是說,一些復雜的東西,我們不應該讓使用者去操作,那也就是說我們應該把復雜的,以及不必要的參數給它封死,不讓使用者去操作。

為什么不讓使用者去操作?

因為往往使用者是不太專業的,如果暴露太多的接口給他們,就很有可能出現一些稀奇古怪的問題,好比一個不會做水煮魚的,如果讓他去做那肯定是不好的,那怎么辦,給他買一包水煮魚的調料,讓他直接放進鍋里就好,這樣就減少了不必要的麻煩。我們封裝程序也是這樣,把復雜的代碼封死,不讓操作者去操作,以免出錯。

比如下面這個例子:

class Average{
    private int[] fractions = new int[3]; //分數
    private int average = 0; //平均分
    public void setFraction(int[] fraction){
        fractions = fraction;
    }
    public double getAverage(){
        for(int cell:fractions){
            average += cell;
        }
        return (double) average / fractions.length;
    }
}

class app{
    public static void main(String[] args){
        int[] a = {50,40,50};

        Average average = new Average();
        average.setFraction(a); //設置分數
        double n = average.getAverage(); //獲取平均分
        System.out.println(average.average); //報錯
        System.out.println(n); //46.0
    }
}
提示:Java通過private設置私有變量,通過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;
    }
}

class Cat extends Animal{
    public void voice(){
        System.out.println(super.getName() + " 喵");
    }
    public void eat(){
        System.out.println(super.getName() + " fish");
    }
}

class Dog extends Animal{
    public void voice(){
        System.out.println(super.getName() + " 汪");
    }
    public void eat(){
        System.out.println(super.getName() + " Bone");
    }
}

class app{
    public static void main(String[] args){
        Cat cat = new Cat();
        cat.setName("貓大王"); //Cat本身沒有setName方法,但是它的基類有,所以java解析器會到Cat的基類那里拿
        cat.voice();

        Dog dog = new Dog();
        dog.setName("大黑");
        dog.setAge(13);
        dog.voice();
        System.out.println(dog.getName() + dog.getAge());
    }
}

------Output------
貓大王 喵
大黑 汪
大黑13
提示:Java通過extends關鍵字來實現繼承,父類中通過private定義的變量和方法不會被繼承,也就是你不能在子類中直接操作父類通過private定義的變量以及方法。

在上面代碼中,我們可以看到,CatDog並沒有定義setNamesetAgegetNamegetAge方法,但是我們依然可以在CatDog類中使用,這是因為我們通過extends關鍵字繼承了Animal類,因此在Animal中定義的變量和方法,我們可以在子類中直接使用,除private定義的變量和方法。

反過來說,姓名和年齡是貓和狗的基本信息也是它們的共同特性。

不過得注意一下,一個類只能有一個直接父類,也就是在一個類中只能使用一次extends,雖然說它只能有一個直接父類,但是如果它的父類去繼承了其他的類,那么在父類中繼承來的變量和方法,也是能被子類所使用的。

重寫父類方法或變量

一般重寫父類方法,是因為你把貓當成是一個基類,而將狗繼承自貓類。看似這很好笑,但如果你去翻翻你的代碼,這種情況多如牛毛。當然,如果你不需要繼承,那就另說了。那么如果碰到這種情況,我們怎么重寫基類呢?很簡單,在子類中定義一個和父類中一樣的方法,如下面這樣:

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;
    }
}

class Dog extends Animal{
    public String getName(){
        return super.getName() + "2";
    }
    public void voice(){
        System.out.println(super.getName() + " 汪");
    }
    public void eat(){
        System.out.println(super.getName() + " Bone");
    }
}

class app{
    public static void main(String[] args){
        Dog dog = new Dog();
        dog.setName("大黑");
        System.out.println(dog.getName()); //執行的是Dog中的getName方法
    }
}
提示:通過super可以在子類中直接調用父類的方法以及變量,通過this調用當前類。

我覺得把這叫做重寫不太好,因為如果從本質來講,它不算重寫,只是Java尋找變量以及方法的規則罷了。Java會先看一下,自己身上有沒有某個變量或方法,如果沒有,它會接着到父類中找,如果父類中還是沒有,那么它又會到更上一級中找,如果一直找上去都沒有,那么才報錯。

在重寫父類時,需要注意一下,重寫時,方法的返回值類型必須和父類中定義的一致,比如下面這樣就會報錯

class Animal{
    private String name;

    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return this.name;
    }
}
class Dog extends Animal{
    public int getName(){ //和父類中的getName返回值不同,報錯
        return 123;
    }
}

class app{
    public static void main(String[] args){
        Dog dog = new Dog();
        System.out.println(dog.getName());
    }
}

另外還需要注意,如果重寫時,和父類中的參數不一致,則會發生意想不到的事,比如下面這個

class Animal{
    private String name;

    public void setName(String name){
        this.name = name;
    }
    public String getName(String hello){
        return this.name + hello;
    }
}

class Dog extends Animal{
    public String getName(){
        return "123";
    }
}

class app{
    public static void main(String[] args){
        Dog dog = new Dog();
        dog.setName("大黑");
        System.out.println(dog.getName("hello"));
    }
}

------Output------
大黑hello

可以看到當我們給getName傳達了參數時,執行的是Animal中的方法,而非Dog中的getName方法,也就是說如果參數不一致最后執行的可能就不是重寫的那個方法。另外也不可將父類公開的方法或變量改成私有(如將public改成private),否則也會報錯,我估計是Java有一套覆蓋規則,如果沒有達到條件就不會進行覆蓋。

多態

先來幾個例子,再講理論

class Animal{
    public int age = 5;

    public int getAge(){
        return age;
    }
    
}

class Dog extends Animal{
    public int age = 8;

    public int getAge(){
        return age + 2;
    }
}

class app{
    public static void main(String[] args){
        Animal dog = new Dog();
        System.out.println(dog.age);
    }
}
------Output------
5

Animal dog = new Dog();這么一句話,可以發現它們的類型並不一樣,但卻可以正常運行,之所以可以運行是因為,Dog類是Animal的子類,而父類是包括子類的。我們說動物,那么狗是不是就是動物中的一員呢,這是肯定的,而這里之所以如果運行也正是這個理。

不過需要注意一下,通過這種方式創建的對象,在獲取實例變量時,獲取到的是父類中的實例變量,如果是方法,則看子類中是否存在和父類中同名的方法,如果存在則使用子類中的方法,但是如果子類中有某個方法,而父類中沒有,那么就會報錯。如下這段代碼就會報錯

class Animal{
    public int age = 5;

    public int getAge(){
        return age;
    }
    
}

class Dog extends Animal{
    public int age = 8;

    public int getAge(){
        return age + 2;
    }
    public setAge(int a){
        this.age = a;
    }
}

class app{
    public static void main(String[] args){
        Animal dog = new Dog();
        System.out.println(dog.setAge(5));
    }
}

因為父類中沒有setAge這個方法,因此會報錯。

從上面這幾段代碼,可以看出使用多態也是有缺陷的,添加新方法都沒有效果,只有覆蓋才有效果。我們可以把它總結成如下這句話:

如果一個Dog類型的對象,它的引用卻是其他類型,那么則不能使用Dog類型中的方法,除非引用類中也有這個方法。

這里所謂的多態,在程序中你可以理解成,一個方法,它可以有不同的效果,那怎么實現不同的效果呢?在java中通過切換類型來實現(不一定正確)。

多態有什么用?

還是再來看幾個例子吧

class Animal{
    public int age = 5;

    public int getAge(){
        return age;
    }
    
}

class Dog extends Animal{
    public int getAge(){
        return age + 2;
    }
}

class Cat extends Animal{
    public int getAge(){
        return age + 3;
    }
}

class app{
    public static void main(String[] args){
        Animal dog = new Dog();
        Animal cat = new Cat();

        System.out.println(dog.getAge());
        System.out.println(cat.getAge());
    }
}
------Output------
7
8

可以看到,它會根據自身執行不同的方法。不過話說回來,這並不能代表什么,畢竟我們按照正常情況來創建,效果也可以一樣,不過還真有它的用武之處,比如下面這段代碼

class Animal{
    public int age = 5;

    public int getAge(){
        return age;
    }
    
}

class Dog extends Animal{
    public int getAge(){
        return age + 2;
    }
}

class Cat extends Animal{
    public int getAge(){
        return age + 3;
    }
}

class app{
    public static void main(String[] args){
        Animal[] animals = new Animal[2];
        animals[0] = new Dog();
        animals[1] = new Cat();

        System.out.println(animals[0].getAge());
        System.out.println(animals[1].getAge());
    }
}
------Output------
7
8

這段代碼和上面一段差不多,不過這段代碼中用的是一個數組,這種情況就比較適合使用多態了,不然好像沒有其他辦法來弄了吧(初學java,不太懂)。在這里面多態不僅僅只是指一個方法有不同的效果,在這里還指類型的多樣性。

好了就到這里了。

推薦閱讀

Java中函數的重載和重寫


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM