Java基础24:对象向上转型和向下转型

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

一、转型的分类和动态绑定介绍

1、转型的分类

转型是在继承的基础上而言的,继承是面向对象语言中,代码复用的一种机制,通过继承,子类可以复用父类的功能,如果父类不能满足当前子类的需求,则子类可以重写父类中的方法来加以扩展。

向上转型:子类引用的对象转换为父类类型称为向上转型。通俗地说就是用父类的引用变量去引用子类的实例对象,此处父类对象可以是接口。向上转型属于自动类型转换。

向下转型:父类引用的对象转换为子类类型称为向下转型。向下转型属于强制类型转换。

2、动态绑定介绍

程序绑定的概念:绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来。对Java来说,绑定分为静态绑定和动态绑定,或者叫做前期绑定和后期绑定

静态绑定:在程序执行前方法已经被绑定,此时由编译器或其它连接程序实现。例如:C。针对Java简单的可以理解为程序编译期的绑定;这里特别说明一点,Java当中的方法只有final,static,private和构造方法是前期绑定
动态绑定:

动态绑定:在运行时根据具体对象的类型进行绑定。若一种语言实现了后期绑定,同时必须提供一些机制,可在运行期间判断对象的类型,并分别调用适当的方法。也就是说,编译器此时依然不知道对象的类型,但方法调用机制能自己去调查,找到正确的方法主体。不同的语言对后期绑定的实现方法是有所区别的。但我们至少可以这样认为:它们都要在对象中安插某些特殊类型的信息。

动态绑定的过程:JVM虚拟机提取对象的实际类型的方法表—>JVM虚拟机搜索方法签名—>调用方法。

二、向上转型

1、前篇文章提到过,在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备调用该子类的方法的能力,其实这就是向上转型。

向上转型,就是用父类的引用变量去引用子类的实例,这是允许的。当向上转型之后,父类引用变量可以访问子类中属于父类的属性和方法,但是不能访问子类独有的属性和方法,因为向上转型后子类对象会丢失身份。

2、举个栗子:

假设有一个Fruit类,Fruit类中有一个show()方法,代码如下:

class Fruit{
	public void show() {
		System.out.println("this is a fruit");
	}
}

有一个Apple类继承自Fruit类,该类有自己的方法test(),并且重写了父类的show()方法,代码如下:

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()方法:

Fruit fruit = new Apple();//自动类型转换
fruit.show();

运行结果如下图所示:

向上转型需要注意的问题:

向上转型时,父类指向子类引用对象会遗失除与父类对象共有的其他方法,也就是在转型过程中,子类的新有的方法都会遗失掉,在编译时,系统会提供找不到方法的错误。

所以,子类对象当成父类对象,只能调用父类的成员,如果子类重写了父类的方法,就根据这个引用指向调用子类重写的这个方法。这个调用过程就称为“动态绑定”。

三、向下转型

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

/*
*  第一段代码
**/
Fruit fruit = new Apple();
Apple apple = (Apple) fruit;//强制类型转换

/*
*  第二段代码
**/
class Orange extends Fruit{
    @Override
    public void show() {
	System.out.println("this is a Orange");
    }
    public void test() {
	System.out.println("i am a Orange");
    }
}

 Fruit fruit = new Apple();
 Orange orange = (Orange) fruit;//强制类型转换

代码分析:

第一段代码向下转型成功:因为fruit引用的对象原本就是Apple对象向上转型得到的,在对fruit向下转型后得到的还是Apple类的对象,能够被Apple类的引用变量引用;

第二代码向下转型失败:上述代码虽然能够编译成功,但是在运行的时候会报错,因为fruit对象是由Apple对象向上转型得到的,只能够向下转型成Apple对象,不能够向下转型成Orange对象。

总结:

情况一:如果父类引用的对象如果引用的是指向的子类对象,那么在向下转型的过程中是安全的。也就是编译是不会出错误的。

情况二:如果父类引用的对象是父类本身或者原本不是子类对象,那么在向下转型的过程中是不安全的,编译不会出错,但是运行时会出现java.lang.ClassCastException错误。它可以使用instanceof来避免出错此类错误。

四、转型的好处

通过向上向下转型肯定是有好处的,比如可以减少编程代码。

假设在主类中定义了一个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()方法,减少了代码量
 

注意:向上转型常用于多态中,既能减少代码量,又能提高程序的扩展性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章