多态

  1. 定义
    同一个对象,在不同时刻表现出来的不同形态。(或者说父类的同一个方法在不同子类表现不同的结果,可以理解为表面声明为父类对象,但是真正调用的是那个new子类的方法。就是比如A是B,C的父类。A a1=new B();和A a2=new A():虽然这个a1和a2都是表现为表面是A但是你分别调用a1.f();和a2.f();的结果就不一样,就体现出不同的状态)
  2. 例子:
    我们可以说猫是猫:即,猫 cat=new 猫();
    也可以说猫是动物,即,动物 animal=new 猫();
  3. 多态的前提:
  • 两个类有继承或者实现的关系
  • 有方法重写
  • 有父类指向子类对象
  1. 多态中的成员访问特点
  • 成员变量
    编译看父类,运行看父类(编译看左边,执行看左边)
  • 成员方法
    编译看父类,运行看子类(编译看左边,执行看右边)

例子:

public class Animal {
    public int age = 40;

    public void eat() {
        System.out.println("动物吃东西");
    }
}
public class Cat extends Animal {
    public int age = 20;
    public int weight = 10;

    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }

    public void playGame() {
        System.out.println("猫捉迷藏");
    }
}
public class AnimalDemo {
    public static void main(String[] args) {
        //有父类引用指向子类对象
        Animal a = new Cat();

        System.out.println(a.age);//输出40,体现多态的成员变量执行看左边。执行的效果看那个Animal a = new Cat();的左边,即看那个Animal类的age属性
//        System.out.println(a.weight);//错误,体现多态的成员变量的编译看左边,这个编译不能通过

        a.eat();//输出"猫吃鱼"。体现多态的成员方法,执行看右边。执行的效果看那个Animal a = new Cat();的右边,即看那个Animal类的eat方法
//        a.playGame();//错误,体现多态的成员方法,编译看左边,这个编译不能通过
    }
}
  1. 多态的好处和弊端
  • 好处
    提高程序的扩展性。定义方法时候,使用父类型作为参数,在使用的时候,可以传递这个父类对象或这个父类对象的子类对象。这样有什么好处呢?比如你要是不用多态的话,你一个工具类的一个方法只能处理一种类对象,比如你工具类里面有一个方法是
public void f(A a){a.g();}

那么这个工具类里的这个方法只能处理这个A这种对象f(new A()),但是你要是想传递一个B对象,你就只能再建一个方法。就算这两个方法的语句差不多,也要再建一个方法只是参数列表不同的方法,这样就很浪费时间,代码重复多。

public void f(A a){a.g();}
public void f(B b){b.g();}

但是你要是在工具类里面定义一个这样的方法,然后ZIMU这个类是A,B,C……的父类,那么他们都可以用这一个方法来做事,不用重新定义一个方法了,而且这个zimu.g();的执行效果是看具体传入的对象的。

public void f(ZIMU zimu){zimu.g();}
  • 弊端
    不能使用子类的特有成员
    比如还是用上面那个好处的那个例子:要是父类里只有g()没有h();方法,,但是A对象有这个他特有的父类没有的h()方法,那么你这样使用就会出错,因为编译看左边,即看ZIMU这个类(看对象表明的类型,即接受对象的那个引用的类型)。
public void f(ZIMU zimu){zimu.h();}
  1. 多态中的转型
    转型可以解决多态的弊端。下面介绍转型。转型分为向上转型和向下转型两种。向上转型是自动的,向下转型的话要用“(要转为的类)”这个东西,具体看例子。
  • 向上转型
    父类引用指向子类对象就是向上转型,如,父类 对象名=new 子类();即父类接受子类都是向上转型。
  • 向下转型
    格式:子类型 对象名 = (子类型)父类引用;
    例子:
public class Animal {
    public void eat() {
        System.out.println("动物吃东西");
    }
}
public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }

    public void playGame() {
        System.out.println("猫捉迷藏");
    }
}
public class AnimalDemo {
    public static void main(String[] args) {
        //多态
        Animal a = new Cat();//向上转型
        a.eat();//编译看左边,执行看右边
//      a.playGame();//编译看左边,执行看右边

        Cat c = (Cat)a;//向下转型
        c.eat();
        c.playGame();//向下转型了,所以这个playGame方法就可以用了
    }
}
  • 注意:向下转型可能是会出现问题的,比如你把一个Animal cat=new Cat(); Dog dog=(Dog)cat;会抛出异常。即把实体为Cat的对象转为别的类可能出现问题,能不能转得看那个实体能不能放进到那个强转的那个类里面。
  1. instanceof
    instance是 Java 的一个二元操作符,类似于 ==,>,< 等操作符。
    instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回值为boolean型。
    比如,x instanceof A。(其中x是对象,A是类的名字)
    那么:
  • x是A这个类的子类的实例x是A这个类的实例都是返回true
  • x不是A这个类的子类实例也不是本类实例,都是返回false
  • x声明的时候表面上的类型和A这个类没有父子关系,那么编译不通过。就是编译看表面。执行看的是实际是否为他的子类或本类实例

例子:

package com.liudashuai;

//x instanceof A:检验x是否为类A的对象,返回值为boolean型。
//         要求x所属的类与类A必须是子类和父类的关系或本类,否则编译错误。
//         如果x属于类A的子类B,x instanceof A值也为true。
public class Demo4 extends HelloWorld{
    public static void main(String[] args) {
        Demo2 demo2=new Demo2();
        Demo1 demo1=new Demo1();
        Demo3 demo3=new Demo3();
        Demo4 demo4=new Demo4();
        Demo1 demo=new Demo3();
        System.out.println(demo1 instanceof Demo1);//true,本类实例instanceof本类
        System.out.println(demo1 instanceof Demo2);//false,父类实例instanceof子类
        System.out.println(demo3 instanceof Demo2);//true,子类实例instanceof父类,
        System.out.println(demo instanceof Demo2);//true,看实际的那个对象的类型判断true还是false。所以看实际的即“Demo3的实例 instanceof Demo2”是true,看表面的话,就是“Demo1的实例是不是Demo2”结果是false
//        System.out.println(demo4 instanceof Demo2);//编译错误,因为这个demo4非Demo2这个类子类或父类或本类的实例。要求demo4表面所属的类与类Demo2必须是子类和父类的关系或本类,否则编译错误。=
        HelloWorld helloWorld=new Demo4();
        //表面是Demo2的父类(其实是子类的子类),实际上是Demo4,而Demo4和Demo2没有父子类关系,所以上面的demo4 instanceof Demo2编译错误是只看表面的
        System.out.println(helloWorld instanceof Demo2);//false
    }
}

package com.liudashuai;
public class Demo3 extends Demo2{
}

package com.liudashuai;
public class Demo2 extends Demo1{
}

package com.liudashuai;
public class HelloWorld {
}

package com.liudashuai;
public class Demo1 extends HelloWorld{
}

这几个类的父子关系是这样的:

java多态的面试题 java多态知识点_开发语言