概念
多态性是面向对象编程的又一个重要特征,它是指在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为,
对面向对象来说,多态分为编译时多态和运行时多态。
编译时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的方法。通过编译之后会变成两个不同的方法,在运行时谈不上多态。
运行时多态是动态的,它是通过动态绑定来实现的,也就是大家通常所说的多态性。
Java 实现多态有 3 个必要条件
继承、重写和向上转型。
继承:在多态中必须存在有继承关系的子类和父类。
重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才既能可以调用父类的方法,又能调用子类的方法。
这里提一下向上转型和向下转型
向上转型
- 向上转型概念
隐式转型、自动转型,父类引用指向子类实例,小类转型为大类。
如 Animal two=new Dog();//Dog类是Animal类的子类
向上转型是安全的,任何子列都继承并接受了父类的方法,狗属于动物。但是向下转型不可以,不能说动物就是狗这是不成立的,(向下转型要通过强制类型转换)
-
应用:
1)、当一个子列对象向上转型父类类型之后,就被当成了父类对象,能调用的方法会减少 可以调用子类重写父类的方法(此处调的是子类的方法)以及父类派生的方法,无法调用子类独有方法
2)、父类中的静态方法无法被子类重写,
例如父类的静态方法
public static void say(){
System.out.println("动物叫...");
}
子类中也有一个同名的静态方法,此时say()属于子类自己独有的方法,不构成重写(可以用@Override注解判断是否是重写)
public static void say(){
System.out.println("小狗汪汪叫...");
}
Animal two=new Dog();
two.say();//此时调用的是父类静态方法say(),打印:动物叫...
所以向上转型之后,只能调用到父类原有的静态方法
多态的实现可以通过向上转型和动态绑定机制来完成,向上转型实现了将子类对象向上转型为父类对象类型,二动态绑定机制能识别对象转型前的类型,从而自动调用该类的方法,
动态绑定:
绑定就是将一个方法调用同一个方法所在的类连接到一起,绑定分为静态绑定和动态绑定两种。
静态绑定:在程序运行之前进行绑定(由编译器和链接程序完成)
例如:
Dog dog = new Dog();
dog.say();
这种调用在代码里指定,编译时编译器就知道调用dog类中的say()方法
动态绑定:在程序运行期间由JVM根据对象的类型自动的判断应该调用哪个方法,
Animal [] animals = new Animal[5];
for(int i = 0; i < animals.length; i++){
int n = (int)(Math.random() *2);//随机产生0到1中的一个数
switch (n){
case 0:
animals[i] = new Cat();
break;
case 1:
animals[i] = new Dog();
break;
}
}
for (int i = 0; i < animals.length; i++){
animals[i].eat();
}
在编译过程中不知道调用哪个类的eat()方法,直到运行时才能到底调用哪个类的eat方法,这种就是动态绑定
--------------------------------------------------
向下转型
子类引用指向父类对象,需要强制类型转换
如:
Animal two=new Dog();
Dog dog = (Dog)two;//此处必须进行强制类型转换
向下转型后,可以调用子类特有的方法
但是必须满足转型条件才能进行强转
Animal animal = new Dog();
Dog dog = (Dog) animal; //正确写法
Cat cat = (Cat) animal; //这种写法不正确,编译报错Exception in thread "main" java.lang.ClassCastException: com.imooc.animal.Dog cannot be cast to com.imooc.animal.Cat
这个时候可以用instanceof运算符进行判断:返回true/false
Animal animal = new Dog();
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
System.out.println("dog...");
}else if (animal instanceof Cat){
Cat cat = (Cat) animal;
System.out.println("cat...");
}
抽象类和抽象方法
1. 概念
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。抽象方法也是同一个道理。
使用abstract class来定义抽象类
例如:
//抽象类:不允许实例化,可以通过向上转型,指向子类实例
public abstract class Animal {
public Animal(){
}
//抽象方法:不允许包含方法体;子类中需要重写父类的抽象方法,否则,子类也是抽象类
//static final private不能与abstract并存
public abstract void eat();
}
2. 特点:
1.抽象方法不能被直接实例化
它只能作为其他类的父类,但可以向上转型,指向实例化
//Animal是抽象类的错误示范:
Animal Animal = new Animal();//直接new是错误的
2.抽象方法只有声明,不能有实现。
例如:
public abstract void eat();
3. 意义:
为其子类提供一个公共的类型。
封装子类中的重复内容(成员变量和方法)
将父类设计成抽象咧后,即可借由父子继承关系限制子类设计随意性,在一定程度上避免了无父类的实例化
4. 重点:
1.含有抽象方法的类,只能被定义成抽象类
正确的写法
2.抽象类不一定包含抽象方法
3.在抽象类中的成员方法可以包括一般方法和抽象方法
4.抽象类不能被实例化,即使抽象类中不包含抽象方法,这个抽象咧也不能创建实例。抽象类的构造方法主要是用于被其子类调用
5.一个类继承抽象类后,必须实现其所有抽象方法,否则也是抽象类,不同子类对父类的抽象方法可以有不同的实现。
6.即使父类是具体的,但其子类也可以是抽象的,如object类是具体的类,但是可以创建抽象子列
7.abstract方法不能用static和private修饰,
对于类,不能同时用final和abstract修饰,因为final关键字使得类不可继承,而abstract修饰的类如果不可以被继承将没有任何意义。