1 概述
-
面向对象三大特征:封装性、继承性、多态性。其中,继承性是多态性的前提。
-
继承性在java中的体现除了extends还有implements。所以extends继承和implements实现都是继承性的前提。
-
不一定是类与类之间的继承,还有可能是接口与接口之间的继承,还有可能是类与接口之间的实现
- 注意:多态性指的是对象而不是类
2 多态的格式与使用
代码当中体现多态性,其实就是一句话:父类引用指向子类对象。
- 格式
//方式1:
父类名称 对象名 = new 子类名称();//子类对象就被当作父类进行使用,一只猫被当中动物来看待
//或者
//方式2
接口名称 对象名 = new 实现类名称();
对于方式1举例
- 定义一个父类
public class Fu {
public void method(){
System.out.println("父类方法");
}
public void methodFu() {
System.out.println("父类特有方法");
}
}
- 定义一个子类
public class Zi extends Fu{
@Override
public void method() {
System.out.println("子类方法");
}
}
- 调用
public class Demo01Multi {
public static void main(String[] args) {
//使用多态写法
//左侧父类的引用指向右侧子类的对象
Fu obj = new Zi();
obj.method();//子类方法 对于成员方法,new的是谁就调用谁的方法 若没有就往上找
obj.methodFu();//父类特有方法
}
}
3 多态中成员变量的使用(规则不变)
规则:访问成员变量两种方式
- 1.直接通过对象名称访问成员变量(对象名称.变量名):看等号左边是谁,优先用谁,没有则向上找。
- 2.间接通过成员方法访问成员变量:该方法属于谁则优先用谁,没有则向上找。
注意:成员变量不可以覆盖重写,只有方法可以覆盖重写
举例
- 定义一个父类
public class Fu {
int num = 10;
public void showNum(){
System.out.println(num);
}
}
- 定义一个子类
public class Zi extends Fu {
int num = 20;
int age = 18;
}
- 使用
public class Demo01MutilField {
public static void main(String[] args) {
//使用多态写法,父类引用指向子类对象
Fu obj = new Zi();//等号左边是Fu
//1.
System.out.println(obj.num);//10
//System.out.println(obj.age);//错误写法,Fu中没有age,再向上找是Oject类也不会有age
//2.
obj.showNum();//10 子类没有对该方法覆盖重写则就向上找父输出10;若子类对该方法覆盖重写那就是子输出20
}
}
4 多态中成员方法的使用(规则不变)
规则:看new的是谁,就优先用谁,没有则向上找
口诀:编译看左边,运行看右边
对于成员变量的口诀:编译看左边,运行还看左边
举例
- 定义一个父类
public class Fu {
public void method(){
System.out.println("父类方法");
}
public void methodFu(){
System.out.println("父类特有方法");
}
}
- 定义一个子类
public class Zi extends Fu {
@Override
public void method(){
System.out.println("子类方法");
}
public void methodZi(){
System.out.println("子类特有方法");
}
}
- 使用
public class Demo02MutilMethod {
public static void main(String[] args) {
Fu obj = new Zi();//多态
obj.method();//子类方法
obj.methodFu();//父类特有方法 子类没有改方法,所以向上找到了父类的该方法
//obj.methodZi();//错误写法
//编译看左边,左边是Fu,而Fu当中没有methodZi方法,所以出现红线,编译报错
}
}
5 多态的好处
调用方法的时候,编译看左边,运行看右边
6 对象的向上转型
举例
本例用的是类当然也可以用接口
- 定义一个Animal类
public abstract class Animal {
public abstract void eat();
}
- 定义一个Cat类
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
- 使用
public class Demo01Main {
public static void main(String[] args) {
//对象的向上转型就是:父类引用指向子类对象
Animal animal = new Cat();
animal.eat();//猫吃鱼
//编译看左:左边Animal有eat方法
//运行看右:运行右边Cat的eat方法
}
}
举例(接口)
- 定义一个接口
public interface Animal {
public abstract void eat();
}
- 定义一个实现类
public class Cat implements Animal{
@Override
public void eat() {
System.out.println("猫爱吃鱼鱼");
}
}
- 使用
public class Demo01Main {
public static void main(String[] args) {
Animal animal = new Cat();
animal.eat();//猫爱吃鱼鱼
}
}
7 对象的向下转型
问题引入
向上转型是安全的,没有问题。但是有弊端:对象一旦向上转型为父类,那么就无法调用子类原本特有的内容。
- 父类
public abstract class Animal {
public abstract void eat();
}
- 子类
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
//子类特有方法
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
- 使用
public class Demo01Main {
public static void main(String[] args) {
//对象的向上转型就是:父类引用指向子类对象
Animal animal = new Cat();
animal.eat();//猫吃鱼
//编译看左:左边Animal有eat方法
//运行看右:运行右边Cat的eat方法
//animal.catchMouse();//错误写法 编译报错 Animal中没有这个方法 并不是所有的动物都会猫抓老鼠
}
}
解决方案:向下转型【还原】
向上转型一定安全,向下转型有前提条件
- 父类
public abstract class Animal {
public abstract void eat();
}
- 子类Cat
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
//子类特有方法
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
- 子类Dog
public class Dog extends Animal{
@Override
public void eat(){
System.out.println("狗吃骨头");
}
public void watchHouse(){
System.out.println("狗狗看家");
}
}
- 使用
public class Demo01Main {
public static void main(String[] args) {
//对象的向上转型就是:父类引用指向子类对象
Animal animal = new Cat();//本来创建的时候是一只猫
animal.eat();//猫吃鱼
//animal.catchMouse();//错误写法 编译报错 Animal中没有这个方法 并不是所有的动物都会猫抓老鼠
//向下转型进行还原动作
Cat cat = (Cat)animal;
cat.catchMouse();//猫抓老鼠
//错误的向下转型
//本来new的时候是一只猫,现在非要当作狗狗
Dog dog = (Dog)animal;//编译不报错,但是运行异常:java.lang.ClassCastException类转换异常
}
}
新的问题:如果知道父类的引入本来是Cat还是Dog呢?
- 格式
对象 instanceof 类名称
这将会得到一个boolean值结果,也就是判断前面的对象能不能当作后面类型的实例。
public class Demo02Instanceof {
public static void main(String[] args) {
Animal animal = new Cat();//本来就是一只猫
animal.eat();//猫吃鱼
giveMePet(new Cat());
}
public static void giveMePet(Animal animal){
//如果希望用子类特有方法,需要向下转型
//判断传入的参数本来是猫还是狗,再决实施行何种向下转型的方案
if(animal instanceof Dog) {//判断一下父类引用animal本来是不是狗
Dog dog = (Dog)animal;
dog.watchHouse();
}
if(animal instanceof Cat){//判断一下父类引用animal本来是不是猫
Cat cat = (Cat)animal;//猫抓老鼠
cat.catchMouse();
}
}
}