彻底搞懂Java多态

目录

引例 - -认识多态  

向上转型

    向上转型发生场景

    方法覆写(override)

    方法覆写的三个要求:

向下转型

向下转型的风险!

instanceof关键字


引例

面向对象编程有三大特征:封装、继承、多态。现在我们对多态展开详细的了解。

  •  在现实中,如老师,一位具体的老师他既是老师,也是,既表现出了多种形态
  •  在Java语言中,多态就是一个引用可以表现出多种状态例如:

我们定义两个类:

public class Animal {
    String name;
    public Animal(){}
    public Animal(String name){
        this.name=name;
    }
    public void eat(String food) {
        System.out.println(name+"正在吃"+food);
    }
}
public class Dog extends Animal{
    String name;
    public Dog(String name){
       super(name);//调用父类的有参构造
    }
    public void eat(String food) {
        System.out.println(name+"正在吃"+food);
    }

public static void main(String[] args) {
       Animal animal = new Animal("动物");
        Dog dog = new Dog("小狗");
        animal.eat("食物");
        dog.eat("骨头");
    }

运行结果:

可以看到两个对象调用的都是eat方法,运行结果却不一样,这就是多态的表现。


向上转型

  • 语法:父类  引用名称  =  new  子类实例();那么向上转型怎么使用呢,承接上面的例子。我们做如下转换,将dog引用前面的类换成父类Animal,运行结果和上面一样

Dog继承Animal之间是一种天然的 is a 关系 :dog ia an animal, 所以可以自然而然的向上转型。

有了向上转型之后,我们将代码做如下转变:

public class Test {
    public static void fun(Animal animal,String food){
        animal.eat(food);
    }
    public static void main(String[] args) {
        Animal animal = new Animal("动物");
        Animal dog = new Dog("小狗");
         fun(animal,"食物");
        fun(dog,"骨头");
    }


}

运行结果: 

 

上述代码也中的Animal dog = new Dog("小狗");也可以写成Dog dog = new Dog("小狗"); 

变成Dog类对象后,依然可以使用fun方法,那么,fun方法里是Animal对象,为什么Dog对象可以使用呢?这就是向上转型的原因。只要对象是Animal和它的子类,都可以传入fun方法。

也就是说,不论Animal有多少个子类,fun方法只需要写一次,将参数定义为Animal就可以接受所有它的子类对象。

  • 另个我们知道Dog类和Animal类都有完全相同的eat方法,当主方法如下写时:
Animal animal = new Animal("动物");
Animal dog = new Dog("小狗");
animal.eat("食物");
dog.eat("骨头");

dog向上转型成了Animal类的引用,那么它调用的是哪个类的对象呢? 

其实,当发生向上转型时,若调用的方法子类和父类中都有,我们要看是通过哪个类new的对象,该对象就调用该类的方法。

向上转型发生场景

  1. 产生对象时:Animal dog = new Dog();
  2. 方法参数传递:既上面的例子,产生的是Dog类的对象,但用fun方法接收。在传递Dog对象时发生了向上转型。
  3. 方法返回值:如下代码,我们new的是Dog类的实例,test方法返回的却是Animal对象,这就是在返回的时候发生了向上转型。
     public static Animal test(){
            return new Dog("狗狗");
        }
        public static void main(String[] args) {
            Animal a = test();

    综上我们可得:向上转型是为了使参数统一化,父类引用可以接收子类所有对象,同时向上转型是一个非常实用也很重要的概念,读者一定要理解掌握。

方法覆写(override)

定义:发生在有继承关系的类之间,子类定义了和父类完全相同的方法。这种方法就称为方法覆写。

回到我们最开始的例子中:

public static void main(String[] args) {
       Animal animal = new Animal("动物");
        Dog dog = new Dog("小狗");
        animal.eat("食物");
        dog.eat("骨头");
    }

eat()就称为方法覆写。

在IDEA中,子类中方法覆写的旁边有一个这样的标,表示发生了方法覆写,在父类中是一个向下的标。点击一下就可以找到父类/子类的这个方法。

 

方法覆写的三个要求:

        1.只能重写成员方法,不能重写静态方法

//在父类写一个test方法
public static void test(){
    System.out.println("Animal's test");
}

//在子类写一个test方法

public static void test(){
    System.out.println("Dog's test");
}

//在主方法中测试

Animal animal = new Animal("动物");
 Animal dog = new Dog("小狗");     
        animal.test();
        dog.test();

输出结果为: 

可见没有调用Dog类中的test方法,而且在IDEA中没有出现方法覆写的图标,所有这不是方法覆写。

 

     2.子类中方法的权限修饰符权限要大于或等于父类的  (权限修饰符:private < default < protected < public)

        另外,方法覆写时不能出现private权限。当方法是包访问权限(default)时,若父类和子类不在同一个包中,就无法方法覆写。

     3.方法覆写的返回值是基本类型时必须相同,否则至少是向上转型的返回值

若父类方法返回值类型是int,而子类是double,编译就会报错 。那么向上转型的返回值是怎样的的呢?

//父类
public Animal func(){
    return new Animal("动物");
}

//子类

public Dog func(){
    return new Dog("狗狗·");
}

这样就是方法返回值是向上转型的使用,func()一个方法覆写。

向下转型

需要使用子类独有的方法时使用向下转型,而且向下转型之前一定要先发生向上转型。由于父类不一定是子类的类型,所有向下转型必须要强制类型转换。

什么叫父类不一定是子类的类型呢?上面我们提到dog ia an animal,但是animal is a dog吗,显然不一定。

Animal animal = new Dog();

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

 这里可以发生向下转型的原因是已经发生了向上转型,这个时候就可以使用dog引用去访问Dog类中独有的方法了。

!!要注意,向下转型是有风险的!!

1.如果发生强制转换的两个类之间没有关系,那么一定会出错,例如:

Cat  cat  =  new  Cat() ;  //猫类

Dog  dog  =  (dog) cat ;//编译错误

 上述是强制转换出的错误,两个类之间没有联系,所以编译就会出错

2.现在我们在Dog中写一个方法

public void yell(){ System.out.println("汪汪"); }

yell方法是Dog类独有的,Animal对象要调用就要使用向下转型

Animal animal = new Animal("动物");
       Dog dog1 = (Dog) animal;
       dog1.func();

我们发现编译没有出错,而运行时却出错了,ClassCastException是类型转换异常,大多发生在向下转型时父类引用没有和子类建立联系时。细读代码我们发现animal就是Animal类的对象,没有发生向上转型,所以出错。

instanceof关键字

如何规避向下转型出现错误呢,Java中使用instanceof关键字,它返回一个布尔值,表示一个引用能否指向一个类的实例

if(animal instanceof Dog){
    Dog dog1 = (Dog) animal;
    dog1.yell();
}

 


 

  • 14
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值