淺析java屬性不能多態的緣由

代碼定義java

class Father {

    public String name;
    public void out(){
        System.out.println("father");
    }

    public void drink(){
        System.out.println("father drink");
    }
}
public class Son extends Father {

    private String name ;

    public void out(){
        System.out.println("son");
    }


    public static void main(String[] args) {
        Father father = new Son();
        father.name = "lisi";
        System.out.println(father.name);
        father.out();
        father.drink();
        Son son =  (Son)father;
        son.out();
        System.out.println(son.name);
    }
}

結果
lisi
son
father drink
son
nullc++

發現用父對象引用子實例後,方法能夠調用子類對象,可是屬性並不能爲子類對象賦值。
後來經過反編譯後獲取以下編譯結果性能

public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: new           #5                  // class example/Son
         3: dup
         4: invokespecial #6                  // Method "<init>":()V
         7: astore_1
         8: aload_1
         9: ldc           #7                  // String lisi
        11: putfield      #8                  // Field example/Father.name:Ljava/lang/String;
        14: aload_1
        15: invokevirtual #9                  // Method example/Father.out:()V
        18: aload_1
        19: invokevirtual #10                 // Method example/Father.drink:()V
        22: aload_1
        23: checkcast     #5                  // class example/Son
        26: astore_2
        27: aload_2
        28: invokevirtual #11                 // Method out:()V
        31: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        34: aload_2
        35: getfield      #12                 // Field name:Ljava/lang/String;
        38: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        41: return
      LineNumberTable:
        line 24: 0
        line 25: 8
        line 26: 14
        line 27: 18
        line 28: 22
        line 29: 27
        line 30: 31
        line 31: 41

能夠看到11行在設置name屬性值的時候,仍然用的是父類對象,和父類對象中的name屬性,因此在強轉子類後,獲取name屬性爲空。
方法也一樣如此,第15行調用father.out方法時,也用的是父類對象,以及父類對象中的out方法,可是打印結果倒是son,是由於invokevirtural這個指令的問題,後來查閱的知,類在編譯完成後會生成一張虛方法表,存儲對應子類的方法調用,虛方法配合虛方法表完成多態的功能。
可是屬性就沒有這種功能了,屬性經過本地變量表的方法簽名,去獲取屬性和設置屬性,跟實際對象已經無關,只和左值類型有關
虛方法在使用時會消耗虛擬機性能,若是想減小虛方法的使用能夠用final修飾,或者採用靜態方法,也不要過度糾結性能,根據業務場景和性能寫出合適代碼。
java的實現多態和c++實現多態有些許類似,藉助c++代碼能夠更好的瞭解虛擬機幫咱們作了什麼操做,如下兩個代碼塊的方法,區別是virtural關鍵字
在父類方法中加入virtual關鍵字,就會調用實際類型的方法
去掉virtual關鍵字,就會根據聲明的指針類型,調用方法,不會去管實際類型指針

class Parent
{
public:
      string name = "10";
     virtual void print() {
        cout << "parent" << endl;
    }

};

class Son :  public Parent
{
public:
     string name = "20";
     virtual void print() {
        cout << "son" << endl;
    }

};

int main() {
    Parent* p = new Son();
    cout<< p->name<< endl;
    p->print();
    auto son = ((Son*)p);
    cout << son->name << endl;
    son->print();
    system("pause");
    return 1;
}
結果
10
son
20
son
class Parent
{
public:
      string name = "10";
      void print() {
        cout << "parent" << endl;
    }

};

class Son :  public Parent
{
public:
     string name = "20";
      void print() {
        cout << "son" << endl;
    }

};

int main() {
    Parent* p = new Son();
    cout<< p->name<< endl;
    p->print();
    auto son = ((Son*)p);
    cout << son->name << endl;
    son->print();
    system("pause");
    return 1;
}

10
parent
20
son
相關文章
相關標籤/搜索