什么是多态?
public class Person {
String name;
int age;
public void eat(){
System.out.println("人,吃饭");
}
public void walk(){
System.out.println("人,走路");
}
}
public class Woman extends Person{
``
boolean isBeauty;
public void goShopping(){
System.out.println("女人喜欢购物");
}
public void eat(){
System.out.println("女人少吃,为了减肥。");
}
public void walk(){
System.out.println("女人,窈窕的走路。");
}
}
public class Man extends Person{
boolean isSmoking;
public void earnMoney(){
System.out.println("男人负责工作养家");
}
public void eat() {
System.out.println("男人多吃肉,长肌肉");
}
public void walk() {
System.out.println("男人霸气的走路");
}
}
对象的多态性:父类的引用指向子类的对象(或子类的对象赋值给父类的引用)
Person p1 = new Man();
Person p2 = new Woman();
可以理解为一个事物的多种形态
p1.eat();p1.walk();
输出:男人。。。
p2.eat();p2.walk();
输出:女人。。。
同样是Person类的引用,但是指向的对象,一个是男人/一个是女人。在男人和女人类中都改写了Person类的方法。所以表现出来的是不同的方法。
当调用子父类同名同参数方法时,实际调用的是子类重写父类的方法,这叫作虚拟方法调用
可能对多态的一个误解
但是也会出现一个问题如果:
p1.earnMoney()
报错
虽然p1能调用Person里面有的方法,但是执行的时候却是执行子类中重写的方法。
也就是java编译看左边(编译期只能调用父类中声明的方法),执行看右边(实际执行的时候,是子类重写父类的方法)
所以,请牢记:
编译看左边,执行看右边
多态的前提
满足下列的关系,才能在这此基础上谈论多态。
① 类的继承关系
② 方法的重写
程序为什么要有多态性?
举例
/*
* 多态性应用举例
*/
class Animal{
public void eat(){
System.out.println("动物,进食");
}
public void shout(){
System.out.println("动物:叫");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("狗吃骨头");
}
public void shout() {
System.out.println("汪!汪!汪!");
}
}
class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼");
}
public void shout() {
System.out.println("喵!喵!喵!");
}
}
public class AnimalTest {
public static void main(String[] args) {
AnimalTest test = new AnimalTest();
test.func(new Dog());
test.func(new Cat());
}
public void func(Animal animal){ //Animal animal = new Dog();
animal.eat();
animal.shout();
}
//如果没有多态性,就会写很多如下的方法去调用分别子类的方法,造成代码的重复
public void func(Dog dog){
dog.eat();
dog.shout();
}
public void func(Cat cat){
cat.eat();
cat.shout();
}
}
多态性不适用于类的属性
public class Person{
int id = 1001;
}
public class Man{
int id = 1002;
}
Person p = new Man();
System.out.println(p.id)
输出1001
多态性不适用于属性,编译和运行都是看左边。
多态是怎么实现的?
虚拟方法的调用
之所以能够实现多态,是因为父类在生成方法的时候,是生成了一个虚拟方法 子类中定义了与父类同名同参数的方法。在多态情况下,将此时父类的方法称为虚拟方法.父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
//正常的方法调用
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();
//编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo()方法。也叫作动态绑定
Person e = new Student();
e.getInfo(); //调用Student类的getInfo()方法
方法的重载与重写
重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;
而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。
引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”
instanceof 关键字
判断A是不是B的一个实例
A instanceof B
向下转型的几种情况
先看几种问题 一、编译通过,运行不通过。 情况1:
People p = new Woman();
Man m = (Man)p;
上面的代码,在编译的时候会通过,但是运行的时间会报ClassCastException错误。
情况2:
People p = new People();
Man m = (Man)p;
如果能通过,那m.earnMoney的方法在哪里?
二、编译通过,运行通过。
Object obj = new Woman();
Person p = (Person)obj;
三、编译不过
Man m = new Woman();