Java:多态
一.什么是多态
以下将用一个父类Animal、两个子类:Cat、Dog以及一个测试类说明。
public class Animal {
void howl(){
System.out.println("动物在嚎叫!");
}
}
public class Cat extends Animal{
@Override
void howl() {
System.out.println("cat正在喵喵叫!");
}
void name() {
System.out.println("cat的名字叫做喵喵");
}
}
public class Dog extends Animal{
@Override
void howl() {
System.out.println("dog正在汪汪叫!");
}
}
测试类:
public class Test {
public static void main(String[] args) {
Animal animal1 = new Cat();
animal1.howl();
// cat正在喵喵叫!
// animal1.name;
Animal animal2 = new Dog();
animal2.howl();
// dog正在汪汪叫
}
}
当使用多态方式调用时,首先检查父类中是否有该方法,如果没有就会编译错误;例如:
animal1.name;//父类方法中没有该方法,编译错误
如果有,再去调用子类的同名方法。例如:
Animal animal1 = new Cat();
animal1.howl();
此时编译时类型和运行时类型不一致,就会出现多态;简而言之地讲:多态即多种形态、状态。
(一).实现条件
Java实现多态的必要条件:继承、重写。
(二).实现基础
父类声明的变量可以引用所有子类的对象,这是多态实现的基础。
我们只有在运行的时候才会知道引用变量所指向的具体实例对象。
(三).多态作用
把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。简单地说:就是父类或接口的引用变量指向子类的对象。
(四).总结
在涉及多态的编程中,请注意以下细节:
1.静态方法
静态方法不能被重写,所以静态方法也没有多态性。
2.成员变量
成员变量不具备多态性。成员变量的值取决于引用所属的类。
3.成员方法
编译时:检查引用变量所属类中是否有所调的方法。
运行时:调用实际对象所属类中的重写方法。
二.多态的转型
在基本数据类型的转换中我们已经认识了数据类型的转换,那么在多态当中实际上也有父类与子类之间的转换。
Animal类和Dog类:
public class Animal {
void howl() {
System.out.println("Animal.howl()");
}
}
public class Dog extends Animal{
@Override
void howl() {
System.out.println("Dog.howl()");
}
void eatBone() {
System.out.println("Dog.eatBone()");
}
}
(一).向上转型 upcasting
向上转型就是子类转父类,这是由系统来自动完成的;例如:
public class Test {
public static void main(String[] args) {
// 向上转型
Animal animal = new Dog();
animal.howl();// Dog.howl()
// animal.eatBone();
// The method eatBone() is undefined for the type Anima
}
}
一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那就不能再访问子类中特有的属性和方法。
(二).向下转型 downcasting
向下转型就是父类转子类,此时需要使用强制类型转换符()。
例如:
public class Test1 {
public static void main(String[] args) {
// 向下转型
Animal animal = new Dog();
Dog dog = (Dog) animal;
dog.howl();
dog.eatBone();
}
}
在类型转换时,请注意:
1.弄清楚继承关系,谁是父类谁是子类;
2.在创建对象时调用的是谁的构造函数。
三.instanceof
(一).引入
在多态编程中可以通过instanceof判断对象到底属于哪个确切的类型。
示例:
public class Animal {
void howl() {
System.out.println("动物都会嚎叫");
}
}
public class Cat extends Animal{
@Override
void howl() {
System.out.println("Cat正在喵喵叫");
}
}
public class Dog extends Animal{
@Override
void howl() {
System.out.println("Dog正在汪汪叫");
}
}
测试类:
public class Test {
public static void main(String[] args) {
Animal animal = new Cat();
Cat cat = (Cat) animal;
// 当我们不确定对象属于哪个类时,可以使用instanceof进行判断
boolean res = cat instanceof Cat;
System.out.println(res);// true
res = cat instanceof Animal;
System.out.println(res);// true
// 由子类构造函数创建的对象,对象既属于子类,也属于父类
Dog dog = new Dog();
res = dog instanceof Dog;
System.out.println(res);//true
res = dog instanceof Animal;
System.out.println(res);//true
// 由父类构造函数创建的对象,不属于子类
Animal animal1 = new Animal();
res = animal1 instanceof Dog;
System.out.println(res);// false
}
}
(二).实际应用
public class Test {
public static void main(String[] args) {
Animal animal1 = new Cat();
Cat cat = (Cat) animal1;
checkType(cat); // Cat正在喵喵叫
Dog dog = new Dog();
checkType(dog); // Dog正在汪汪叫
}
public static void checkType(Animal animal) {
if (animal instanceof Cat) {
Cat cat = new Cat();
cat.howl();
}
if (animal instanceof Dog) {
Dog dog = new Dog();
dog.howl();
}
}
}
四.多态应用场景
(一).基于继承的多态
多个子类对同一父类方法的重写,可以在运行时表现出不同的行为。
(二).基于接口的多态
一个接口可以有多个实现类。所以,多个实现类对接口中同一方法的重写,可以在运行时表现出不同的行为。
语法:
接口名 变量名 = new 类名()
注意:命名规范——>实现类为 接口名+impl。
例如:
public interface MyInterface {
}
public class MyInterfaceImpl1 implements MyInterface{
}
public class MyInterfaceImpl2 implements MyInterface {
}
public class Test1 {
public static void main(String[] args) {
MyInterfaceImpl1 impl1 = new MyInterfaceImpl1();
MyInterface test1 = test1(impl1);
MyInterface myInterface =new MyInterfaceImpl2();
MyInterface test2 = test2(myInterface);
}
// 返回接口
public static MyInterface test1(MyInterface myInterface) {
MyInterfaceImpl1 impl1=new MyInterfaceImpl1();
return impl1;
}
public static MyInterface test2(MyInterface myInterface) {
MyInterfaceImpl2 impl2=new MyInterfaceImpl2();
return impl2;
}
}
(三).函数输入参数的多态
多态可以作为形参,接受范围更广的对象,接收的参数更加灵活。
(四).函数返回值的多态
多态可以作为返回值,接受范围更广的对象。
public 父类 方法名(){
return 父类对象;
return 子类对象;
}