写在前面:
最近在复习多态的时候,对程序代码运行的结果不理解,自己看了很多博客,总结了一下编译类型,运行类型,多态等的区别和使用,希望对你有所帮助。
介绍编译类型和运行类型之前,先看一下这段代码:
package multiplied;
class Father {
Father() {
System.out.println("父类构造器");
}
public void doWork() {
System.out.println("父类doWork方法");
}
}
class son extends Father {
son() {
System.out.println("子类构造器");
}
public void doWork() {
System.out.println("子类doWork方法");
}
}
public class MutipliedDemo {
public static void main(String[] args) {
Father child = new son();
child.doWork();
}
}
在测试类中:
Father child = new son();
我们声明了一个Father的引用变量,指向了son类的一个实例,我们可以看到child这个引用变量的Father类就是
编译类型,而 new son()则是
运行类型。
程序在运行时,首先会调用父类的构造器,然后再调用子类的构造器,接下来:在编译过程中,就会自动检查引用变量child的编译类型中,是否包含doWork方法,很明显,在父类中有doWork方法(如果没有会报错),但是,在JVM运行时,由于在子类中覆盖了doWork方法,所以child实际运行时,是调用了子类的doWork方法,而不是父类的,这也就是多态的一种(运行时多态)。
这里,我们为大家解释什么是编译类型,和运行类型:
编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。如果编译时类型和运行时类型不一致,会出现所谓的多态。因为子类其实是一种特殊的父类,因此java允许把一个子类对象直接赋值给一个父类引用变量,无须任何类型转换,或者被称为向上转型,由系统自动完成。引用变量在编译阶段只能调用其编译时类型所具有的方法,但运行时则执行它运行时类型所具有的方法,因此,编写Java代码时,引用变量只能调用声明该变量所用类里包含的方法(方法表)。与方法不同的是,对象的属性则不具备多态性。通过引用变量来访问其包含的实例属性时,系统总是试图访问它编译时类所定义的属性,而不是它运行时所定义的属性。 --------引自《疯狂Java讲义》
根据上面的这段话,我们就可以得出,java在声明引用变量的时候,编译类型和运行类型可以是不一致的,由于这种不一致,就造成了,编译器在编译的时候会处理引用变量的编译类型,而JVM在运行字节码文件的时候,会跟进引用变量的运行类型来调用方法,当然,对象的属性,不具备多态,也就是对象在调用属性的时候,总会访问他编译类型的属性,而不是运行类型,这一点也要我们特殊注意!
第二个问题,什么又是多态呢:
多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。
在刚刚的小代码案例中,我们可以看到父类拥有doWork方法,但在子类中,又进行了重写,我们考虑另外一种情况,如果我们定义引用变量的时候,仍然使用多态,但子类中没有覆盖父类的doWork方法,会是什么结果呢?
package multiplied;
class Father {
Father() {
System.out.println("父类构造器");
}
public void doWork() {
System.out.println("父类doWork方法");
}
}
class son extends Father {
son() {
System.out.println("子类构造器");
}
}
public class MutipliedDemo {
public static void main(String[] args) {
Father child = new son();
child.doWork();
}
}
那么这段代码的结果就是:
父类构造器
子类构造器
父类doWork方法
结果分析:前面构造函数的结果相同,然后在调用doWork方法时,我们仍然会去寻找引用变量的运行类型的类中是否含有doWork方法,但由于子类中没有,所以最后会去父类中寻找,找到后,调用父类的doWork方法。注意:这个结果容易让大家误解为,child对象仍然调用编译类型的方法,实际上并不是,而是因为运行时没有找到,自动寻找父类。
最后再补充一点,多态也分为两种,另外一种,是编译时多态,是由方法重载所导致的一种多态。