基础知识:

        Java中的继承机制使得一个类可以继承另一个类,继承的类称为子类,被继承的类称为父类。在一个子类被创建的时候,首先会在内存中创建一个父类对象,然后在父类对象外部放上子类独有的属性,两者合起来形成一个子类的对象,所以子类可以继承父类中所有的属性和方法,包括private修饰的属性和方法,但是子类只是拥有父类private修饰的属性和方法,却不能直接使用它,也就是无法直接访问到它(子类可以通过调用父类的public声明的get方法来获取父类的private属性,但无法访问父类的private方法)。同时子类可以对继承的方法进行重写(@Override),并且新建自己独有的方法

1.向上转型:

         Apple类继承自Fruit类,该类拥有自己的方法test(),并且重写了父类的show()方法
class Fruit {
    public void show() {
        System.out.println("this is a fruit");
    }
}

class Apple extends Fruit {
    @Override
    public void show() {
        System.out.println("this is a apple");
    }

    public void test() {
        System.out.println("i am a apple");
    }
}
        实例化Apple类,并新建一个Fruit类的引用变量引用该实例,调用实例的show()方法
public class test {
    public static void main(String[] args) {
        Fruit fruit = new Apple(); //向上转型
        fruit.show();
    }
}
        输出
        接着调用实例的test()方法
        分析:
                这里使用了向上转型,换言之,就是用父类的引用变量去引用子类的实例,这里是允许的。当向上转型之后,父类引用变量可以访问子类中属于父类的属性和方法,但是不能访问子类独有的属性和方法。例子中由于子类重写了父类的show()方法,所以调用的show()方法是子类的show()方法,输出结果为:“this is a apple”,而调用子类的test()方法则会报错。

2.向下转型

并不是所有的对象都可以向下转型,只有当这个对象原本就是子类对象通过向上转型得到的时候才能够成功转型。

       实例化Apple类,并新建一个Fruit类的引用变量“fruit”引用该实例,然后新建一个Apple类的引用变量,引用向下转型的“fruit”变量,代码如下:
public class test {
    public static void main(String[] args) {
        Fruit fruit = new Apple();     // 向上转型
        Apple apple = (Apple) fruit; // 向下转型
        apple.show();
        apple.test();

        Apple apple1 = (Apple) new Fruit();
        apple1.test();
        apple1.show();
    }
}
        第一种代码是允许的,因为Fruit引用的对象原本就是Apple对象向上转型得到的,在对fruit向下转型后得到的还是Apple类的对象,能够被Apple类的引用变量引用。
        第二种代码Fruit引用的对象是Fruit本身,那么在向下转型的过程中是不安全的,编译不会出错,但是运行时会出现java.lang.ClassCastException错误。它可以使用instanceof来避免出错此类错误。

        运行结果

3.转型的好处

        通过向上向下转型肯定是有好处的,比如可以减少编程代码。
        假设在主类中定义了一个run()方法,该方法传入一个Fruit参数,并调用了Fruit对象的show()方法,代码如下:
    public static void run(Fruit fruit) {
        fruit.show();
    }
        在main()方法中的代码如下:
    public static void main(String[] args) {
        run(new Fruit());
        run(new Apple());
        run(new Orange());
    }
        结果:
   
上述代码中,调用run()方法时的参数不仅是Fruit对象,也可以是Apple对象和Orange对象,当传入的是Apple对象和Orange对象时,就会向上转型成Fruit对象,但是调用的show()方法还是Apple对象和Orange对象的show()方法。这样就不需要在主类中同时重载三个run()方法,减少了代码量。