JavaSE-No.8——Java三大特征之多态

JavaSE传送门

JavaSE_Start

JavaSE-No.7.1——Java的继承(super关键字)

JavaSE-No.7.2——Java的继承(与组合)



1. 多态的概念

通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。


总的来说:同一件事情,发生在不同对象身上,就会产生不同的结果。


2. 多态的实现条件

在java中要实现多态,必须要满足如下几个条件:

  1. 在继承体系下,完成向上转型
  2. 子类对父类的方法进行重写
  3. 通过父类的引用调用重写的方法

我们先创建一个父类Flower:

class Flower {
    private String name;
    private String color;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public Flower(String name, String color) {//构造方法
        this.name = name;
        this.color = color;
    }

    @Override
    public String toString() {
        return "Flower{" +
                "name='" + name + '\'' +
                ", color='" + color + '\'' +
                '}';
    }

    public void flowering() {
        System.out.println(name+"开花了💐");
    }
}

我们再写两个子类CherryBlossom和Rose:

class CherryBlossom extends Flower{
    public CherryBlossom(String name, String color) {
        super(name, color);
    }

    public void view() {
        System.out.println("观赏"+getName()+"🌸");
    }
}
class Rose extends Flower{
    public Rose(String name, String color) {
        super(name, color);
    }
    
    public void gift() {
        System.out.println("一束"+getName()+"🌹作为礼物");
    }
}

20200811130123_5074f

什么叫做向上转型?

把子类对象(例:Rose),赋值给父类对象(例:Flower)。

public class Test {
    public static void main(String[] args) {
        Rose rose = new Rose("白玫瑰","white");
        Flower flower1 = rose;//向上转型
        
        //简写
        Flower flower = new Rose("红玫瑰","red");
    }
}

我们通过新定义的flower来分别调用子类,父类的方法,我们发现在调用子类特有的方法的时候报错了。

因为:flower是Flower类型的,flower只能调用Flower中的方法。

20200811130123_5074f

重写

重写(override):也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程进行重新编写, 返回值和形参都不能改变即外壳不变,核心重写! 重写的好处在于子类可以根据需要,定义特定于自己的行为。

重写的规则

  1. 一般子类与父类方法原型一致:修饰符 返回值类型 方法名(参数列表) 要完全一致

  2. 被重写的方法返回值类型可以不同,但是必须是具有父子关系的。

    子类和父类被重写方法的返回值构成协变类型(子类的返回值和父类的返回值构成父子关系)

  3. 父类被staticprivate修饰的方法、构造方法都不能被重写。

  4. 访问权限大于等于父类中被重写的方法的访问权限。

# 注意 # 如果一个方法不想被重写,用final修饰(密封方法)。

接下来我们要子类对父类的方法(例:flowering)进行重写

//class CherryBlossom中重写flowering
public void flowering() {
    System.out.println(getName()+"开了🌸");
}

//class Rose中重写flowering
public void flowering() {
    System.out.println(getName()+"开了🌹");
}

20200811130123_5074f

通过父类的引用调用重写的方法

这时我们再调用flowering方法会发生什么?

public class Test {
    public static void main(String[] args) {
        Flower flower3 = new Flower("花田","colorful");
        flower3.flowering();
        
        Flower flower2 = new CherryBlossom("樱花","pink");
        flower2.flowering();//动态绑定
        
        Flower flower1 = new Rose("红玫瑰","red");
        flower1.flowering();//动态绑定
    }
}

运行结果展示:向上转型后的变量调用了子类的flowering,发生了动态绑定(本篇文章后面会介绍什么是动态绑定)


3. 向上转型和向下转型

3.1 向上转型

直接赋值

public class Test {
    public static void main(String[] args) {
        
        Flower flower = new Rose("红玫瑰","red");
        flower.flowering();
    }
}

方法的传参

public class Test {
    public static void func(Flower flower) {
        flower.flowering();
    }
    public static void main(String[] args) {
        func(new Rose("红玫瑰","red"));
        func1(new CherryBlossom("樱花","pink"));
    }
}

运行结果展示:

方法的返回值

public class Test {
    public static Flower func() {
        return new Rose("红玫瑰","red");
    }
    
    public static void main(String[] args) {
         Flower flower = func();
    }
}

向上转型的优点:让代码实现更简单灵活。

向上转型的缺陷:不能调用到子类特有的方法。


3.2 向下转型

将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可,即向下转换。

public static void main(String[] args) {
    Flower flower = new Rose("红玫瑰","red");
    Rose rose = (Rose) flower;//向下转型
    rose.gift();
}

# 注意 # 向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛异常。

我们来看一段错误代码。

public static void main(String[] args) {
    Flower flower = new CherryBlossom("樱花","pink");
    Rose rose = (Rose) flower;//向下转型,樱花时玫瑰花吗??
    rose.gift();
}

instanceof

不是所有的花都是"玫瑰花",Java中为了提高向下转型的安全性,引入了 instanceof ,如果该表达式为true,则可以安全转换。

public static void main(String[] args) {
        Flower flower = new CherryBlossom("樱花","pink");
        //判断 flower是不是引用了Rose这个对象
        if (flower instanceof Rose) {
            Rose rose = (Rose) flower;//向下转型
            rose.gift();
        }
        flower = new Rose("红玫瑰","red");
        if (flower instanceof Rose) {
            Rose rose = (Rose) flower;//向下转型
            rose.gift();
        }
}

4.重写的设计规则

对于已经投入使用的类,尽量不要进行修改。最好的方式是:重新定义一个新的类,来重复利用其中共性的内容,并且添加或者改动新的内容。

例如: 若干年前的手机,来电显示只能显示号码,而今天的手机在来电显示的时候,不仅仅可以显示号码,还可以显示头像,地区等。在这个过程当中,我们不应该在原来老的类上进行修改,因为原来的类,可能还在有用户使用,正确做法是:新建一个新手机的类,对来电显示这个方法重写就好了,这样就达到了我们当今的需求了


5. 动态绑定和静态绑定

5.1 动态绑定

动态绑定: 也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体调用那个类的方法。

继续上面的代码,我们在main写入这样两行代码:

public class Test {
    public static void main(String[] args) {
    
        Flower flower = new Rose("红玫瑰","red");
        flower.flowering();
    }
}
//运行结果
红玫瑰开了🌹

我们打开Powershell窗口,使用javap -c Test反编译一下。

编译的时候调用的是父类的方法,但是在运行时,程序发生了动态绑定,调用了子类的方法。

20200811130123_5074f

# 注意事项 # 在父类的构造方法中也会发生动态绑定。但是不要这样写!!

class A {
    public A() {
        print();
    }
    public void print() {
        System.out.println("A");
    }
}
class B extends A {
    public void print() {
        System.out.println("B");
    }
}

public class Example {
    public static void main(String[] args) {
        B bb = new B();
    }
}
//运行结果
B

结论:“用尽量简单的方式使对象进入可工作状态”, 尽量不要在构造器中调用方法, 可能会出现一些隐藏的但是又极难发现的问题。


5.2 静态绑定

静态绑定: 也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代表函数重载。

public class Example {
    public static void print() {
        System.out.println("hh");
    }

    public static void print(int a) {
        System.out.println("haha");
    }
    
    public static void main(String[] args) {
        //静态绑定:编译的时候,就确定了最终要调用的方法
        print();
        print(1);
    }
}

6. 多态的优缺点

使用多态的好处

  1. 能够降低代码的"圈复杂度", 避免使用大量的 if - else

小知识: 圈复杂度

圈复杂度是一种描述一段代码复杂程度的方式. 一段代码如果有很多的条件分支或者循环语句, 就认为理解起来更复杂.

因此我们可以简单粗暴的计算一段代码中条件语句和循环语句出现的个数, 这个个数就称为 “圈复杂度”.

我们改写一下的代码,写入几个新的子类:

class Flower {
    public String name;
    public String color;

    public void flowering() {
        System.out.println("花开了💐");
    }
}
class CherryBlossom extends Flower{
    @Override
    public void flowering() {
        System.out.println("🌸开了");
    }
}
class Rose extends Flower{
    @Override
    public void flowering() {
        System.out.println("🌹开了");
    }
}
class Sunflower extends Flower{
    @Override
    public void flowering() {
        System.out.println("🌻开了");
    }
}
class Tulip extends Flower{
    @Override
    public void flowering() {
        System.out.println("🌷开了");
    }
}

如果我们要让💐按照"🌸🌻🌷🌹🌻🌷"的顺序开花,用if-else应该怎么写?我们可以看到非常繁琐。

public class Test {
    public static void func() {
        CherryBlossom cherryBlossom = new CherryBlossom();
        Rose rose = new Rose();
        Sunflower sunflower = new Sunflower();
        Tulip tulip = new Tulip();
        //"🌸🌻🌷🌹🌻🌷"
        String[] flowers = {"cherryBlossom","sunflower","tulip","rose","sunflower","tulip"};
        for (String flower:flowers) {
            if (flower.equals("cherryBlossom")) {
                cherryBlossom.flowering();
            } else if (flower.equals("sunflower")) {
                sunflower.flowering();
            } else if (flower.equals("rose")) {
                rose.flowering();
            } else if (flower.equals("tulip")) {
                tulip.flowering();
            }
        }
    }
}

如果我们使用多态

public class Test {
    public static void func() {
        CherryBlossom cherryBlossom = new CherryBlossom();
        Rose rose = new Rose();
        Sunflower sunflower = new Sunflower();
        Tulip tulip = new Tulip();
        //"🌸🌻🌷🌹🌻🌷"
        Flower[] flowers = {cherryBlossom,sunflower,tulip,rose,sunflower,tulip};
        for (Flower flower:flowers) {
            flower.flowering();
        }
    }
}
  1. 可扩展能力更强

    如果要新增一种新的形状, 使用多态的方式代码改动成本也比较低

    对于类的调用者来说(func方法), 只要创建一个新类的实例就可以了, 改动成本很低.

多态缺陷:代码的运行效率降低。


🌷(( ◞•̀д•́)◞⚔◟(•̀д•́◟ ))🌷

以上就是今天要讲的内容了,希望对大家有所帮助,如果有问题欢迎评论指出,会积极改正!!下一篇文章会讲述Java的抽象类的相关知识,期待大家支持,谢谢~

  • 14
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
### 回答1: 这是Oracle官网上Java SE的下载页面,Java SE是Java平台的标准版本,它提供了Java程序开发和运行所需的核心功能和库。在该页面中,您可以下载Java SE的不同版本,包括JDK(Java Development Kit)、JRE(Java Runtime Environment)和服务器JRE。您可以选择根据您的需求和操作系统下载适当的版本。另外,您也可以在该页面上找到Java SE的文档和相关信息。 ### 回答2: https://www.oracle.com/java/technologies/javase-downloads.html 是Oracle官方Java SE下载页面。这个页面提供了Java SE的最新版本下载,以及Java SE的旧版本下载。Java SE是一种全面的计算机技术,用于开发和部署强大、可靠、安全的Java应用程序。Java SE是Java平台的核心,是开发Java Web、Java EE、Java ME等各种应用程序所必须的绕不开的基础。 在这个页面上,用户可以下载适合自己平台的Java SE版本,包括Windows、Linux和Mac OS X。用户可以选择不同的版本,比如JDK和JRE,以及特定版本号。在下载之前,用户需要同意Oracle的授权协议才能下载。协议列明了用户可以使用Java SE做什么,不可以做什么,以及特定条款和条件。用户需要认真阅读,理解,并且同意了协议才能下载。 Java SE具有很多优点,比如跨平台移植性、良好的安全性、易于学习和使用等特点。因此Java SE深受广大程序员和企业的欢迎。Java SE更新比较频繁,同时也增加了很多新的特性和优化。因此,在Java开发中要时刻关注Java SE的更新和改进,及时进行版本升级,以便获得更好的使用体验和应用性能。 ### 回答3: Oracle Java SE(Standard Edition)是目前全球最大的应用软件平台之一,它提供了广泛的基于Java语言开发和部署工具,可在各种计算机、服务器、移动设备、网络和云中使用。Oracle Java SE是Java编程语言的官方发布,目前最新版本为Java 17。 在Oracle官方网站上,可下载Oracle Java SE的最新版本,以及其他旧版本供特定用途使用。下载包括Java开发工具包(JDK)和Java运行时环境(JRE),其中JDK含有Java编译器、调试器、JavaDoc等工具,而JRE则提供了Java应用程序的运行环境。 通过使用Oracle Java SE,开发者可编写安全、可靠和可扩展的Java应用程序,支持多个操作系统平台,包括Windows、Linux和macOS。开发Java应用程序的一个主要优点是其可移植性。Java程序可在不同平台上执行,而不需要进行修改。开发人员还可以使用大量的Java类库和API,以提高生产效率和降低应用程序的开发成本。 Oracle Java SE支持各种开发领域,包括企业级应用程序、移动应用程序、桌面应用程序、嵌入式设备和游戏开发。此外,Java也用于其他领域,如大数据处理、人工智能和机器学习。 总的来说,Oracle Java SE具有出色的跨平台性能和多功能性。它已成为开发Java应用程序的必备开发工具之一。对于Java开发者来说,定期下载和使用Oracle Java SE的最新版本是十分重要的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值