详谈多态

       多态性来自于希腊语,意思是“有许多形态”。在面向对象的软件技术中,多态性是指子类对象可以像父类对象那样使用,同样的消息既可以发送给父类对象也可以发送给子类对象。也就是说,在类等级的不同层次中可以共享(公用)一个行为(方法)的名字,然而不同层次中的每个类却按自己的需求来实现这个行为。当对象接收到发送给它的消息时,根据该对象所属的类 动态选用在该类中定义的实现方法。

      多态性机制不仅增加了面向对象软件系统的灵活性,进一步减少了信息冗余,而且显著提高了软件的可重用性和可扩充性。当扩充系统功能增加新的实体类型时,只需派生出与新实体类相应的新的子类,并在派生的新的派生出的子类中定义符合该类需要的方法,完全无需修改原有的代码,甚至不需要重新编译原有的程序(仅需要编译新派生类的源程序,在与原有的程序连接)

 

在Java语言中,多态主要有以下两种表现形式: 

编译时多态----方法的重载(Overload)

重载是指同一个类中有多个同名的方法,但这些方法有不同的参数,因此在编译时就可以确定到底调用哪个方法,它是一种编译时多态。重载可以看成一个类中方法的多态性。

方法重载的定义:一个类中有多个同名方法(构造方法或普通方法),在调用这些方法时,到底调用哪个方法取决于调用方法时传入的参数的数据类型和个数。 

public class Teacher {

	public static void print(int age, String name) {
		System.out.println(age + "," + name);
	}

	public static void print(String name, int age) {
		System.out.println(name + "," + age);
	}

	public static void main(String[] args) {
		print(33, “王小红”);// 依次传入int类型和String类型数据,所以调用第一个方法
		print("王小红", 33);//依次传入String类型和int类型数据,所以调用第二个方法
	}
}

 

运行时多态----方法的重写或覆盖(Override)

       子类可以覆盖父类的方法,因此同样的方法会在父类与子类中有着不同的表现形式。在Java语言中,基类的引用变量不仅可以指向基类的实例对象,也可以指向其子类的实例对象。同样接口的引用变量也可以指向其实现类的实例对象。而程序调用的方法在运行期间才动态绑定(绑定指的是将一个方法调用和一个方法主体连接在一起),也就是引用变量所指向的具体实例对象的方法,也就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。通过这种动态绑定的方法实现了多态。由于只有在运行时才能确定调用哪个方法,因此通过方法重写实现的多态也可以被称为运行时多态。 

       方法重写或方法覆盖的定义:子类可以继承父类方法,但有时从父类继承的方法在子类中必须进行修改以适应新类的需要,这种对父类方法进行改写或改造的现象称为方法重写或方法覆盖。父类方法在子类中重写使继承更加灵活。 子类重写了父类的方法,则使用子类创建的对象调用该方法时,调用的是重写后的方法,即子类中的方法

详谈多态_第1张图片 

详谈多态_第2张图片

父类类型(比如Mammal)的变量(比如mammal1)指向子类创建的对象,使用该变量调用父类中一个被子类重写的方法(比如move方法),则父类中的方法呈现出不同的行为特征,这就是多态。

Java引用变量有两种类型,分别是编译时类型和运行时类型(编译时类型决定了其所修饰的变量名只能调用编译时类型中定义的或其继承过来方法,运行时类型决定了编译时类型修饰的变量名所调用的方法在程序执行过程中最终调用调用的方法):编译时类型由声明该变量时使用的类型决定,编译时方法行为表现的是父类中的方法;运行时类型由实际赋给该变量的对象,运行时方法行为表现的是子类重写的方法的行为特征。如果编译时类型和运行时类型不一致,就可能出现所谓多态。

上例分析:当把子类创建的对象直接赋给父类引用类型时,例如上例Test main方法中“Mammal mammal1 = new Whale();”, mammal1引用变量的编译时类型是Mammal,运行时类型是Whale,当程序运行时,该引用变量mammal1调用父类中被子类重写的方法时,其方法行为表现的是子类重写该方法后的行为特征,而不是父类方法的行为特征。

上转型对象

上转型对象调用父类方法,如果该方法已被子类重写,则表现子类重写后的行为特征,否则表现父类的行为特征。 使用上转型对象调用成员变量,无论该成员变量是否已经被子类覆盖,使用的都是父类中的成员变量:

详谈多态_第3张图片

 对象下转型

可以将上转型对象再强制转换为创建该对象的子类类型的对象,即将上转型对象还原为子类对象,对应于数据类型转换中的强制类型转换。 还原后的对象又具备了子类所有属性和功能,即可以操作子类中继承或新增的成员变量,可以调用子类中继承或新增的方法。 注意:不可以将父类创建的对象通过强制类型转换赋值给子类声明的变量。

 

重载和覆盖(重写)的区别

重载()和重写是Java多态性的不同表现形式。

重载

重载是在一个类中多态性的一种表现,是指在一个类中定义了多个同名的方法,他们或有不同的参数个数或者有不同的参数类型。在使用重载时,需要注意:

1、重载是通过不同的方法参数在区分的,例如不同的参数个数、不同的参数类型、不同的参数顺序

2.不能通过方法的访问权限、返回值类型和抛出异常类型进行重载

3.对于继承来说,如果基类方法的访问权限为private,就不能再派生类对其重载;如果派生类也定义了一个同名的函数,这只是一个新的方法,不会达到重载的效果。

覆盖(重写)

覆盖是指派生类函数覆盖基类函数。覆盖一个方法并对其重写,以达到不同的作用。在使用覆盖时,需要注意:

1、(@Override注解可以判断当前方法是否重写了父类的某个方法,如果在方法上加上该注解没有出错,则说明重写了父类方法,否则没有重写父类方法。)派生类的覆盖方法必须要和基类中被覆盖的方法有相同的函数名和参数列表。

2、派生类的覆盖方法的返回值必须和基类中被覆盖方法的返回值相同。(如果父类被重写的方法没有返回值类型或者返回值类型为基本数据类型,则要求子类重写的方法的返回值类型和父类被重写方法的返回值类型相同; 如果父类被重写的方法返回值类型为引用数据类型,则要求子类重写的方法的返回值类型和父类被重写方法的返回值类型相同或是其子类。)

详谈多态_第4张图片 

3、派生类的覆盖方法所抛出的异常必须和基类(或是其子类)中覆盖的方法所抛出的异常一致。

4、子类重写的方法不能缩小父类被重写方法的访问权限,子类重写方法的访问权限必须大于等于父类被重写方法的访问权限-----基类中被覆盖的方法不能为private,否则其子类只是定义了一个方法,并没有对齐其覆盖。

详谈多态_第5张图片

5、如果要在子类非static修饰的代码块或方法中调用父类被重写的方法可以通过super关键字实现:

详谈多态_第6张图片

6、父类中静态方法可以被子类继承,但却不能被子类重写;

详谈多态_第7张图片 

7、重写父类非静态方法时,重写后的方法不能添加static修饰;

详谈多态_第8张图片 

8、父类中被final关键字修饰的方法可以被子类继承,但却不能被子类重写;

详谈多态_第9张图片

区别:

1、重写是子类和父类之间的关系,是垂直关系;重载是同一个类中方法之间的关系,是水平关系。

2、重写只能由一个方法或只能由一对方法产生关系;重载是多个方法之间的关系。

3、重写要求参数列表相同;重载要求参数列表不同。

4、重写关系中,调用方法体是根据对象的类型(对象对应存储空间类型)来决定;而重载关系是根据调用时的实参来选择而方法体的。

 

你可能感兴趣的:(java)