重新精读《Java 编程思想》系列之向上转型与向下转型

简介: 重新精读《Java 编程思想》系列之向上转型与向下转型

前言

今天重读了一下向上转型与向下转型,有些新的体会,了解了向上转型的好处,及如何向下转型。在此分享给大家。

向上转型

向上转型是用来表现新类和基类之间的关系。在传统中,由导出类转型成基类,在继承图中是向上移动的。因此称作向上转型。由于向上转型是从一个较专用类型向较通用类型转换,所以总是安全的。也就是说,导出类是基类的一个超集。它可能比基类含有更多的方法。但他必须具备基类中所含有的方法。
我们来看一个例子。

class Car {
    public void run() {
        System.out.println("这是父类run()方法");
    }
}

public class Benz extends Car {
    public void run() {
        System.out.println("这是Benz的run()方法");

    }

    public void price() {
        System.out.println("Benz:800000$");
    }

    public static void main(String[] args) {
        Car car = new Benz();
        car.run();
       //car.price();程序报错
    }
}

运行后输出。这是Benz的run()方法。
但是当我们用car这个对象去调用Benz类中price这个方法时,就会报错。
这就是因为我们此处进行的向上转型,car这个对象虽然指向子类,但是子类由于进行了向上转型,就失去了使用父类中所没有的方法的“权利”,在此处就是不能调用price()这个方法。
那么向上转型到底有什么用呢,到目前为止我们不仅看不到它的好处,反而发现使用了向上转型后反而不能调用子类所特有的方法了。那么向上转型的作用到底是什么呢,我们一起来看下面的代码:

class Car {
    public void run() {
        System.out.println("这是父类run()方法");
    }

    public void speed() {
        System.out.println("speed:0");
    }

}

class BMW extends Car {
    public void run() {
        System.out.println("这是BMW的run()方法");
    }

    public void speed() {
        System.out.println("speed:80");
    }
}

public class Benz extends Car {
    public void run() {
        System.out.println("这是Benz的run()方法");

    }

    public void speed() {
        System.out.println("speed:100");
    }

    public void price() {
        System.out.println("Benz:800000$");
    }

    public static void main(String[] args) {
        show(new Benz());//向上转型实现
        show(new BMW());
    }

    public static void show(Car car) {//父类实例作为参数
        car.run();
        car.speed();
    }
}

上面代码中

    public static void main(String[] args) {
        show(new Benz());
        show(new BMW());
    }

    public static void show(Car car) {
        car.run();
        car.speed();
    }

就体现了向上转型的优点,这也体现了Java抽象编程的思想。如果此处没有向上转型,要实现show每个子类的功能,那么有几个子类就要写多少函数。代码如下:

    public static void main(String[] args) {
        show(new Benz());
        show(new BMW());
    }

    public static void show(Benz benz) {
        benz.run();
        benz.speed();
    }
    public static void show(BMW bmw) {
        bmw.run();
        bmw.speed();
    }

试想一下,一旦有很多子类,那么这个工作量将会比没有使用向上转型大很多。这也表明向上转型还有个优点就是提高了代码的简洁性。
我们再来一种带着static的特殊调用情况。

public class Animal {
    
        String name = "我是动物";
        static int age = 20;
        public void eat() {
            System.out.println("动物可以吃饭");
        }
        public static void sleep() {
            System.out.println("动物可以睡觉");
        }

        public void run(){
            System.out.println("动物可以奔跑");
        }

        public static void main(String[] args) {
            Animal am = new Dog();
            am.eat();
            am.sleep();
            am.run();
            //am.watchdog();这里会报错
            System.out.println(am.name);
            System.out.println(am.age);
        }

}
class Dog extends Animal {
    String name = "小狗";
    static int age = 60;
    public void eat() {
        System.out.println("小狗可以吃饭");
    }
    public static void sleep() {
        System.out.println("小狗可以睡觉");
    }
    public void watchdog() {
        System.out.println("小狗可以看门");
    }

}

运行结果:

但是可以看到代码块中,直接调用Dog的watchdog()方法会报错。

这就是因为我们此处进行的向上转型,am这个对象虽然指向子类,但是子类由于进行了向上转型,就失去了使用父类中所没有的方法的“权利”,在此处就是不能调用watchdog()这个方法。
而且结果里也可以看到,睡觉是引用的父类“Animal”的睡觉方法,这是因为Animal的睡觉方法为静态方法,可以总结如下:
如果是访问成员变量,编译的话就是看父类,运行同样是看父类。
如果访问的方法,编译就看父类,运行则看子类。
如果是静态方法,编译和运行都是看父类。

向下转型

先看一个错误的例子

public class Animal {
    public void eat(){
        System.out.println("Animal eat()");
    }
}

public class Dog extends Animal {
    @Override
    public void eat(){
        System.out.println("Dog eat");
    }
}

public class Test {
    public static void main(String[] args) {
        //向下转型
        Animal animal = new Animal();
        ((Dog)animal).eat();
    }
}

运行结果:
Exception in thread "main" java.lang.ClassCastException: com.hello.test.Animal cannot be cast to com.hello.test.Dog

at com.hello.test.Test.main(Test.java:7)

从上述例子来看,Java似乎并不支持向下转型,真是如此吗?其实不然,Java同样支持向下转型,只是向下转型是有条件的——只有引用子类对象的父类引用才能被向下转型为子类对象。也就是说,向下转型之前,必须先向上转型。

public class Animal {
    public void eat(){
        System.out.println("Animal eat()");
    }
}

public class Dog extends Animal {
    @Override
    public void eat(){
        System.out.println("Dog eat");
    }
}

public class Test {
    public static void main(String[] args) {
        //向上转型
        Animal animal = new Dog();
        //向下转型
        ((Dog)animal).eat();
    }
}

运行结果:
Dog eat

相关文章
|
7月前
|
设计模式 敏捷开发 Java
全网首发!Java界的四大名著之一:Java编程思想最新中文版已开源
老版《Java编程思想》(原书名《Thinking in Java》)得益作者开放深度研讨的创作方式,受到了全世界读者的追捧,被译为了十几种语言。但遗憾的是,在经历了 4 个版本的更新后,其最后一版发布于 2007 年,之后再无更新。
|
6月前
|
存储 安全 Java
小白聊《JAVA编程思想》-一切都是对象(一)
小白聊《JAVA编程思想》-一切都是对象(一)
31 0
|
2月前
|
安全 Java
【Java】向上转型和向下转型
【Java】向上转型和向下转型
20 0
|
安全 Java
【零基础学Java】—对象的向上和向下转型(二十七)
【零基础学Java】—对象的向上和向下转型(二十七)
|
6月前
|
Java
Java编程思想(面向对象)第四版PDF分享
探索《Java编程思想》第四版,理解Java不仅是特性集合,更是解决问题的强大工具。本书深入设计层面,构建编程思维模型,助你逐步精通Java。[阅读更多](https://zhangfeidezhu.com/?p=355) ![Java编程思想](https://ucc.alicdn.com/pic/developer-ecology/nrw3f3oqlpmag_c8ff959a921545f1bbabcefd37f029cf.png)
60 1
Java编程思想(面向对象)第四版PDF分享
|
6月前
|
Java
Java:什么是向上转型与向下转型
Java:什么是向上转型与向下转型
|
6月前
|
存储 Java 容器
小白聊《JAVA编程思想》-对象导论二
小白聊《JAVA编程思想》-对象导论二
29 0
|
6月前
|
存储 Java 数据安全/隐私保护
小白聊《JAVA编程思想》 对象导论一
小白聊《JAVA编程思想》 对象导论一
32 0
|
6月前
|
设计模式 安全 Java
Java编程思想重点开发必看
Java编程思想重点开发必看
28 0
|
6月前
|
设计模式 Java 程序员
《On Java 8》中文版,又名《Java 编程思想》中文第五版
《On Java 8》中文版,又名《Java 编程思想》中文第五版
230 0