多态的成员访问
- 构造方法:同继承一样,子类会通过super访问父类构造方法
- 成员变量:编译看左边(父类),执行看右边(父类)
只去查找父类的变量,父类中没有我们要调用的变量,那么就会报错
- 成员方法:编译看左边(父类),执行看右边(父类)
方法执行时,先执行子类重写的方法,如果子类没有重写,则动态的去父类查找
多态的优缺点
1 多态的优缺点 :
- 优点
提高代码的扩展性
- 缺点
不能调用子类特有的功能。在下面的代码中,你无法调用a.lookname( )/a.catchMouse( )/a.sleep( )方法,因为程序编译看的是父类,而这些方法是子类特有的功能,父类当中不存在,因此就会报错。
public class Test02 {
public static void main(String[] args) {
useAnimal(new Dog());
useAnimal(new Cat());
useAnimal(new Pig());
}
public static void useAnimal(Animal a) {
// 这就是多态的用法,原本需要编写像下面一样的三个测试类,但是现在只需给方法传入一个Animal类
// 即可调用全部的子类方法
a.eat();
}
// // 测试狗类
// public static void useAnimal(Dog dog) {
// dog.eat();
// dog.lookHome();
// }
//
// // 测试猫类
// public static void useAnimal(Cat cat) {
// cat.eat();
// cat.catchMouse();
// }
//
// // 测试猪类
// public static void useAnimal(Pig pig) {
// pig.eat();
// pig.sleep();
// }
}
// 父类
abstract class Animal {
public abstract void eat();
public void drink() {
System.out.println("喝水....");
}
}
// 子类
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃肉!");
}
public void lookHome() {
System.out.println("看家...");
}
}
// 子类
class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼...");
}
public void catchMouse() {
System.out.println("猫抓老鼠...");
}
}
// 子类
class Pig extends Animal {
@Override
public void eat() {
System.out.println("猪拱白菜...");
}
public void sleep() {
System.out.println("猪特能睡...");
}
}
// 输出 :狗吃肉!
猫吃鱼...
猪拱白菜...
多态的转型
如何解决上述多态的缺点呢?就需要多态转型。
- 向上转型(upcasting)
父类的引用指向子类对象(把子类类型的对象赋值给父类类型)
Animal an = new Dog();
- 向下转型(downcasting)
向上转型我很好理解,但是向下转型稍稍抽象一点,不过细细分析也不难。
首先要知道一点:父类引用指向子类对象,而子类引用不能指向父类对象。(举个很俗的例子,街上看到一个狗,你可以叫动物,但是看到一个动物,你就不能说是狗)。
因此Dog dog = a;这种写法会报错,因为Dog是子类,a是父类,这就需要强制转换。
正确写法:Dog dog = (Dog) a;
注意 :
1 想要调用子类特有的功能 , 需要向下转型
2 对象的本质类型 , 和转型的类型不匹配就会发生 classCastException:类型转换异常
注意 : 方法的参数是一个类 , 那么调用此方法需要传入此类的对象或者此类的子类对象 !!!
于是上面的主方法快就可以改进为:
public class Test02 {
public static void main(String[] args) {
useAnimal(new Dog());
useAnimal(new Cat());
useAnimal(new Pig());
}
public static void useAnimal(Animal a) {
a.eat();
Dog dog = (Dog) a;
dog.lookHome();
Cat cat = (Cat) a;
cat.catchMouse();
// 简化写法
((Pig)a).sleep();
}
}
但是你以为仅仅通过向下转型就能解决Java不能调用子类特殊方法的问题了?还不行,程序仍然会报错。原因在于当我们执行useAniaml(new Dog())方法时,传入的是一个Dog类对象,因此a的本质是一个“狗”,那么a.eat( )方法以及 dog.lookHome();会被成功执行,但是执行到cat.catchMouse();会报错classCastException(类型转换异常)。顾名思义,Dog类的对象不能强转为Cat类的对象,怎么办呢?这个时候就需要引出instanceof关键字。
instanceof关键字
格式:对象名 instanceof 引用数据类型
作用: 判断一个对象是否属于另一种引用数据类型, 返回boolean类型结果
于是最终的主方法代码改进如下:
public class Test1 {
public static void main(String[] args) {
useAnimal(new Dog());
System.out.println("================");
useAnimal(new Cat());
System.out.println("================");
useAnimal(new Pig());
}
public static void useAnimal(Animal a) {
a.eat();
// 子类特有功能
// 向下转型
if (a instanceof Dog) {
Dog dog = (Dog) a;
dog.lookHome();
}
if(a instanceof Cat){
Cat cat = (Cat) a;
cat.catchMouse();
}
if(a instanceof Pig){
((Pig) a).sleep();
}
}
接口实现多态
多态的应用:
- 以多态形式创建一个对象。
- 方法的参数返回值是一个类或者一个接口,那么我们可以传入子类的对象。
- 方法的返回值类型是一个类或者一个接口,那么我们可以返回子类的对象。
public class Test2 {
public static void main(String[] args) {
// 父类类型接收子类类型对象
Player player = new Mp3Player();
// 方法的参数是一个父类类型,这里的父类就是Player,那么可以接受子类类型对象
usePlayer(new Mp3Player());
}
public static void usePlayer(Player player) {
}
public static Player getPlayer(){
// 方法的返回值类型是一个父类型,那么可以返回此类型的子类对象
return new Mp3Player();
}
}
interface Player {
void play();
}
class Mp3Player implements Player{
@Override
public void play(){
System.out.println("mp3播放音乐");
}
}