My Blog

Java面向对象六之多态

学习笔记,仅供参考

参考B站Mirco_Frank - java与生活

扩展阅读多态的详细讲解 | 多态的有趣解释

本文重点:多态 | 匿名内部类


🎈 多态

经过一段时间的学习,终于到了面向对象的最后一个特性 – 多态 指代不同数据类型的实体提供统一的接口,或者用一个单一的符号来表示多个不同的类型。说白了多态就是具有多种形态,在不同的场合表现出的形态不同,从而做出相应的动作。比如:电脑在学习时是学习的工具,在娱乐时又是电视机或者游戏机,在通讯交流时又是聊天通讯的工具。

本节以花木兰替父从军的例子来展现多态在 Java 中的使用情形。

我们都知道花木兰替父从军的故事,在故事中花木兰扮演着两种身份,分别是打仗时的将士形象和归来时的女儿形象。将士形象指的就是她爹花胡,女儿形象就是花木兰自己,所以先创建两个类来表示花胡和花木兰。

/**
 *  姓名:花胡
 *  年龄:45
 *  行为:自我介绍、打仗
 */
public class HuaHu {
    public String name = "花胡" ;
    public int age = 45;

    public static void sayMe() {
        System.out.println("大家好!我叫花胡,今年45岁");
    }

    public void fight() {
        System.out.println("打仗...");
    }
}

/***************分割线*********************/

/**
 *  姓名:花木兰
 *  年龄:19
 *  行为:自我介绍、化妆
 *  因为要替父从军,所以要继承花胡
 */
public class HuaMuLan extends HuaHu {
    public String name = "花木兰";
    public int age = 19;

    public static void sayMe() {
        System.out.println("大家好!我叫花木兰,今年19岁");
    }

    public void dressing() {
        System.out.println("梳妆打扮...");
    }
}

接着在测试类中模拟这两种情形:

 public class Test {
     public static void main(String args[]) {
         // 替父从军,向上转型,父类 new 子类
         // 此时的实例对象可以使用父类的属性和子类未重写的方法
         HuaHu huaHu = new HuaMuLan();
         System.out.println(huaHu.name);
         System.out.println(huaHu.age);
         HuaHu.sayMe();
         HuaHu.fight();

// 输出如下:        
// 花胡 | 45 | 大家好!我家花木兰今年19岁 | 打仗!
// 因为在 HuaMuLan 类中重写了 sayMe(); 方法,所以这里是花木兰的自我介绍。
// 要想用花胡的自我介绍,就要删除 HuaMuLan 类的sayMe(); 方法

/**********************分割线********************/

        //胜利归来,重返女儿身,向下转型, 父类对象强制转换为子类对象 
        HuaMuLan huaMuLan = (HuaMuLan) huaHu;
        System.out.println(huaMuLan.name);
        System.out.println(huaMuLan.age);
        huaMuLan.sayMe();
        huaMuLan.dressing();

// 输出如下:
// 花木兰 | 19 大家好!我家花木兰今年19岁 | 梳妆打扮...
     }
 }

经过验证,向上转型的变量既是父类的实例也是子类的实例,如下图所示。之所以如此,我认为是多态对象既可以使用父类中的属性和未被子类重写的方法,如上例中的 huaHu.name; huaHu.age; huaHu.fight(); 也可以使用子类中重写的方法, 如 huaHu.sayMe(); 就像儿子冒充老子一样,但有一定的风险,那就是不能重写父类的方法,不然就会暴露身份。

多态


🚀 匿名内部类

所谓的匿名内部类中的 匿名 指没有名字,内部类 指在类的内部还有类。正因为没有名字,所以匿名内部类只能使用一次,通常用来简化代码编写。但是前提条件是:必须继承一个父类或者实现一个接口

接下来就对比一下匿名内部类和常规的子类继承父类并实例化:

/**
 *  先创建一个接口(父类)
 *  记住:接口的方法没有意义(内容)
 */
 public interface Human {
     public void eat();
     public void sleep();
 }

 /*****************分割线************************/

 /**
 *  常规的形式是再创建一个实现类(子类)
 *  实现类要重写接口的所有方法
 */
public class Chinese implements Human {
    @Override
    public void eat() {
        System.out.println("eating...");
    }

    @Override
    public void sleep() {
        System.out.println("sleeping");
    }
}

/*********************分割线**********************/
 /**
 *  测试两者的不同
 */
public class Test {
    public static void main(String args[]) {

         // 常规方法
         Chinese chinese = new Chinese();
         chinese.eat();
         chinese.sleep();

         // 匿名内部类
         Human chinese1 = new Human() {
             @Override
             public void eat() {
                System.out.println("eating...");
             }

             @Override
             public void sleep() {
                 System.out.println("sleeping");
             }
         }
         chinese1.eat();
         chinese1.sleep()

    }
}

从上面的代码可以知道,匿名内部类就相当于把子类的重写和对象的实例化两步并作一步,从而省去了中间创建子类的过程。