借助动态绑定实现向上转型

 

      首先介绍一下绑定的概念:绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来。对java来说,绑定分为静态绑定(前期绑定)和动态绑定(后期绑定)

 

      静态绑定(前期绑定)是指在程序执行前方法已经被绑定,可以理解为程序编译期的绑定。只有final,static,private和构造方法是前期绑定,因为private方法是不能被继承的,所以和final方法有相同的效果,也可以说是用来有效的关闭动态绑定的,而static方法在编译时就分配了方法区的内存,所以都是静态绑定。也许还不太好理解,但是理解了动态绑定也就理解了静态绑定;

      动态绑定(后期绑定)即运行时绑定,指在运行期间判断对象的类型,并调用适当的方法。也就是说,编译器不知道对象的类型,但方法调用机制能自己去调查,找到正确的方法主体。

 

      动态绑定的过程如下:

      1.编译器检查对象的声明类型和方法名,然后的实际类型的方法表。加入A类中对象a调用fun(obj)方法,那么编译器会列出所有A类中fun()方法,和A类父类中所有fun()方法以及A父类的父类的所有方法以及更上层的。

      2.编译器检查方法调用中提供的参数类型,如果在所有fun()方法中有一个参数类型和调用提供的参数类型最为匹配,那么就调用这个方法。这个过程叫做“重载解析”。

      3.当程序运行并且使用动态绑定调用方法时,虚拟机必须调用同a所指向的对象的实际类型相匹配的方法,如果A类中有合适的方法则调用,否则在A的父类中找以及更上层。

 

      光说不练嘴把式,废话说了这么多,咱也来点实际的! Look:

 

父类

public class Father{
    public void f1(Object o){
         System.out.println("Object of Father");
    }
    public void f1(double[] d){
        System.out.println("double[] of Father");
    }
}  

 子类

public class Son extends Father{
     public void fi(Object o){
          System.out.println("Object of Son");
     }
}

 测试类

public class Demo{
	public static void main(String[] args) {
		double []d = {1.2,3.5};
		Father f = new Son();
		Father father = new Father();
		Son son = new Son();
		f.f1(d);//输出double[] of Father
		father.f1(d);//输出double[] of Father
		son.f1(d);//输出double[] of Father
	}
}

 

      f.f1(d);输出double[] of Father,这里既是动态绑定也是上转型,下面再做介绍。

      father.f1(d);输出double[] of Father,这个是肯定的,不用介绍。

      son.f1(d);输出double[] of Father,这是为什么呢,Son类里面有f1(Object o)方法,传进去的double数组也适合。这个就解释了动态绑定,调用f1()方法前,先列出Son及Son的父类Father中所有f1()方法,然后看哪个合适,其实Son和Father中的f1()方法都合适,那么JVM就会选出最合适的一个。我们可以看出不管传入什么参数,f1(Object o)方法都合适,但是f1(double[] d)不一定适合,可以说第二个方法精度更高,所以在传入一个double数组时,第二个方法更合适。我们都知道继承后,如果对象没在自己类找到方法会在父类中找,找到的话就调用父类中的方法。所以son虽然是Son类对象,但是父类Father中有更合适的方法,当然就会调用父类中更合适的方法。

      如果在本类和父类及更上层都找不到合适的方法,则编译阶段就无法通过。

 

 

      下面来介绍一下向上转型:

 

      向上转型就是创建父类引用,然后将子类对象赋给这个父类引用,再调用方法,如果子类重写了该方法,那么执行子类方法,如 果子类没有重写,那么执行父类方法。就像上面例子,f在子类中没有找到该合适方法,那么就执行父类合适的方法,如果父类中没有合适方法,那么就执行父类的父类及上层的方法。

      1.虽然Father f 这里创建的是父类的引用,但是给其赋了子类对象,所以f指的是子类对象。

      2.变量是可以被继承的,但是不能被覆盖(重写),如:

         父类中有:public String str = "父类";

         子类中有:public String str = "子类";

         测试类中有:Father f = new Son();       System.out.println(f.str);

      输出的当然是"父类",这会是很多人一下迷茫了,上面明明说了f是子类的对象,那么f.str当然调用的是子类的变量了,这里调用的为什么是父类的变量呢?接着往下看:

 

父类:

public class Person {
	public String temp = "人类临时变量";
	public void say(){
		System.out.println("人类说话方法");
	}
	public void eat(){
		System.out.println("人类吃饭方法");
	}
}

 子类:

public class Student extends Person{
	public String temp = "学生临时变量";
	public String only = "学生独有变量";
	public void say(){
		System.out.println("学生说话方法");
	}
	public void eat(){
		System.out.println("学生吃饭方法");
	}
	public void study(){
		System.out.println("学生学习方法");
	}
}

 测试类

public class Test {
	public static void main(String [] args){
		Person p = new Student();
		p.say();//输出学生说话方法
		p.eat();//输出学生吃饭方法
		p.study();
		System.out.println(p.temp);//输出人类临时变量
		System.out.println(p.only);
	}
}

      看上去这段代码毫无问题,其实呢第六行会报错:"The method study() is undefined for the type Person";第八行会报错:"only cannot be resolved or is not a field"。

      向上转型会使子类对象遗失和父类不同的属性和方法,所以会报错。子类中的属性是子类自己独有的属性,因为属性是不能被重写的(上面说了),所以在上转型中,子类丢了这个属性,所以调用的当然是父类的属性了。而方法呢,子类重写了某方法,在上转型中,对象调用该方法,当然是执行的子类中重写的方法,如果没重写,那么肯定是执行父类的方法。

      那么既然会丢失子类独有的属性和方法,这不是违背了多态么?为什么还要用呢?不用上转型,就丧失了面向抽象的编程特色降低了可扩展性(这个我还不是很清楚),其实不仅仅如此,向上转型还可以减轻编程工作量,请看下面的例子:

 

父类

public class Monitor{//显示器类
	public void displayText() {}
	public void displayGraphics() {}
}

 子类1

public class LCDMonitor extends Monitor {//液晶显示器类
	public void displayText() {
		System.out.println("LCD display text");

	}
	public void displayGraphics() {
		System.out.println("LCD display graphics");
	}
}

 子类2

public class CRTMonitor extends Monitor {//阴极射线管显示器类
	public void displayText() {
		System.out.println("CRT display text");
	}
	public void displayGraphics() {
		System.out.println("CRT display graphics");
	}
}

 如果没有上转型

public class MyMonitor {
	public static void main(String[] args) {
		run(new LCDMonitor());
		run(new CRTMonitor());
	}
	public static void run(LCDMonitor monitor) {
		monitor.displayText();
		monitor.displayGraphics();
	}
	public static void run(CRTMonitor monitor) {
		monitor.displayText();
		monitor.displayGraphics();
	}
} 

你会发现上述代码有很多重复代码,而且也不易维护。。。。如果有了上转型呢

public class MyMonitor {
	public static void main(String[] args){
		run(new LCDMonitor());//向上转型
		run(new CRTMonitor());//向上转型
	}
	public static void run(Monitor monitor){//父类实例作为参数
		monitor.displayText();
		monitor.displayGraphics();
	}
}

由此可见向上转型的强大吧。。。嘿嘿嘿

 

      如果说要用子类独有的属性和方法,那当然创建子类对象,像前面的例子:创建的是Son son = new Son();  那么要调用父类中继承来的属性,就用super.属性,这样就得到的父类中的属性。如果子类没重写父类方法,当调用的时候,自动调用的父类中的该方法,如果重写了,用super.方法,调用父类中的该方法。

你可能感兴趣的:(动态绑定)