JAVA SE: 多态

目录

1.概念

2.多态发生的条件

3.向上转型 和 向下转型

3.1 向上转型

n3.2 向下转型 

4.多态的优缺点 

 4.1 优点

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

2.可扩展能力更强

 4.2 缺点

1.代码的运行效率降低。


1.概念

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

多态的概念是有些抽象的,先了解下面的一些知识点,再回来重新阅读概念,会有新的理解。

2.多态发生的条件

知识准备:什么是重写?

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

拿动物类举例:

public class Animal {
    public int age;
    public String name;

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

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

}

public class Cat extends Animal{

    public Cat(int age, String name) {
        super(age, name);
    }

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

    public void mew(){
        System.out.println(this.name+"喵喵叫");
    }
}

public class Dog extends Animal{

    public Dog(int age, String name) {
        super(age, name);
    }

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

     public void bark(){
        System.out.println(this.name+"汪汪叫");
    }

}


public class Test {
     public static void main(String[] args) {
        Animal animal1 = new Dog(12,"菜询困");
        Animal animal2 = new Cat(21,"啥鸟");

        animal1.eat();
        animal2.eat();//调用了子类重写的方法
    }
}

 对于Animal类的eat方法进行了重写。

运行结果:

 

重写的规则:

子类在重写父类的方法时,一般必须与父类方法原型一致: 返回值类型 方法名 (参数列表) 要完全一致

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

访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被public修饰,则子类中重写该方法就不能声明为 protected

父类被static、private修饰的方法、构造方法都不能被重写。

重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验. 例如不小心 将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法, 就会编译报错, 提示无法 构成重写.

静态绑定:也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代
表函数重载。
动态绑定:也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体调用那个类的方法。 

了解了什么是重写,看看多态发生的条件。 

多态发生的条件有三点:

1.发生在继承体系下

2.存在子类对于父类方法的重写

3.通过父类调用重写的方法

3.向上转型 和 向下转型

3.1 向上转型

向上转型实际上就是通过父类引用接收子类对象来使用。翻译成大白话就是创建一个子类对象,将其当成父类对象来使用。

语法格式:父类类型 对象名 = new 子类类型()

向上转型的使用场景有三种:1.直接复制 2.作为参数传参 3.作为返回值 

 直接上例子:

public class TestAnimal {
    // 2. 方法传参:形参为父类型引用,可以接收任意子类的对象
    public static void eatFood(Animal a){
        a.eat();
   }
 
    // 3. 作返回值:返回任意子类对象
    public static Animal buyAnimal(String var){
        if("狗".equals(var) ){
            return new Dog("狗狗",1);
       }else if("猫" .equals(var)){
            return new Cat("猫猫", 1);
       }else{
            return null;
       }
   }
 
    public static void main(String[] args) {
        Animal cat = new Cat("元宝",2);   // 1. 直接赋值:子类对象赋值给父类对象
        Dog dog = new Dog("小七", 1);
 
        eatFood(cat);
        eatFood(dog);
 
        Animal animal = buyAnimal("狗");
        animal.eat();
 
        animal = buyAnimal("猫");
        animal.eat();
   }
}

向上转型的优点:让代码实现更简单灵活。
向上转型的缺陷:不能调用到子类特有的方法。

n3.2 向下转型 

当使用向上转型后是无法再调用子类独有的方法的,这个时候就用到向下转型了。此时:将父类引用再还原为子类对象即可,即向下转换。

 

看代码:

public class Test {
     public static void main(String[] args) {
        Animal animal1 = new Dog(12,"菜询困");
        Animal animal2 = new Cat(21,"啥鸟");

        animal1.eat();
        animal2.eat();//调用了子类重写的方法

         //animal1.bark();
         //报错原因:向上转型无法调用子类独有的方法
         //报错信息:java: 找不到符号 符号:   方法 bark() 位置: 类型为demo1.Animal的变量 
         //animal1

         //向下转型
         Dog dog = (Dog)animal1;
         dog.bark();
        
         

    }
}



//执行结果
//菜询困正在吃狗粮
//啥鸟正在吃猫粮
//菜询困汪汪叫

向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛异常。Java中为了提高向下转型的安全性,引入 了 instanceof ,如果该表达式为true,则可以安全转换。

public class Test {
     public static void main(String[] args) {
        Animal animal1 = new Dog(12,"菜询困");
        Animal animal2 = new Cat(21,"啥鸟");

        animal1.eat();
        animal2.eat();//调用了子类重写的方法

         //animal1.bark();
         //报错原因:向上转型无法调用子类独有的方法
         //报错信息:java: 找不到符号 符号:   方法 bark() 位置: 类型为demo1.Animal的变量 animal1

         //向下转型
         Dog dog = (Dog)animal1;
         dog.bark();
         //dog.mew(); 报错animal1实际指向Dog,故不能调用Cat里的独有方法
         //为了解决这类问题:引入关键字 instanceof
         Cat cat = (Cat)animal2;
         if(animal2 instanceof Dog){
             dog.bark();
         }else{
             cat.mew();;
         }

    }
}

4.多态的优缺点 

 4.1 优点

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

看个例子:
我们三个类:

public class shape {
    public void draw(){
        System.out.println("画图形!");
    }
}

public class Cycle extends shape{
    @Override
    public void draw() {
        System.out.println("●");
    }
}

public class Rect extends shape{
    public void draw(){
        System.out.println("♦");
    }
}

站在调用者的角度,不使用多态的思想就是这样:

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

        String[] strings = {"cycle","rect","rect","cycle"};

        for (String  string:
             strings) {
            if(string.equals("cycle")){
                cycle.draw();
            }
            else if(string.equals("rect")){
                rect.draw();
            }
        }
    }
}

 使用多态的思想:

public class Test {    

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

        shape[] shapes = {cycle,rect,rect,cycle};

        for (shape shape:
             shapes) {
            shape.draw();
        }

    }
}

它们的运行结果相同,但是后者明显更简便、更清晰。 

2.可扩展能力更强

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

 4.2 缺点

1.代码的运行效率降低。

 1. 属性没有多态性

当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性

2. 构造方法没有多态性

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值