一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
两种实现方式:方法的重载(overload)重载指一个类中有多个同名的方法,但这些方法有着不同的参数(个数或者类型),返回类型可以相同也可以不同,因此编译时可以确定到底调用哪个方法,是一种编译时多态。
方法的覆盖(override,也叫重写)子类可以重写父类的方法,因此同样的方法会在父类和子类中有着不同的表现形式。是运行时多态。一般多态指运行时多态。
方法重写特点:1.子类重写父类方法2.重写方法的访问修饰符大于等于被重写方法的访问修饰符。3.参数列表相同,返回值一致。4.被重写的方法不能被private5.静态方法不能被重写为非静态方法(编译出错)
多态三要素:1.要有继承关系2.子类要重写父类的方法3.父类引用指向子类对象(向上转型)
运行时多态的实现方式为:继承和接口,继承是通过重写父类的同一方法的几个不同子类来体现的,那么就可就是通过实现接口并覆盖接口中同一方法的几不同的类体现的。
class Animal { //父类
String name = "animal";
static int age = 20; //静态属性
public void eat() {
System.out.println("动物 eat");
}
protected static void sleep () { //静态方法
System.out.println("动物 sleep");
}
public void run () {
System.out.println("动物 run");
}
}
class Cat extends Animal { //子类
String name = "cat";
static int age = 12; //静态属性
@Override
public void eat() {
System.out.println("猫 eat");
}
public static void sleep() { //父类的静态方法可以被子类继承,但是不能重写。
System.out.println("猫 run");//这里与父类的方法名相同但是实现不同,所以并未继承!
}
public void fight() {
System.out.println("猫 fight");
}
}
public static void main(String[] args) {
Animal animal = new Cat();
System.out.println(animal.name + " " + animal.age); //animal 20
animal.eat(); //猫 eat
animal.sleep(); //动物 sleep
animal.run(); //动物 run
}
根据测试结果,验证了多态的一套规则:
“成员变量,静态方法看左边;非静态方法:编译看左边,运行看右边。”意思是:当父类变量引用子类对象时(Fu f = new Zi();),在这个引用变量f指向的对象中,他的成员变量和静态方法与父类是一致的,他的非静态方法,在编译时是与父类一致的,运行时却与子类一致(发生了复写)。
子类Cat中还有一个独有的fight方法,通过animal对象是访问不到的,这时要animal向下转型为Cat,即将父类强制转成子类:
Cat cat = (Cat) animal;
cat.fight();
总结:父类引用调用子类对象,编译时看父类引用,运行看子类对象,如果没有重写方法,调用时运行的是父类的方法,
重写就是为了虽然用的父类的引用,但是调用的却是子类的方法。
多态的运行顺序this.show(o)->supper.show(o)->this.show(supper(o))->supper.show(supper(o))