1.向上转型
1.1 向上转型的概念
向上转型:实际就是创建一个子类对象,将其当成父类对象来使用。
举个例子:
//Animal是Cat的父类
Animal animal = new Cat("翠花",2);
animal是父类类型,但可以引用一个子类对象,因为大范围可以囊括小范围,是安全的,就比如小猫和小狗它们在物种上来说都属于动物。
1.2 向上转型的使用
【使用场景】
- 直接赋值
- 方法传参
- 方法返回
以下是我们接下来需要用到的一些类:
//Animal类
public class Animal(){public String name;public int age;public Animal(String name, int age){this.name = name;this.age = age;}public void eat(){System.out.println(name + "吃饭");}
}
//继承Animal的Cat类
public class Cat extends Animal{public Cat(String name, int age){super(name, age);}@Overridepublic void eat(){System.out.println(name + "吃鱼");}
}
//继承Animal的Dog类
public class Dog extends Animal{public Dog(String name, int age){super(name, age);}@Overridepublic void eat(){System.out.println(name + "吃骨头");}
}
每个场景的应用:
public class TestAnimal {// 1. 方法传参:形参为父类型引用,可以接收任意子类的对象public static void eatFood(Animal a){a.eat();}// 2. 作返回值:返回任意子类对象public static Animal buyAnimal(String word){if("狗".equals(word) ){return new Dog("狗狗",1);}else if("猫" .equals(word)){return new Cat("猫猫", 1);}else{return null;}}public static void main(String[] args) {Animal cat = new Cat("小猫",2); // 3. 直接赋值:子类对象赋值给父类对象Dog dog = new Dog("小狗", 1);eatFood(cat);eatFood(dog);Animal animal = buyAnimal("狗");animal.eat();animal = buyAnimal("猫");animal.eat();}
}
1.3 代码详解:
- 1.方法传参:
public static void eatFood(Animal a){a.eat();
}
这个方法的参数类型是Animal,Animal是Cat和Dog的父类,那这里也可以接收任意子类的对象,就像在main方法中写的这样:
eatFood(cat);//参数是Cat对象
eatFood(dog);//参数是Dog对象
- 2.方法返回
// 2. 作返回值:返回任意子类对象
public static Animal buyAnimal(String word){if("狗".equals(word) ){return new Dog("狗狗",1);}else if("猫" .equals(word)){return new Cat("猫猫", 1);}else{return null;}
}
这个方法的返回值的类型是Animal,这里我们同样可以返回Animal的子类,故这里我们可以直接返回Cat对象和Dog对象。
- 3.直接赋值:
Animal cat = new Cat("小猫",2);
这个就比较直观一点了,我们可以看到子类对象可以直接赋值给父类对象。
向上转型的优点:让代码实现更加简单灵活
向上转型的缺点:不能调用到子类特有的方法
2.向下转型
2.1 向下转型的概念
将一个子类对象经过向上转型之后,就无法调用到子类特有的方法了,但有时候可能需要调用子类特有的方法,那么此时,我们将父类引用再还原为子类对象即可,这就是向下转型。
那么这个时候,我们就需要考虑到一个问题了,上述介绍的向上转型,它的原理是大范围可以囊括小范围,就像小猫和小狗从物种上来说,它们都属于动物,那么现在将两者的位置替换之后,我们提到动物,就一定是小猫或者小狗吗,那可不一定,所以向下转型一般来说是不安全的。
我们先来准备接下来需要用到的一些类,与上面的类有所不同的是,这次里面增加了两个新的 方法,即每个子类中特有的方法.
//Animal类
public class Animal(){public String name;public int age;public Animal(String name, int age){this.name = name;this.age = age;}public void eat(){System.out.println(name + "吃饭");}
}
//继承Animal的Cat类
public class Cat extends Animal{public Cat(String name, int age){super(name, age);}@Overridepublic void eat(){System.out.println(name + "吃鱼");}public void mew(){System.out.println(name + "喵喵叫");}
}
//继承Animal的Dog类
public class Dog extends Animal{public Dog(String name, int age){super(name, age);}@Overridepublic void eat(){System.out.println(name + "吃骨头");}public void bark(){System.out.println(name + "汪汪叫");}
}
2.2 向下转型的应用:
public class TestAnimal {public static void main(String[] args) {Cat cat = new Cat("小猫",2);Dog dog = new Dog("小狗", 1);
// 先向上转型 向上转型后只能调用到父类中的方法Animal animal = cat;animal.eat();animal = dog;animal.eat();//animal.bark(); 编译报错//向下转型cat = (Cat)animal;cat.mew();dog = (Dog)animal;dog.bark();}
}
2.3 代码详解:
animal = dog;
animal.eat();
animal.bark();//编译报错
当我们写出上面第三句代码时,就会出现编译报错,因为编译器在编译时是将animal当成Animal对象处理的,而不是Dog对象,而Animal类中没有bark方法,因此会编译失败,所以我们要想调用Dog类和Cat类中特有的对象,就要这样做:将父类引用强制转换为子类引用
cat = (Cat)animal;
cat.mew();
这么写时,编译不会报错,但运行时会抛出异常,因为animal最后实际指向的是dog,现在要强制还原为猫,无法正常还原,运行时会抛出ClassCastException异常。
dog = (Dog)animal;
dog.bark();
这里animal本来指向的是dog,因此将animal还原为Dog也是安全的。
- 从上述代码中,我们也可以看出,向下转型并不安全,一旦转换失败,运行时就会抛出异常。Java中为了提高向下转型的安全性,引入了instanceof,如果该表达式为true,则可以安全转换。
2.4 instanceof 的使用:
public class TestAnimal {public static void main(String[] args) {Cat cat = new Cat("小猫",2);Dog dog = new Dog("小狗", 1);//向上转型Animal animal = cat;animal.eat();animal = dog;animal.eat();//向下转型//表达式为false的话就不会执行里面的代码if(animal instanceof Cat){cat = (Cat)animal;cat.mew();}if(animal instanceof Dog){dog = (Dog)animal;dog.bark();}}
}