浅析java属性不能多态的原因

Posted OKFree

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅析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
null

发现用父对象引用子实例后,方法可以调用子类对象,但是属性并不能为子类对象赋值。
后来通过反编译后获取如下编译结果

 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

以上是关于浅析java属性不能多态的原因的主要内容,如果未能解决你的问题,请参考以下文章

java 代码片段

每个人单核苷酸多态性(SNP)形成的原因是啥?

java多态:向上转型和向下转型

java多态:向上转型和向下转型

java多态:向上转型和向下转型

Java 多态