超详细Java基础-多态

茫茫人海千千万万,感谢这一秒你看到这里。但愿个人能对你的有所帮助!共勉!java

愿你在将来的日子,保持热爱,奔赴山海!编程

Java基础知识(多态)

多态

多态就是指程序中定义的引用变量所指向的具体类型和经过该引用变量发出的方法调用在编程时并不肯定,而是在程序运行期间才肯定,即一个引用变量到底会指向哪一个类的实例对象,该引用变量发出的方法调用究竟是哪一个类中实现的方法,必须在由程序运行期间才能决定。学习

由于在程序运行时才肯定具体的类,这样,不用修改源程序代码,就可让引用变量绑定到各类不一样的类实现上,从而致使该引用调用的具体方法随之改变,即不修改程序代码就能够改变程序运行时所绑定的具体代码,让程序能够选择多个运行状态,这就是多态性。测试

多态的定义和存在的必要条件

多态的定义code

  • 多态是指同一个行为具备多个不一样表现形式或形态的能力。
  • 多态就是同一个接口,使用不一样的实例而执行不一样操做。

就举动物类的例子吧,cat和dog都是属于动物这一类,而动物呢,都有一个共同的行为就是吃吧,而不一样的动物所吃的食物都大不相同吧!对象

呢,它喜欢吃鱼blog

而对于狗呢,它就比较喜欢啃骨头继承

因此多态就是对于吃这一行为来讲,每种动物对吃这一行为所表现的行为都不尽相同。接口

多态存在的三个必要条件io

  1. 继承或者实现

    在多态中必须存在有继承或者实现关系的子类和父类。

  2. 方法的重写

    子类对父类中某些方法进行从新定义(重写),在调用这些方法时就会调用子类的方法。

  3. 基类引用指向派生类对象,即父类引用指向子类对象

    父类类型:指子类对象继承的父类类型,或者实现的父接口类型。

多态的格式:

父类类型 变量名 = new 子类类型();

变量名.方法名();

多态格式能够充分体现了同一个接口,使用不一样的实例而执行不一样操做。

接下来咱们具体来进行案例体会体会吧!

多态的案例

多态咱们首先要知道的一点:

当使用多态方式调用方法时,首先检查父类中是否有该方法,若是没有,则编译错误;若是有,执行的是子类重写后方法。若是子类没有重写该方法,就会调用父类的该方法

总结起来就是:编译看左边,运行看右边。

首先咱们先定义一个父类动物类,动物有吃的行为!

接着定义一个猫类和狗类去继承动物类,重写里面的吃行为!

具体代码以下

定义动物父类:

package com.nz.pojo;

/**
 * 先定义一个父类 --> 动物类
 * 动物都有一个吃的行为属性
 */
public class Animal {

    public void eat() {
        System.out.println("动物它们都会吃东西!!!");
    }
}

定义猫咪子类:

package com.nz.pojo;

/**
 * 定义猫类继承动物类,
 * 随之重写里面的吃行为,由于猫也有吃的行为,可是猫喜欢吃罐头
 */
public class Cat extends Animal{

    public void eat() {
        System.out.println("小喵咪都喜欢吃鱼罐头!");
    }
}

定义小狗子类:

package com.nz.pojo;

/**
 * 定义狗类继承动物类,
 * 随之重写里面的吃行为,由于狗也有吃的行为,可是狗喜欢啃骨头
 */
public class Dog extends Animal{

    public void eat() {
        System.out.println("小狗狗都爱啃骨头!");
    }
}

定义测试类,测试多态的形式:

package com.nz;

import com.nz.pojo.Animal;
import com.nz.pojo.Cat;
import com.nz.pojo.Dog;

/**
 * 测试多态的形式
 */
public class Demo {
    public static void main(String[] args) {

        // 多态形式,建立猫类对象
        Animal animal = new Cat();
        // 调用的是Cat的 eat
        animal.eat();

        // 多态形式,建立狗类对象
        Animal animal2 = new Dog();
        // 调用的是Dog的eat
        animal2.eat();
    }
}

获得的结果:

小喵咪都喜欢吃鱼罐头!
小狗狗都爱啃骨头!

类的大体结构:

能够看出咱们可使用多态的属性获得不一样的动物的一个吃的行为属性!

多态的好处

提升了代码的拓展性,使用父类类型做为方法形式参数,传递子类对象给方法,进行方法的调用。

具体咱们来看看吧:

继续使用上述的动物类、猫类、狗类吧。

定义测试类:

package com.nz;

import com.nz.pojo.Animal;
import com.nz.pojo.Cat;
import com.nz.pojo.Dog;

/**
 * 测试多态的好处
 */
public class Demo2 {
    public static void main(String[] args) {
        // 建立猫和狗对象
        Cat cat = new Cat();
        Dog dog = new Dog();

        // 调用catEat
        catEat(cat);
        // 调用dogEat
        dogEat(dog);

		/*
        多态的好处:
            以上各个动物的吃的方法, 咱们均可以使用animalEat(Animal a)方法来代替。
            而且执行效果都同样, 因此咱们可使用animalEat直接替代了不一样动物的吃方法。
        */
        animalEat(cat);
        animalEat(dog);
    }

    /*
        定义几个不一样吃的方法,看看具体调用后的结果是什么吧!
     */
    public static void catEat (Cat cat){
        cat.eat();
    }

    public static void dogEat (Dog dog){
        dog.eat();
    }

    public static void animalEat (Animal animal){
        animal.eat();
    }
}

执行结果:

小喵咪都喜欢吃鱼罐头!
小狗狗都爱啃骨头!
小喵咪都喜欢吃鱼罐头!
小狗狗都爱啃骨头!

能够看出,因为多态的特性,咱们的animalEat()方法传入的Animal类型参数,而且它是咱们的Cat和Dog的父类类型,父类类型接收子类对象,因此咱们能够将Cat对象和Dog对象,传递给animalEat()方法。

因此咱们能够彻底使用animalEat()方法来替代catEat()方法和dogEat()方法,达到一样的效果!以致于咱们能够没必要再单独写xxxEat()方法来传入指定的动物参数了,从而实现了实现类的自动切换。

因此多态的好处体如今:可使咱们的程序编写的更简单,并有良好的扩展性。

多态的弊端

从上面的多态的好处,能够看到咱们可使用父类的参数代替了某个子类的参数,从而达到程序的扩展!

可是对于某个子类有些独有的功能方法时,此时咱们的多态的写法就没法访问子类独有功能了

具体来瞧瞧?

代码以下:

从新定义下猫的子类:

package com.nz.pojo;

/**
 * 定义猫类继承动物类,
 * 随之重写里面的吃行为,由于猫也有吃的行为,可是猫喜欢吃罐头
 */
public class Cat extends Animal{

    public void eat() {
        System.out.println("小喵咪都喜欢吃鱼罐头!");
    }

    /**
     * 增长一哥猫咪特有的玩球方法()
     */
    public void playBall() {
        System.out.println("小喵咪都喜欢小球!");
    }
}

定义测试类:

package com.nz;

import com.nz.pojo.Animal;
import com.nz.pojo.Cat;

/**
 * 测试多态的弊端!
 */
public class Demo3 {
    public static void main(String[] args) {
        Animal animal = new Cat();

        animal.eat();

        animal.playBall();//编译报错,编译看左边,Animal没有这个方法
    }
}

能够看到动物类和猫类有个共同的eat吃方法,可是呢,猫咪多了个玩球球的方法。而对于动物对象来讲,它自己动物类没有玩球球的方法,因此它的编译就直接没有经过了!

那有什么方法解决呢?且看下一章节吧!

引用类型转换

1. 引用类型转换是什么,为何须要它?

从上面的多态的弊端的案例中,咱们能够看到,咱们使用动物对象时没法直接访问到猫类中的玩球球方法,这也就是咱们以前说的编译看左边,运行看右边。

而在咱们使用多态方式调用方法时,首先检查会左边的父类中是否有该方法,若是没有,则编译错误。也就表明着,父类没法调用子类独有的方法。、

因此说,若是编译都错误,更别说运行了。这也是多态给咱们带来的一点小困扰,而咱们若是想要调用子类特有的方法,必须作向下转型。

2. 向上转型(自动转换)

对于向下转型,咱们先来说解下向上转型的概念吧。

向上转型

多态自己是子类向父类向上转换(自动转换)的过程,这个过程是默认的。当父类引用指向一个子类对象时,即是向上转型。

对于父类和子类的关系来讲,具体来看图说话:

父类相对与子类来讲是大范围的类型,Animal是动物类,是父类。而Cat是猫咪类,是子类。

因此对于父类Animal来讲,它的范围是比较大的,它包含一切动物,包括猫咪类和小狗类。

因此对于子类类型这种范围小的,咱们能够直接自动转型给父类类型的变量。

使用格式:

父类类型 变量名 = new 子类类型();

如:Animal animal = new Cat();

至关于有:
Animal animal = (Animal) new Cat();

至关于自动帮咱们了一个隐形的转换为动物类的一个过程,由于动物自己就包含了猫咪。

3. 向下转型(强制转换)

向上转型能够知道它是子类自动转换为父类的一个过程,因此咱们如今再来看看向下转型的定义:

向下转型

向下转型就是由父类向子类向下转换的过程,这个过程是强制的。一个须要将父类对象转为子类对象,可使用强制类型转换的格式,这即是向下转型。

为何这种就必须本身强制加上一个类型转换过程呢?

对于父类和子类的关系来讲,咱们接着看图说话:

对于猫咪类的话,它在动物类中只是其中的一部分吧,而对于动物类来讲,它有许多其余子类动物如狗,牛,猪等等。

因此对于动物父类想要向下转型的时候, 它此时不知道指向那个子类,由于不肯定呀,因此就必须本身加上强制的类型转换的一个过程。

使用格式:

子类类型 变量名 = (子类类型) 父类变量名;
如:
Animal animal = new Cat();
Cat cat = (Cat) animal;
cat.playBall();// 此时咱们就可使用猫咪的特有方法啦

因此对于多态的弊端,没法使用子类特有的参数,咱们也解决啦,能够经过向下转型的方法,从而将类型强制转换为某个子类对象后,再去调用子类的特有方法!

4. 向下转型的问题

虽然咱们可使用向下转型使得咱们可使用子类的独有方法,可是转型的过程当中,一不当心就会遇到这样的问题了,来,咱们来看看下面的代码:

public class Test {
    public static void main(String[] args) {
        // 向上转型  
        Animal a = new Cat();  
        a.eat();               // 调用的是 Cat 的 eat

        // 向下转型  
        Dog d = (Dog)a;       
        d.watchHouse();        // 调用的是 Dog 的 watchHouse 【运行报错】
    }  
}

这段代码能够经过编译,可是运行时,却报出了 ClassCastException ,类型转换异常!这是由于,明明建立了Cat类型对象,运行时,固然不能转换成Dog对象的。

5. 转型的异常

转型的过程当中,一不当心就会遇到这样的问题,请看以下代码:

定义狗类中额外的独有遛狗方法:

package com.nz.pojo;

/**
 * 定义狗类继承动物类,
 * 随之重写里面的吃行为,由于狗也有吃的行为,可是狗喜欢啃骨头
 */
public class Dog extends Animal{

    public void eat() {
        System.out.println("小狗狗都爱啃骨头!");
    }

    public void walk() {
        System.out.println("小狗在被我溜着!");
    }
}

定义测试类

package com.nz;

import com.nz.pojo.Animal;
import com.nz.pojo.Cat;
import com.nz.pojo.Dog;

/**
 * 测试多态的向下转型的问题
 */
public class Demo4 {
    public static void main(String[] args) {

        // 向上转型的过程
        Animal animal = new Cat();

        // 调用了猫咪的吃方法
        animal.eat();

        // 向下转型
        Dog dog = (Dog) animal;

        dog.walk(); // 调用的是 Dog 的 walk 能够经过,可是会运行报错
    }
}

获得结果:

小喵咪都喜欢吃鱼罐头!
Exception in thread "main" java.lang.ClassCastException: com.nz.pojo.Cat cannot be cast to com.nz.pojo.Dog
	at com.nz.Demo4.main(Demo4.java:20)

咱们能够看到,虽然咱们的代码经过编译,可是终究在运行时,仍是出错了,抛出了 ClassCastException 类型转换的异常。

其实咱们能够知道,咱们在上面的时候,建立了Cat类型对象,而在向下转型时,将其强行转换为了Dog类型,因此程序在运行时,就会抛出类型转换的异常!

那咱们如何能够避免这种异常发生呢?且看下一节分析!

6. instanceof关键字

Java为咱们提供一个关键字instanceof ,它能够帮助咱们避免了ClassCastException 类型转换异常的发生。

那如何作呢?

格式:

变量名 instanceof 数据类型

解释:

  • 若是变量属于该数据类型或者其子类类型,返回true。
  • 若是变量不属于该数据类型或者其子类类型,返回false。

代码实现:

package com.nz;

import com.nz.pojo.Animal;
import com.nz.pojo.Cat;
import com.nz.pojo.Dog;

/**
 * 使用instanceof解决类型转换异常!
 */
public class Demo5 {
    public static void main(String[] args) {

        // 向上转型的过程
        Animal animal = new Cat();

        // 调用了猫咪的吃方法
        animal.eat();

        // 向下转型
        if (animal instanceof Cat){
            Cat cat = (Cat) animal;
            cat.playBall();        // 调用的是 Cat 的 playBall
        } else if (animal instanceof Dog){
            Dog dog = (Dog) animal;
            dog.walk();       // 调用的是 Dog 的 walk
        }
    }
}

结果:

小喵咪都喜欢吃鱼罐头!
小喵咪都喜欢小球!

能够发现,它能够帮助咱们在作类型转换前,判断该类型是否属于该类型或者子类类型,若是是,咱们就能够强转啦!

总结

相信各位看官都对Java中的特性之一多态的知识和使用有了必定了解,等待下一次更多Java基础的学习吧!

学到这里,今天的世界打烊了,晚安!虽然这篇文章完结了,可是我还在,永不完结。我会努力保持写文章。来日方长,何惧车遥马慢!

感谢各位看到这里!愿你韶华不负,青春无悔!

注: 若是文章有任何错误和建议,请各位大佬尽情评论留言!若是这篇文章对你也有所帮助,但愿可爱亲切的您给个关注点赞收藏下,很是感谢啦!

相关文章
相关标签/搜索