前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >【Java面向对象三大特征——多态】

【Java面向对象三大特征——多态】

作者头像
ImAileen
发布2024-12-04 08:18:55
发布2024-12-04 08:18:55
13300
代码可运行
举报
运行总次数:0
代码可运行

多态

多态:完成某个行为,当不同对象去完成时会产生出不同的状态。(同一件事,发生在不同对象身上,产生不同的效果。eg:动物吃粮食这个动作,对于猫是吃猫粮,狗是吃狗粮。)

多态实现条件
  • 在Java中要实现多态,必须要满足以下几个条件,缺一不可:
    1. 必须在继承体系下
    2. 子类必须要对父类中的方法进行重写
    3. 通过父类的引用调用重写的方法

多态体现在:代码运行时,当传递不同的对象时,会调用对应类中的方法。

对比上面Animal和Dog的eat方法我们可得到以下关于方法重写的结论:

  1. 这两个方法的方法名一样 。
  2. 参数列表(个数,数据类型,顺序)一样 。
  3. 返回值一样。

向上转型:父类引用 引用了子类的对象。

向上转型是发生多态的条件之一。

动态绑定:方法重写,父类和子类拥有同样的方法(子类通过重写父类的eat方法),(通过父类对象的引用调用此时调用的是子类的方法),如下图所示:我们通过父类的引用animal1来调用子类的eat方法这就是动态绑定。

动态绑定,编译时期无法确定或者确定其他的。我们可以通过下图中的反汇编代码看到在编译期间,我们通过animal1引用调用的eat方法还是Animal的,而在根据运行结果我们可以知道,运行的时候animal1这个引用调用的是Dog的eat方法,这个方法从animal绑定到(dog的eat方法)或其它函数方法执行的过程就叫做动态绑定

  • 动态绑定过程:
    • 1.通过父类引用引用子类对象。
    • 2.通过父类引用去调用父类和子类同名的方法,此时调用的方法是子类的方法

静态绑定: 重载就是一种静态绑定,编译时期绑定的,根据所传参数的不一样来确定调用哪一个方法。

方法的重载,在编译时根据参数个数就能够自动调用相应的方法。


当父类有构造方法时,子类在进行构造之前需要先帮助父类构造进行初始化

向上转型的第一种传参方式:直接赋值

向上转型的第二种传参方式:通过传参

本来Animal就是父类类型的引用,然后返回任何一个子类对象,这三种写法本质一样,只是搭配组合的问题。

向上转型的第三种方式:通过返回值

重写

不能进行重写的情况

1.被final修饰的叫密封方法,是不能够进行重写(@Override,作为注解用来检测是否有重写)的。

2.被static修饰的方法也不能够进行重写 不同访问修饰符在Java中的访问权限:

访问修饰符

同一包中的同类

同一包中的不同类

不同包中的子类

不同包中的非子类

private

default (no modifier)

protected

public

3.子类重写父类的时候,子类的方法访问修饰限定符权限要大于等于父类。有两个比较特殊的

  • ①.被private修饰的方法不能够进行重写,这是由于private方法的访问级别最低,他们不能被子类访问,因此也就无法被子类重写。
  • ②.被public修饰的方法本来权限就已经是最大的了,所以重写的子类方法也得是public的。

4.构造方法不能够进行重写

能够重写的条件
普通重写
类型之间构成父子关系的重写
  • 被重写的方法返回类型可以不同,但必须具有父子关系。

同一个引用(animal)调用了同一个方法(eat),但是因为引用的对象(一个是mydog一个是mybird)不一样,所表现的行为不一样(狗吃狗粮鸟吃饲料),我们把这种思想称为多态


向上转型

代码语言:javascript
代码运行次数:0
复制
package demo1;

class Aniamal{
    public String name;
    public  int age;

    public Aniamal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void eat(){
        System.out.println(this.name+" 正在吃...");
    }
}

class Bird extends Aniamal{

    //创建Bird的构造方法
    public  Bird(String name,int age){
        super(name,age);
    }

    public void eat(){
        System.out.println(this.name + " 正在吃鸟饲料");
    }

    public void fly(){
        System.out.println(this.name + " 正在飞翔~");
    }
}

class Dog extends Aniamal{
    public  String color;

    public Dog(String name , int age, String color){
        super(name, age);
        this.color = color;
    }
    public void barks(){
        System.out.println(this.name + " 正在汪汪叫!");
    }

    public void eat(){
        System.out.println(this.name + " 正在吃狗粮。");
    }
}

public class Test {
    public static void main(String[] args) {
        Aniamal animal2 = new Bird("妙妙",6);
        //向上转型
        animal2.eat();}
  }
在这里插入图片描述
在这里插入图片描述
  • 向上转型的优点:让代码的实现更简单灵活
  • 向上转型的缺陷:不能调用到子类特有的方法(除非子类重写了父类的方法)

向下转型

代码语言:javascript
代码运行次数:0
复制
package demo1;

class Aniamal{
    public String name;
    public  int age;

    public Aniamal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void eat(){
        System.out.println(this.name+" 正在吃...");
    }
}

class Bird extends Aniamal{

    //创建Bird的构造方法
    public  Bird(String name,int age){
        super(name,age);
    }

    public void eat(){
        System.out.println(this.name + " 正在吃鸟饲料");
    }

    public void fly(){
        System.out.println(this.name + " 正在飞翔~");
    }
}

class Dog extends Aniamal{
    public  String color;

    public Dog(String name , int age, String color){
        super(name, age);
        this.color = color;
    }
    public void barks(){
        System.out.println(this.name + " 正在汪汪叫!");
    }

    public void eat(){
        System.out.println(this.name + " 正在吃狗粮。");
    }
}

public class Test {
    public static void main(String[] args) {
        Aniamal animal2 = new Bird("妙妙",6);
        //向下转型 -> 正常版
        Bird bird = (Bird) animal2;
        bird.fly();

        Aniamal animal1 = new Dog("旺旺",7,"red");
        //向下转型需要进行条件判断 -> 因为这个fly是鸟的方法
        if (animal1 instanceof Bird) {
            Bird bird2 = (Bird) animal1;
            bird2.fly();
        }else{
            System.out.println("we can't use this method ");
        }
	}
  }
  • 向下转型用的较少,且不安全,万一转换失败,运行时会抛出异常。java中为了提高向下转型的安全性,引入了instanceof,如果该表达式为true,则可以安全转换。

避免在构造方法中调用重写方法

代码语言:javascript
代码运行次数:0
复制
package demo1;
class B{
    public B(){
        //do nothing
        func();
    }
    public void func(){
        System.out.println("B.func()");
    }
}

class D extends B{
    private int num = 1;

    public  D(){
        super();
    }

    @Override
    public void func(){
        System.out.println("D.func() ");
    }
}

public class Test2 {
    public static void main(String[] args) {
        D d = new D();
    }
}
  • 上面的代码在创建D的对象的时候会先执行D的构造方法,然后通过super调用其父类B的构造方法,由于B的构造方法里面调用了func这个无参的方法,这里会被误以为是调用父类的方法func,但根据实际上的运行结果是调用了子类D的方法func,这是因为在父类构造方法中,调用父类和子类同名的方法的时候也会发生动态绑定,这意味着构造方法内也会发生动态绑定。
代码语言:javascript
代码运行次数:0
复制
package demo1;
class B{
    public B(){
        //do nothing
        func();
    }
    public void func(){
        System.out.println("B.func()");
    }
}

class D extends B{
    private int num = 1;

    public  D(){
        super();
    }

    @Override
    public void func(){
        System.out.println("D.func() " + num);
    }
}

public class Test2 {
    public static void main(String[] args) {
        D d = new D();
    }
}
  • 我们在上面代码基础上,给子类重写的方法func加上参数num,打印结果居然是0,而不是1,这是为什么呢?下面我们将通过调试看一下它是怎么运行的。
  • 根据上面的调试过程,我们可以根据之前的知识,由于代码里面无静态方法,所以应该是先执行父类实例和父类构造,然后才是执行子类实例和子类构造。我们可以看到通过子类的引用去调用子类的构造代码块,由于在调用子类之前要先对父类进行初始化,所以我们通过super关键字它先执行父类的构造代码块,然后执行父类构造代码块中的无参方法func,根据上面的讲解我们知道这里会发生动态绑定,所以它会执行子类的方法func,由于还未执行子类的实例代码块private int num = 1所以此时num的值还是默认为0,因此我们的打印结果是0,然后再往下执行子类D的实例代码块。

多态的优缺点

代码语言:javascript
代码运行次数:0
复制
package demo2;
class Shape {
    public  void draw(){
        System.out.println("画图形!");
    }
}

class Cycle extends Shape{

    @Override
    public void draw() {
        System.out.println("○");
    }
}

class Rect extends Shape{
    @Override
    public void draw() {
        System.out.println("矩形");
    }
}

class Triangle extends Shape{
    @Override
    public void draw() {
        System.out.println("△");
    }
}

public class Test {
    public static void drawMap(Shape shape){
        shape.draw();
    }

    public static void main(String[] args) {
        Rect rect = new Rect();
        Cycle cycle = new Cycle();
        Triangle triangle = new Triangle();

        drawMap(rect);
        drawMap(cycle);
        drawMap(triangle);
    }
}

上面的代码中:通过drawMap()进行向上转型,这个shape引用分别指向rect,cycle,triangle这三个对象,然后通过一个shape这个引用直接调用draw方法,打印出来的结果分别表现出不同的形状,这就体现了多态(都是画,但是画的图案不一样)。

  • 多态好处
    • 1.能够降低代码的“圈复杂度”,避免大量使用“if-else”。
    • eg:我们通过if-else语句打印○,矩形,○,矩形,△,其代码如下:
代码语言:javascript
代码运行次数:0
复制
package demo2;
class Shape {
    public  void draw(){
        System.out.println("画图形!");
    }
}

class Cycle extends Shape{

    @Override
    public void draw() {
        System.out.println("○");
    }
}

class Rect extends Shape{
    @Override
    public void draw() {
        System.out.println("矩形");
    }
}

class Triangle extends Shape{
    @Override
    public void draw() {
        System.out.println("△");
    }
}

public class Test {
//************************************************
    public static void main(String[] args) {
        Rect rect = new Rect();
        Cycle cycle = new Cycle();
        Triangle triangle = new Triangle();

        String[] strings = {"○","矩形","○","矩形","△"};
        for(String s : strings){
            if(s.equals("○")){
                cycle.draw();
            } else if (s.equals("矩形")) {
                rect.draw();
            }else {
                triangle.draw();
            }
        }
//************************************************
    }
  • 通过多态进行打印,其代码如下所示:
代码语言:javascript
代码运行次数:0
复制
package demo2;
class Shape {
    public  void draw(){
        System.out.println("画图形!");
    }
}

class Cycle extends Shape{

    @Override
    public void draw() {
        System.out.println("○");
    }
}

class Rect extends Shape{
    @Override
    public void draw() {
        System.out.println("矩形");
    }
}

class Triangle extends Shape{
    @Override
    public void draw() {
        System.out.println("△");
    }
}

public class Test {
    //*****************通过多态打印*******************************
    public static void main(String[] args) {
        Rect rect = new Rect();
        Cycle cycle = new Cycle();
        Triangle triangle = new Triangle();

        //数组当中放的是相同数据类型的元素,这样写相当于向上转型,他和下面的注释代码等价
        Shape [] shapes = {cycle,rect,cycle,rect,triangle};
//        Shape rect = new Rect();
//        Shape cycle = new Cycle();
//        Shape triangle = new Triangle();
        for(Shape myshape : shapes){
            myshape.draw();
        }

    }
  • 2.多态的可扩展能力强,如果要新增一种新的形状,使用多态的方式代码改动成本也比较低(如下代码中我们为其增加了打印“❀”的代码)。
代码语言:javascript
代码运行次数:0
复制
package demo2;
class Shape {
    public  void draw(){
        System.out.println("画图形!");
    }
}

class Flower extends Shape{
    @Override
    public void draw() {
        System.out.println("❀");
    }
}

class Cycle extends Shape{

    @Override
    public void draw() {
        System.out.println("○");
    }
}

class Rect extends Shape{
    @Override
    public void draw() {
        System.out.println("矩形");
    }
}

class Triangle extends Shape{
    @Override
    public void draw() {
        System.out.println("△");
    }
}
public class Test {
    //*****************通过多态打印*******************************
    public static void main(String[] args) {
        Rect rect = new Rect();
        Cycle cycle = new Cycle();
        Triangle triangle = new Triangle();
        Flower flower = new Flower();
        //数组当中放的是相同数据类型的元素,这样写相当于向上转型,他和下面的注释代码等价
        Shape [] shapes = {cycle,rect,cycle,rect,triangle,flower};
//        Shape rect = new Rect();
//        Shape cycle = new Cycle();
//        Shape triangle = new Triangle();
        for(Shape myshape : shapes){
            myshape.draw();
        }
    }
  • 多态的缺点:代码运行效率低
    • 1.属性没有多态性。当父类和子类都具有同名属性时,通过父类引用,只能引用父类自己的属性。
    • 2.构造方法没有多态性。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-12-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 多态实现条件
  • 重写
    • 不能进行重写的情况
    • 能够重写的条件
    • 普通重写
    • 类型之间构成父子关系的重写
  • 向上转型
  • 向下转型
  • 避免在构造方法中调用重写方法
  • 多态的优缺点
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档