【Java学习】java实例对象的编译时类型和运行时类型(更正版)

为什么要区分编译时类型和运行时类型?
看这样一句代码:Person p=new Women()(Women类继承自Person类)那么,假如p的属性修饰符为public 访问属性时得到的是Person类的属性还是Women类的属性,方法调用又是哪个类?答案:会得到Person类的属性,调用Women类的方法。为什么会这样呢?这里就需要知道什么是编译时类型和运行时类型,java程序状态会分为编译和运行这两种状态,编译时,JVM会在栈中静态创建基本数据变量,和引用数据变量的引用,回到刚刚那句代码,显然,p这个引用就是在编译时创建的,那么,p的编译时类型就是Person了,当运行这句java代码时,JVM在堆中为p新建一块内存,对应new Women()这句代码,所以p的运行时类型就是Women。有这样一条规则,对象调用编译时类型的属性和运行时类型的方法。下面先用代码表示这样的结果,然后再说明我个人的一些理解。

code1:

public class TestDemo1 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Person p=new Women();
        System.out.println("p.name:"+p.name);
        p.show();
    }

}
class Person{
    public String name;
    public Person()
    {
        name="person";
    }
    public void show()
    {
        System.out.println("class person's show()");
    }
}

class Women extends Person
{
    public String name;
    public Women()
    {
        name="women";
    }
    public void show()
    {
        System.out.println("class women's show()");
    }
}

结果如下:

【Java学习】java实例对象的编译时类型和运行时类型(更正版)_第1张图片

从代码运行结果可以看出,p调用的属性属于Person类,而调用的方法是Women类的,验证了上面的规则–对象调用编译时类型的属性和运行时类型的方法

个人理解
这里属于我个人的理解,可能有错,以后发现会重新修正
根据上述规则
根据继承的特点我们可以知道,子类会继承父类非私有的属性和方法,也就是说,父类的(非私有)属性也会出现在子类中,当然,这是显而易见的,然而关键在于,如果子类重新定义了这一属性,会怎么样呢?实际上,父类的属性并不会被覆盖,为了方便起见,我把从父类继承来的属性记为– 属性<父类> 而自己重新定义的同名属性为–属性<子类> 这样,在子类中,会有两个属性 即:属性<父类> 属性<子类>,那么如何调用呢?–解答:<>中的内容对应着调用该属性的对象的编译时类型,编译时类型为父类,调用属性<父类> ,另一种情况就是调用子类的属性了。下面用图来表示:

Class A中定义属性a,Class B继承自A,重新定义了属性a,此时,B中有编译时类型为A的属性a和编译时类型为B的属性a,Class C继承自B,自己重新定义了属性a,这时,C具有三种编译时类型的属性a。这样就好看多了,不知道应该调用的属性是哪个类的,就只要分析自己的编译时类型就可以了,调用方法其实不用在意,直接调用运行时类型的方法即可(运行时类型还是比较容易看的)。
就上图的例子我们用代码测试如下

code2
 

public class TestDemo2 {

    public static void main(String[] args) {
        // 编译时类型为A,输出应该是A
        System.out.println("编译时类型为A,输出应该是A");
        A a=new A();
        System.out.println(a.name);
        A ab=new B();
        System.out.println(ab.name);
        A ac=new C();
        System.out.println(ac.name);
        // 编译时类型为B,输出应该是B
        System.out.println("编译时类型为B,输出应该是B");
        B b=new B();
        System.out.println(b.name);
        B bc=new C();
        System.out.println(bc.name);
        // 编译时类型为C,输出应该是C
        System.out.println("编译时类型为C,输出应该是C");
        C c=new C();
        System.out.println(c.name);
    }

}
class A
{
    String name="A";
}
class B extends A
{
    String name="B";
}
class C extends B
{
    String name="C";
}

运行结果如下:

【Java学习】java实例对象的编译时类型和运行时类型(更正版)_第2张图片

下面这张图可以帮助看一下,总结的很到位:

【Java学习】java实例对象的编译时类型和运行时类型(更正版)_第3张图片

该文章非本人所写,因为原文有两处错误,作为强迫症的小白,更正了一下,作为转载,如有冒犯,请联系删除,原文链接:https://blog.csdn.net/qq_23419401/article/details/52064871

你可能感兴趣的:(java)