为什么要区分编译时类型和运行时类型?
看这样一句代码: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()");
}
}
结果如下:
从代码运行结果可以看出,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";
}
运行结果如下:
下面这张图可以帮助看一下,总结的很到位: