面向对象的三大特征:封装,继承和多态。多态是面向程序设计中代码重用的一个重要机制,它表示当同一操作作用在不同对象时,会有不同的语义。
Java 多态主要有以下两种表现方式:
1)方法重载(overload)。重载是指同一类中有多个同名方法,但是这些方法必须在参数列表上加以区分,要么参数个数不同,要么在相同位置上的参数类型不同,返回值和访问控制符可以不同。
2)方法的覆盖(override)。子类可以覆盖父类的方法。父类的引用不但可以指向其实例对象,也能够指向子类的实例对象。接口的引用变量也可以指向其实现类的实例对象。
1、方法覆盖要求方法名,参数列表,返回值都必须相同,假如参数列表不同就属于重载,假如返回值不同,编译通不过!
2、另外覆盖方法权限不能低于被覆盖方法,例如父类是public 子类就必须是public,假如是protected就通不过。
3、重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常。例如:父类的一个方法申明了一个检查异常IOException,在重写这个方法是就不能抛出Exception,只能抛出IOException的子类异常,可以抛出非检查异常。
而程序的调用方法是在运行期才动态绑定的,就是引用变量所指向的具体实例对象的方法,而不是引用变量的类型中定义的方法。这种动态绑定实现了多态。只有运行时才能够确定具体调用哪个方法。因此通过方法覆盖的实例也称为运行时多态。
注意成员变量是无法实现多态的,成员变量的取值取决于定义变量的类型。
public class Father { public int i=1; public void a() { System.out.println("father a"); } public void b() { System.out.println("father b"); } }
public class Son extends Father{ public int i=5; public void a() { System.out.println("Son a"); } public void b() { System.out.println("Son b"); } public void c() { System.out.println("Son c"); } }
public static void main(String[] args) { Father father=new Son(); father.a(); father.b(); //father.c(); error System.out.println(father.i); Son son=(Son)father; //父类强制转换成子类的原则:父类型的引用指向的是哪个子类的实例,就能转换成哪个子类的引用 son.c(); System.out.println(son.i);
//会抛出异常 java.lang.ClassCastException: Father cannot be cast to Son
//Son son=(Son)new Father();
}
输出:
Son a Son b 1 Son c 5
PS:Java动态绑定和静态绑定
这两种绑定方式是根据是否能够在编译器确定要执行的方法来区分的。
其中只有final,static,private和构造方法是静态绑定,其他的全部是动态绑定,即在运行期才能确定要执行的方法。
final 类的方法无法被覆盖,子类可以调用这个方法,但是都是父类的这个方法,所以在编译器就能够知道。
static是类方法,引用变量是什么类型的,调用的就是什么类型的静态方法,注意子类可以转换成父类,因为子类包含了父类的全部,而父类不能转换成子类。
Father father=new Son(); father.say(); Son son=new Son(); son.say(); father=son; father.say();
输出
father say
son say
father say
这个例子中父类和子类都有一个同名的静态方法say(),当引用变量是father类型的,那么调用的就是Father类型的,当引用变量是Son类型的,那么调用的就是Son类型的。
private是类私有的,只能在类内部调用。
构造方法不能被覆盖,可以直接绑定。
其他的方法调用都是动态绑定,类似Objective c中的isa,通过instanceof 先找到对象属于哪类类型,然后查找对象的方法表,有同样签名的就执行,否则到父类去查找。