前言
本篇介绍多态和Java中的上转型,下转型。
多态
面向对象程序中,具有不同参数的方法可以同名,同一个操作被不同类型对象操作时,可以表现出不同的行为,体现了多态性。利用多态技术开发的软件系统。具有更好的可扩展性和强壮性。
多态:相同的方法,不同的实现,方法的行为表现出不同的形态。
多态的两种情况
- 子类重写了父类的方法,子类和父类的相同方法,实现不同,各自表现出不同的形态。
- 不同的类从同一个父类中派生,它们重写了父类的方法,这些兄弟类的方法实现不同,各自表现出不同的形态。
在java中,多态是通过上转型对象来实现的。
我们知道,子类是父类的特例。比如,大学生是学生的特例,大学生是学生的子类,故“大学生是学生”的说法是正确的,但“学生是大学生”的说法却不一定对。在程序设计中,可以把子类对象赋值给父类的引用变量,这样的引用变量称为上转型对象。这就好像说“大学生是学生”。通过上转型对象调用被重写的方法时,调用的是子类中重写的方法。这是Java实现多态的方法。
上转型
向上转型,就是用父类的引用变量去引用子类的实例,这是允许的。且向上转型之后,父类引用变量可以访问子类中属于父类的属性和方法,但是不能访问子类独有的属性和方法。
注意:把父类对象赋值给子类的引用变量时,将会产生异常。就比如上面例子中说“学生是大学生”一样。如果必须要这么做,那就需要强制类型转换。也就是下转型。
下转型
但并不是所有的对象都可以向下转型,只有当这个对象原本就是子类对象通过向上转型得到的时候才能够成功转型。
例如:
class Stu{
public void say() {
System.out.println("I am a student.");
}
}
class uniStu extends Stu{
@Override
public void say() {
System.out.println("I am a university student.");
}
public void eat() {
System.out.println("I can eat.");
}
}
#实例化uniStu类,并新建一个Stu类的引用变量“stu”引用该实例,然后新建一个uniStu类的引用变量,引用向下转型的“stu”变量,代码如下:
Stu stu = new uniStu();
uniStu xiaoxuan = (uniStu)stu;
#上述代码是允许的,因为stu引用的对象原本就是uniStu对象向上转型得到的,在对stu向下转型后得到的还是uniStu类的对象,能够被uniStu类的引用变量引用。
class senhsStu extends Fruit{
@Override
public void say() {
System.out.println("I am a senior high school student.");
}
public void drink() {
System.out.println("I can drink");
}
}
#实例化uniStu类,并新建一个Stu类的引用变量“stu”引用该实例,然后新建一个senhsStu类的引用变量,引用向下转型的“stu”变量,代码如下:
Stu stu = new uniStu();
senhsStu senhsstu = (senhsStu)stu;
上述代码虽然能够编译成功,但是在运行的时候会报错,因为stu对象是由uniStu对象向上转型得到的,只能够向下转型成uniStu对象,不能够向下转型成senhsStu对象。
下面我们看两个例子,了解多态是如何实现的以及如何使用上转型对象。
package com.oop.Rew;
class Pet {
public void speak(){
System.out.println("Who is Speaking?");
}
}
//多态的第一种:Cat从Pet派生,并重写了父类的speak方法,他们的实现方法不同,Cat和Pet的speak表现出不同的形态
class Cat extends Pet {
public void speak(){
System.out.println("Miao Miao Miao!");
}
}
class Dog extends Pet {
public void speak(){
System.out.println("Wang Wang Wang!");
}
}
//主类
public class Example01 {
public static void main(String[] args) {
Pet pet = new Pet();
Cat cat = new Cat();
pet.speak();
cat.speak();
}
}
结果输出:
Who is Speaking?
Miao Miao Miao!
package com.oop.Rew;
class Pet {
public void speak(){
System.out.println("Who is Speaking?");
}
}
//多态的第二种:Cat和Dog都从Pet中派生,并且都重写了父类的speak方法,pet是父类Pet的引用变量。不同的类从同一个父类中派生,它们重写了父类的方法,这些兄弟类的方法实现不同,各自表现出不同的形态。
class Cat extends Pet {
public void speak(){
System.out.println("Miao Miao Miao!");
}
}
class Dog extends Pet {
public void speak(){
System.out.println("Wang Wang Wang!");
}
}
//pet = cat时,pet是Cat类上转型对象,pet.speak();调用的是Cat的speak方法。
//pet = dog时,pet是Dog类上转型对象,pet.speak();调用的是Cat的speak方法。
public class Example02 {
public static void main(String[] args) {
Pet pet;
Cat cat = new Cat();
Dog dog = new Dog();
pet = cat;
pet.speak();
pet = dog;
pet.speak();
}
}
结果输出:
Miao Miao Miao!
Wang Wang Wang!
总结
转型的好处
可以减少代码量。例如,在主类里定义了一个study()方法,此方法传入一个Stu参数,并调用了Stu对象的say()方法,代码如下:
public static void study(Stu stu) {
stu.say();
}
在main()方法中的代码如下:
public static void main(String[] args) {
study(new Stu());
study(new uniStu());
study(new senhsStu());
}
上述代码中,调用study()方法时的参数不仅是Stu对象,也可以是uniStu对象和senhsStu对象,当传入的是uniStu对象和senhsStu对象时,就会向上转型成Stu对象,但是调用的say()方法还是uniStu对象和senhsStu对象的say()方法。这样就不需要在主类中同时重载三个say()方法,减少了代码量。
多态存在的条件
-
有继承关系
-
子类重写父类方法
-
父类引用指向子类对象
-
注意:多态是方法的多态,属性没有多态性。
-
instanceof
(类型转换) 引用类型,判断一个对象是什么类型~
/*
多态注意事项:
1.多态是方法的多态,属性没有多态
2.父类和子类,有联系才能转换。类型转换异常! ClassCastException!
3.存在的条件:继承关系,方法需要重写,父类引用指向子类对象!father f1 = new Son();
1.static 方法,属于类,它不属于实例;
2.final 无法重写 常量;
3.private方法:不能重写。
*/