多态的产生条件:
一.使用父类类型的引用指向子类对象;
二.该引用只能调用父类中定义的方法和变量
三.如果子类中重写了父类的一个方法,那么在调用这个方法的时候,就会调用子类中的这个方法;(动态连接,动态调用)
四.变量不能被重写,如果子类中“重写”了父类中的变量,编译会报错。
多态是如何体现的:
1 接口 和 实现接口并覆盖接口中同一方法的几个不同的类体现的
2 父类 和 继承父类并覆盖父类中同一方法的几个不同子类实现的.
怎样实现动态的方法调用:
java 的这种机制遵循一个原则:当超类对象引用变量引用子类对象时,
被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,
但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。
1.如果a是类A的一个引用,那么,a可以指向类A的一个实例,或者说指向类A的一个子类。
2. 如果a是接口A的一个引用,那么,a必须指向实现了接口A的一个类的实例。
一、多态的基本概念:
一个事物在不同条件下,所表现的多种形态。
二、JAVA多态性实现机制:
SUN目前的JVM实现机制,类实例的引用就是指向一个句柄(handle)的指针,这个句柄是一对指针:
一个指针指向一张表格,实际上这个表格也有两个指针(一个指针指向一个包含了对象的方法表,另外一个指向类对象,表明该对象所属的类型);
另一个指针指向一块从java堆中为分配出来内存空间。
多态的向上转型(自动):
例如 Child child=new Child(); Father father=child; child和Father都有方法fun();子类重写了fun()方法
那么father.fun()执行的是子类的方法。
* 为什么子类的类型的对象实例可以覆给超类引用?
自动实现向上转型。通过该语句,编译器自动将子类实例向上移动,成为通用类型Father;
*为什么是子类的方法?
在运行时期,将根据father这个对象引用实际的类型来获取对应的方法。所以才有多态性。一个基类的对象引用,被赋予不同的子类对象引用,执行该方法时,将表现出不同的行为。
2、不能把父类对象引用赋给子类对象引用变量
Father father=new Child(); Child child=father;//出错
在java里面,向上转型是自动进行的,但是向下转型却不是,需要自己强制进行
child=(child)father;进行强制转化,也就是向下转型
3、记住一个简单有很复杂的规则,一个类型的引用只能引用引用类型自身含有的方法和变量。
你可能说这个规则不对的,因为父类引用指向子类对象的时候,最后执行的是子类的方法的。
其实这并不矛盾,那是因为采用了后期绑定,动态运行的时候又根据型别去调用了子类的方法。而假若子类的这个方法在父类中并没有定义,则会出错。
分析:
当你使用父类引用指向子类的时候,其实jvm已经使用了编译器产生的类型信息调整转换了。
这里你可以这样理解,相当于把不是父类中含有的函数从虚拟函数表中设置为不可见的。注意有可能虚拟函数表中有些函数地址由于在子类中已经被改写了,所以对象虚拟函数表中虚拟函数项目地址已经被设置为子类中完成的方法体的地址了。
2.强制类型转换:
1、基本类型之间的转换只能在数值类型之间进行,这边的数值类型包括整型、字符型和浮点型。需要注意的是,数值类型和布尔类型之间不能进行类型转换。
2、引用类型之间的转换只能在具有继承关系的两个类型之间进行
在进行类型转换之前,通常建议使用instanceof运算符来判断是否可以成功转换,来避免出现ClassCastException异常。instanceof用法如下
if(str instanceof String){
String s=(String)str;
}
instanceof运算符的前一个操作数通常是一个引用类型变量,后一个操作数通常是一个类也可以是接口。
3.向下转型:
子类转型成父类是向上转型,反过来说,父类转型成子类就是向下转型。但是,向下转型可能会带来一些问题:我们可以说麻雀是鸟,但不能说鸟就是麻雀。来看下面的例子:
A类:
package a.b;
public class A {
void aMthod() {
System.out.println("A method");
}
}
A的子类B:
package a.b;
public class B extends A {
void bMethod1() {
System.out.println("B method 1");
}
void bMethod2() {
System.out.println("B method 2");
}
}
C类:
package a.b;
public class C {
public static void main(String[] args) {
A a1 = new B(); // 向上转型
a1.aMthod(); // 调用父类aMthod(),a1遗失B类方法bMethod1()、bMethod2()
B b1 = (B) a1; // 向下转型,编译无错误,运行时无错误
b1.aMthod(); // 调用父类A方法
b1.bMethod1(); // 调用B类方法
b1.bMethod2(); // 调用B类方法
A a2 = new A();
B b2 = (B) a2; // 向下转型,编译无错误,运行时将出错
b2.aMthod();
b2.bMethod1();
b2.bMethod2();
}
}
因为a1指向一个子类B的对象,所以子类B的实例对象b1当然也可以指向a1。而a2是一个父类对象,子类对象b2不能指向父类对象a2。那么如何避免在执行向下转型时发生运行时ClassCastException异常?使用5.7.7节学过的instanceof就可以了
总结:
多态体系下
成员变量,静态方法看左边;非静态方法:编译看左边,运行看右边。”意思是:当父类变量引用子类对象时(Fu f = new Zi();
),在这个引用变量f指向的对象中,他的成员变量和静态方法与父类是一致的,他的非静态方法,在编译时是与父类一致的,运行时却与子类一致(发生了复写)。
向上转型的情况下,如 Fu f=new Zi();
成员变量是父类,静态方法也是父类的,非静态方法编译看父类,运行是子类的结果,也就是重写之后的方法的结果
当静态时,Fu类的所有函数跟随Fu类加载而加载了。也就是Fu类的函数(是先于对象建立之前就存在了,无法被后出现的Zi类对象所复写的,所以没发生复写,那么获得:C:静态方法:编译和运行都看Fu。
向下转型存在危险系数,多态上存在限制:
1.在向下转型之前,必须向上转型 两个类必须存在关系 才能向下转型
可以通过 关键字 instanceof 来判断两个类是否存在关系,语法:
对象 instanceof 类 返回值为boolean类型