Java 多态及实例

得意时要看淡,失意时要看开。不论得意失意,切莫大意;不论成功失败,切莫止步。志得意满时,需要的是淡然,给自己留一条退路;失意落魄时,需要的是泰然,给自己觅一条出路Java 多态及实例,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

转载于:Java多态

多态是某一事物,在不同时刻体现出来的不同的状态

就比如猫,狗,狼都是动物,我们可以说猫是动物,说狗是动物
但我们不能说动物是猫,动物是狼。也不能说猫是狗,或者狗是狼。
上面这几点都会在多态中体现出来

多态的前提:

  1. 类之间要有继承的关系
  2. 子类要有对父类方法的重写(不重写也不会报错,但这样体现不出来多态的意义)
  3. 父类的引用变量指向子类对象

下面看一个多态的简单例子

class Animal{
    public void eat(){
        System.out.println("动物要进食");
    }
}
class Cat extends Animal{
    public void eat(){
        System.out.println("猫咪要吃饭");
    }
}
public class Demo1 {
    public static void main(String[] args) {
        //从右往左念,猫咪是动物
        //new Cat()已经创建了一个Cat类的对象,在堆内存之中
        //Animal类的引用变量指向了Cat类的子类对象
        Animal a = new Cat();
        //这里会优先调用子类的重写方法
        a.eat();
    }
}

结果为
在这里插入图片描述
这里需要注意的是,虽然是父类的引用变量,但实际上调用eat()方法时,调用的是子类重写后的eat()方法

如果对重写不了解的话,可以看下面链接里面的博客
继承里面的重写问题

多态访问成员特点
1、成员变量
编译看左边,运行看左边
2、构造方法
创建子类对象的时候,先访问父类的构造方法,先对父类的数据进行初始化
3、成员方法
编译看左边,运行看右边
因为子类的成员方法存在重写,所以运行访问的时候看右边
4、静态的成员方法:
编译看左边,运行看左边
静态的随着类的加载而加载
由于被static修饰的都是与类有关系的,所以它算不上重写
所以,编译的时候看左边,运行访问的时候也看左边

这里的左边指的是父类的引用变量,右边指的是子类的对象
在这里插入图片描述
这个单独看文字比较难懂,所以下面还是要看几个例子
首先是第一条,成员变量,编译看左边,运行看左边

class Animal{
    int num =10;
    public void eat(){
        System.out.println("动物要进食");
    }
}
class Cat extends Animal{
    int num = 20;
    public void eat(){
        System.out.println("猫咪要吃饭");
    }
}
public class Demo1 {
    public static void main(String[] args) {
        Animal a = new Cat();
        System.out.println(a.num);
    }
}

输出结果为 10
编译和运行都看左边的意思是,输出的结果根据父类的属性值来定,而不是子类

然后是第二条,可以看这篇博客的后半部分,关于构造方法调用顺序的详细解释
关于构造方法调用顺序的详细解释

第三条成员方法,编译看左边,运行看右边

package test1;
class Father1{
    public Father1() {
    }
    public void show(){
        System.out.println("父类的show方法");
    }
}
class Son1 extends Father1{
    public Son1() {
    }
    public void show(){
        System.out.println("子类的show方法");
    }
}
public class duotaiDemo3 {
    public static void main(String[] args) {
        Father1 f = new Son1();
        f.show();
    }
}

结果为
在这里插入图片描述
而当子类没有重写这个方法时,会从父类中继承该方法,然后调用该方法

package test1;
class Father1{
    public Father1() {
    }
    public void show(){
        System.out.println("父类的show方法");
    }
}
class Son1 extends Father1{
    public Son1() {
    }
//    public void show(){
//        System.out.println("子类的show方法");
//    }
}
public class duotaiDemo3 {
    public static void main(String[] args) {
        Father1 f = new Son1();
        f.show();
    }
}

在这里插入图片描述

编译看左边的意思是编译过程中看父类中是否有这个方法
运行看右边的意思是运行过程中实际调用的方法是子类中重写后的方法

第四条静态的成员方法

package review;
class Animal{
    int num =10;
    public void eat(){
        System.out.println("动物要进食");
    }
    public static void sleep(){
        System.out.println("动物要休息");
    }
}
class Cat extends Animal{
    int num = 20;
    public void eat(){
        System.out.println("猫咪要吃饭");
    }
    public static void sleep(){
        System.out.println("猫咪要睡觉");
    }
}
public class duotaiDemo {
    public static void main(String[] args) {
        Animal a = new Cat();
        a.sleep();
        System.out.println("------------");
        Animal.sleep();
        Cat.sleep();
    }
}

结果为
在这里插入图片描述
用对象调用时,调用的是父类的静态成员方法
用类名调用时,都是打印相对应的结果

介绍完多态访问成员的特点后,我们需要思考一个问题,为什么要使用,以及使用多态有什么好处
下面来看这个例子

class Animal{
    public void eat(){
        System.out.println("动物要进食");
    }
    public void sleep(){
        System.out.println("动物要休息");
    }
}
class Cat extends Animal{
    public void eat(){
        System.out.println("猫咪要吃饭");
    }
    public void sleep(){
        System.out.println("猫咪要睡觉");
    }
}
class Dog extends Animal{
    public void eat(){
        System.out.println("狗要啃骨头");
    }
    public void sleep(){
        System.out.println("狗要打盹");
    }
}
public class Demo1 {
    public static void main(String[] args) {
        Cat c = new Cat();
        c.eat();
        c.sleep();
        Dog d = new Dog();
        d.eat();
        d.sleep();
    }
}

这里发现,如果输出猫和狗的实例对象的eat和sleep方法的结果,就需要分别用Cat类的对象c来调用,用Dog类的对象d来调用。这里是因为对象较少,调用的方法也只有eat和sleep两个方法,所以看起来还算简洁。

但如果我要创建十只猫的实例呢?要c1.eat(),c2.sleep()……这样一直调用下去吗?
那如果我不止猫和狗两种动物呢?我还要写狼的类,写豹子的类,写大象的类,都继承Animal类,然后输出这些类的对象调用方法的结果。

这时就会发现,代码的调用方法部分将变得非常冗长,有没有一种办法,可以让我们不需要用实例对象一个一个的调用方法呢?
这时候我们就需要用到多态了。而我们需要做的事情,就是创建对象,并且将对象作为参数传入一个工具类中,工具类里面会定义各种需要调用的方法。

class Animal{
    public void eat(){
        System.out.println("动物要进食");
    }
    public void sleep(){
        System.out.println("动物要休息");
    }
}
class Cat extends Animal{
    public void eat(){
        System.out.println("猫咪要吃饭");
    }
    public void sleep(){
        System.out.println("猫咪要睡觉");
    }
}
class Dog extends Animal{
    public void eat(){
        System.out.println("狗要啃骨头");
    }
    public void sleep(){
        System.out.println("狗要打盹");
    }
}
class Bird extends Animal{
    public void eat(){
        System.out.println("鸟儿吃虫子");
    }
    public void sleep(){
        System.out.println("鸟儿休憩");
    }
}
class AnimalTools {
//private的作用是让外面无法创建AnimalTools类的对象,只能使用这里面的方法
    private AnimalTools() {
    }
    public static void funAnimal(Animal animal){
        animal.eat();
        animal.sleep();
    }
}
public class Demo1 {
    public static void main(String[] args) {
        Cat c = new Cat();
        Dog d = new Dog();
        Bird b = new Bird();
        AnimalTools.funAnimal(c);
        AnimalTools.funAnimal(d);
        AnimalTools.funAnimal(b);
        Cat c2 = new Cat();
        AnimalTools.funAnimal(c2);
    }
}

结果为
在这里插入图片描述

多态的好处:
提高了程序的维护性(由继承保证)
提高了程序的扩展性(由多态保证)


引用2:Java多态详解

多态的实现机制

基于继承和接口实现的多态机制主要表现在父类和继承该父类的一个或多个子类对某些方法的重写,多个子类对同一方法的重写可以表现出不同的行为。多态:多种形态,就是多态性

多态规则

指向子类的父类引用由于向上转型了,它只能访问父类中拥有的方法和属性,而对于子类中存在而父类中不存在的方法,该引用是不能使用的,尽管是重载该方法。若子类重写了父类中的某些方法,在调用该些方法的时候,必定是使用子类中定义的这些方法,他们优先级高于父类中的方法。

多态的经典例子

class A {
    public String show(D obj) {
        return ("A and D");
    }

    public String show(A obj) {
        return ("A and A");
    } 

}

class B extends A{
    public String show(B obj){
        return ("B and B");
    }
    
    public String show(A obj){
        return ("B and A");
    } 
}

class C extends B{

}

class D extends B{

}

public class Test {
    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new B();
        B b = new B();
        C c = new C();
        D d = new D();
        
        System.out.println("1--" + a1.show(b));//A and A
        System.out.println("2--" + a1.show(c));//A and A
        System.out.println("3--" + a1.show(d));//A and D

        System.out.println("4--" + a2.show(b));//B and A
        System.out.println("5--" + a2.show(c));//B and A
        System.out.println("6--" + a2.show(d));//A and D
        System.out.println("7--" + b.show(b));//B and B
        System.out.println("8--" + b.show(c));//B and B
        System.out.println("9--" + b.show(d));//A and D
    }
}

继承链中对象方法的调用存在优先级

this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)

分析第5个:a2.show( c )

  1. this.show(O):a2是A类型的引用变量,所以this就代表了a2,a2.show©,它在A类中找发现没有找到
  2. super.show(O):于是到A的超类中找(super),由于A没有超类(Object除外),所以跳过
  3. this.show((super)O):c的超类有B、A,所以(super)O为B、A,this同样是A,这里在A中找到了show(A obj)
  4. 运行时类型:由于a2是B类的一个引用且B类重写了show(A obj),因此最终会调用子类B类的show(A obj)方法,结果也就是B and A。

注意

当超类对象引用变量引用子类对象时,被引用对象的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过

多态分析步骤

  1. this.show(O)
    this指代的是编译时类型

  2. super.show(O)
    super指代的是编译时类型的父类

  3. this.show((super)O)
    super指代的是参数O类型的父类

  4. super.show((super)O)

  5. 查看运行时类型是否重写了改方法
    以上四个步骤,先找到对应方法的直接跳到第五个步骤执行
    是:执行运行时类型类中的此方法
    否:执行编译时类型类中的此方法

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/157133.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!