多态
绑定
绑定: 将一个方法调用同一个方法主体关联起来被称作绑定。
前期绑定: 若在程序执行前进行绑定,叫做前期绑定,它是面向对象语言不需要选择就默认的绑定方式。
后期绑定: 它的含义就是在运行时根据对象的类型进行绑定,也叫做动态绑定或运行时绑定。java中除了static和final外,都是动态绑定。注:final声明的方法可以有效的关闭动态绑定,编译器就可以调用更有效的代码,但是对于程序整体性能来说,并不会有什么改观。
因为是后期绑定,所以在使用时,某一个基类的引用指向了子类的对象,调用的还是子类的方法,而不是基类的。
下面看一种情况:
public class TestA {
int i;
private void fun(){System.out.println("A");}
public static void main(String[] args) {
// TODO Auto-generated method stub
TestA a=new TestB();
a.fun();
}
}
public class TestB extends TestA{
public void fun(){System.out.println("B");}
}
我们可能会想当然的认为输出是B,因为指向的对象时B,但是结果是A,由于private方法自动认为是final,而且对导出类是屏蔽的,因此,B中的fun方法是一个全新的方法,基类中的fun不能被重载,因此调用的还是基类中的fun方法。
下面改下代码,再猜猜结果:
public class TestA {
int i=1;
void fun(){System.out.println("A"+i);}
public static void main(String[] args) {
// TODO Auto-generated method stub
TestA a=new TestB();
a.fun();
System.out.println(a.i);
}
}
public class TestB extends TestA{
int i=2;
public void fun(){System.out.println("B"+i);}
}
会输出什么呢?答案是
B2
1
为啥方法里面调用的就是子类的,而直接用就是基类的?因为任何域访问操作都将由编译器解析,因此不是多态的。
构造器
如果在构造器中使用多态方法会怎么样呢?
在一般的方法内部,动态绑定是运行时才决定的,如果要调用构造器内部的一个动态绑定方法,就要用到被覆盖后的定义,然而,被覆盖的方法,在被完全构造之前就会被调用,这可能造成隐藏的错误。
下面看段代码:
public class TestA {
int i=1;
void fun(){System.out.println("A"+i);}
TestA(){
System.out.println("fun before");
fun();
System.out.println("fun after");
}
}
public class TestB extends TestA{
int i=2;
public void fun(){System.out.println("B"+i);}
TestB(int a){
i=a;
System.out.println("B end");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
new TestB(5);
}
}
其输出结果为:
fun before
B0
fun after
B end
看其结果,可以知道,在构造器被调用时,先调用的基类的构造器,在构造器中调用方法时,重载子类的方法,但是输出的“i”值既不是1也不是2,而是0,这是为什么呢?
因为在其他任何事物发生之前,将分配给对象的存储空间初始化为二进制的零,所以调用构造器时,i的值还没赋给新值,而是初始化的零。
所以编写构造器时有一条有效的准则:用尽可能简单的方法使对象进入正常的状态,如果可以的话避免调用其他方法。
向下转型
由于向上转型会丢失具体的类型信息,所以出现了向下转型,由于向下转型,我们无法知道一个“动物”他是“猫”是“狗”还是“猪”,所以必须有某种方法确定转型的正确性。
在java中所有转型都会得到检查,这种运行期间对类型进行检查行为称作“运行时类型识别”(RTTI)。