rinetd 服务注册中心 namespace k8s winforms reflection webview timer 虚拟机 arduino pyqt Backbonejs underscorejs 前端vue框架 网校直播系统 idea生成main方法 sallenkey滤波器 css面试题 java手机验证码 python可视化编程 java初学者 java接口实现 java时间类 java的for循环 如何查看java版本 java自定义异常 java求阶乘 linux命令行 linux简介 id解锁大师 beatedit pr黑场过渡 刷声望 PCCAD 流水账软件 medcalc c4d挤压 php随机数函数 c程序 分屏软件
当前位置: 首页 > 学习教程  > 编程语言

day14_多态

2020/9/19 13:34:44 文章标签:

多态概述

多态(Polymorphism)属于面向对象三大特征之一,它的前提是封装形成独立体,独立体之间存在继承关系,从而产生多态机制。多态就是“同一个行为”发生在“不同的对象上”会产生不同的效果。

前提

  • 继承或者实现【二选一】
  • 方法的重写【意义体现:不重写,无意义】
  • 父类引用指向子类对象【格式体现】

多态就是“同一个行为”发生在“不同的对象上”会产生不同的效果。

对象的多态性:父类的引用指向子类的对象

  • 可以直接应用在抽象类和接口上

                         

Java引用变量有两个类型:编译时类型运行时类型。编译时类型由声明 该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。简 称:编译时,看左边;运行时,看右边。

  • 若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)
  • 多态情况下,“看左边”:看的是父类的引用(父类中不具备子类特有的方法) “看右边”:看的是子类的对象(实际运行的是子类重写父类的方法)

对象的多态 Java,子类的对象可以替代父类的对象使用

  • 一个变量只能有一种确定的数据类型
  • 一个引用类型变量可能指向(引用)多种不同类型的对象

代码举例:

定义父类

package demo03;

public class Person {
	String name;
	int age;
	
	int id = 1001;
	
	public void eat(){
		System.out.println("人:吃饭");
	}
	
	public void walk(){
		System.out.println("人:走路");
	}
	
}

定义子类

package demo03;

public class Man extends Person{
	boolean isSmoking;
	
	int id = 1002;
	
	public void eat(){
		System.out.println("男人多吃肉,长肌肉");
	}
	
	public void walk(){
		System.out.println("男人霸气的走路");
	}

}

定义测试类

package demo03;


/*
 * 面向对象特征之三:多态性
 * 
 * 1.理解多态性:可以理解为一个事物的多种形态。
 * 2.何为多态性:
 *   对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
 *   
 * 3. 多态的使用:虚拟方法调用
 *   有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
 *   总结:编译,看左边;运行,看右边。
 *   
 * 4.多态性的使用前提:  ① 类的继承关系  ② 方法的重写
 * 
 * 5.对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
 */
public class PersonTest {
	public static void main(String[] args) {

		//对象的多态性:父类的引用指向子类的对象
		Person p2 = new Man();
        Person p3 = new Woman();
		//多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法 ---虚拟方法调用
		p2.eat();
		p2.walk();
		p3.eat();
		p3.walk();
		System.out.println(p2.id);//1001
		
	}
}

子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)

在 java 中允许这样的两种语法出现,一种是向上转型(Upcasting),一种是向下转型(Downcasting),向上转型是指子类型转换为父类型,又被称为自动类型转换,向下转型是指父类型转换为子类型,又被称为强制类型转换。请看下图: 

                                                 

在 java 语言中有这样的一个规定,无论是向上转型还是向下转型,两种类型之间必须要有继承关系,没有继承关系情况下进行向上转型或向下转型的时候编译器都会报错,

向上转型

  • 向上转型:多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。 当父类引用指向一个子类对象时,便是向上转型。

使用格式:

向下转型

向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。

使用格式:

多态的好处

  • 方法声明的形参类型为父类类型,可以使用子类的对象作为实参调用该方法,更能体现出多态的扩展性与便子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。

定义父类

public class Pet {
    String name;
    //吃的行为
    public void eat(){
    }
}

定义子类cat

public class Cat extends Pet {
    public Cat(String name) {
        this.name = name;
    }

    //吃的行为
    public void eat() {
        System.out.println(this.name + "在吃鱼!");
    }
}

​​​​​​​定义子类Dog

public class Dog extends Pet {
    public Dog(String name) {
        this.name = name;
    }

    //吃的行为
    public void eat() {
        System.out.println(this.name + "在啃肉骨头!");
    }
}

定义主人

//主人
public class Master {
    //喂养行为
    public void feed(Pet pet) {
        //主人喂养宠物,宠物就吃
        System.out.println("主人开始喂食儿");
        pet.eat();
        System.out.println("主人喂食儿完毕");
    }
}

定义测试类

public class Test {
    public static void main(String[] args) {
        //创建狗对象
        Dog dog = new Dog("二哈");
        //创建主人对象
        Master master = new Master();
        //喂养
        master.feed(dog);
        //创建猫对象
        Cat cat = new Cat("汤姆");
        //喂养
        master.feed(cat);
    }
}

以上程序中,Master 类中的方法 feed(Pet pet)的参数类型定义为更加抽象的 Pet 类型,而 不是具体 Dog 宠物,或者 Cat 宠物,显然 Master 类和具体的 DogCat 类解耦合了,依赖性弱 了,这就是我们通常所说的面向抽象编程,尽量不要面向具体编程,面向抽象编程会让你的代码耦合度降低,扩展能力增强,从而符合 OCP 的开发原则。

多态的弊端

  • 一个引用类型变量如果声明为父类的类型,但实际引用的是子类 对象,那么该变量就不能再访问子类中添加的属性和方法

如何解决多态的弊端

  • 有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。我们可以使用向下转型来调用访问子类中特有的属性和方法。

注意:

  • Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。简称:编译时,看左边;运行时,看右边。

多态中的成员访问特点

  • 不管是成员变量还是成员方法,编译的时候,父类中必须存在,否则编译都不会通过,会报错。

总结:

  • 当使用多态方式调用成员变量时,首先检查父类中是否有该变量,如果没有,则编译错误;如果有,执行的是父类中的变量
  • 当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写后方法

为什么成员方法和成员变量不同?

  • 因为成员方法有重写,而成员变量没有
  • 成员变量:不具备多态性,只看引用变量所声明的类

转型的异常

ClassCastException异常,翻译为类型转换异常,这种异常通常出现在向下转型的操作过程当中,当类型不兼容的情况下进行转型出现的异常,之所以出现此异常是因为在程序运行阶段 a 引用指向的对象是一只小鸟,然后我们要将一只小鸟转换成一只猫,这显然是不合理的,因为小鸟和猫之间是没有继承关系的。为了避免这种异常的发生,建议在进行向下转型之前进行运行期类型判断,这就需要我们学习一个运算符了,它就是 instanceof。instanceof 运算符的语法格式是这样的:

  • (引用 instanceof 类型) 

instanceof 运算符的运算结果是布尔类型,可能是 true,也可能是 false,假设(c instanceof Cat)结果是 true 则表示在运行阶段 c 引用指向的对象是 Cat 类型,如果结果是 false 则表示在运行阶段 c 引用指向的对象不是 Cat 类型。有了 instanceof 运算符,向下转型就可以这样写了:

public class AnimalDemo {
    public static void main(String[] args) {
        // 向上转型 
        Animal a = new Cat();
        a.eat();
        // 调用的是 Cat 的 eat 
        // 向下转型 
        if (a instanceof Cat) {
            Cat c = (Cat) a;
            c.catchMouse();
            // 调用的是 Cat 的 catchMouse 
        } else if (a instanceof Dog) {
            Dog d = (Dog) a;
            d.watchHouse(); // 调用的是 Dog 的 watchHouse 
        }
    }
}

总结一下类型转换

                          

多态小结

  • 多态作用: 提高了代码的通用性,常称作接口重用
  • 前提:需要存在继承或者实现关系 并且 有方法的重写

成员方法:

  • 编译时:要查看引用变量所声明的类中是否有所调用的方法。
  • 运行时:调用实际new的对象所属的类中的重写方法。

成员变量:

  • 不具备多态性,只看引用变量所声明的类。

方法的重载与重写  

二者的定义不同

从编译和运行的角度看:

  • 重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不 同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了 不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类 和子类的,即子类可以重载父类的同名不同参数的方法。 所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法, 这称为“早绑定”或“静态绑定”
  • 方法重写:而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。 引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”

本文链接: http://www.dtmao.cc/news_show_200027.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?