anonymous的Java学习笔记(12)——Java面向对象之引用数据类型的强制类型转换

文章转载自Java基础_引用数据类型的强制类型转换

在Java中由于继承向上转型子类可以非常自然地转换成父类,但是父类转换成子类则需要强制转换。因为子类拥有比父类更多的属性、更强的功能,所以父类转换为子类需要强制。那么,是不是只要是父类转换为子类就会成功呢?其实不然,他们之间的强制类型转换是有条件的。

当我们用一个类型的构造器构造出一个对象时,这个对象的类型就已经确定的,也就说它的本质是不会再发生变化了。在Java中我们可以通过继承、向上转型的关系使用父类类型来引用它,这个时候我们是使用功能较弱的类型引用功能较强的对象,这是可行的。但是将功能较强的类型强制转功能较弱的对象时,就不一定可以行了。

举个例子来说明。比如系统中存在Father、Son两个对象。首先我们先构造一个Son对象,然后用一个Father类型变量引用它:

 Father father = new Son();

在这里Son对象实例被向上转型为father了,但是请注意这个father对象实例在内存中的本质还是Son类型的,只是new Son()的引用类型被设置为Father而已。

这里有必要提一下: Father father = new Father ();中的Father 的作用是指father所引用的对象的类型, 在Java中, 任何对象变量的值都是对存储在另外一个地方的一个对象的引用, 一个对象变量并没有实际包含一个对象, 而仅仅引用了一个对象。

Son son = (Son) father;

这条语句是可行的,变量son引用了变量father的地址, 而father引用的对象地址是new Son()创建的Son类型的对象,这里只是将引用的类型强转为Son,而其本质引用地址所存储的对象就是Son类型的,所以是可行的。

测试:

public static void main(String[] args) {
	Father father = new Son();
	Son son = (Son) father;
	System.out.println(father);
	System.out.println(son);
}

控制台输出:

com.lic.demo.Manager@15db9742
com.lic.demo.Manager@15db9742
son和father引用的地址是相同的

前面提到父类强制转换成子类并不是总是成功,那么在什么情况下它会失效呢?

引用类型的真实身份是父类本身的类型时,强制类型转换就会产生错误

例如:

public static void main(String[] args) {Father father = new  Father();
    Father father = new Father ();
	Son son = (Son) father;
}

这个系统会抛出ClassCastException异常信息:

Exception in thread "main" java.lang.ClassCastException: com.lic.demo.Father cannot be cast to com.lic.demo.Son 
	at com.lic.demo.Test.main(Test.java:10)

为什么在这种情况父类强制转换成子类会抛异常呢?

  • 如果是子类强制转换成父类: 那么该Son类型对象的引用为Father类型, 由于Son类型是对Father类型的扩展, 所以在调用Father引用的方法在Son对象中都是存在的, 即可行。
  • 如果父类强制转换成子类: 父类引用的对象是Father类型的, 在强转之后, Father类型对象的引用为Son类型, 而被引用的对象仍是Father类型的, 因此, 在调用方法时,Son中扩展的方法在引用的Father类型对象中并不存在, 一个对象不可能调用一个该对象中不存在方法, 所以不可行! 如果父类引用的对象时是Son类型的, 那么在强转后是Son类型引用的是Son类型的对象, 是可行的。

注意: 编译器在编译时只会检查类型之间是否存在继承关系,有则通过;而在运行时就会检查它的真实类型,是则通过,否则抛出ClassCastException异常。所以在继承中,子类可以自动转型为父类,但是父类强制转换为子类时只有当引用类型真正的身份为子类时才会强制转换成功,否则失败。

可以使用instanceof在强转时先进行判断:

Father father = new Father();
if(father instanceof Son){
	Son son = (Son) father;
}

instanceof运算符是用来在运行时判断对象是否是特定类的一个实例。

instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。即:如果father是Son对象的引用,则可以进行强转。

instanceof 用法:

result = object instanceof class

参数:

  • Result:布尔类型。
  • Object:必选项。任意对象表达式。
  • Class:必选项。任意已定义的对象类。

说明:

  • 如果objectclass的一个实例,则instanceof运算符返回true
  • 如果object不是指定类的一个实例,或者objectnull,则返回false

instanceofJava编译状态运行状态是有区别的:

  • 在编译状态中,class可以是object对象的父类,自身类,子类。在这三种情况下Java编译时不会报错。
  • 在运行状态中,class可以是object对象的父类,自身类,不能是子类。在前两种情况下result的结果为true,最后一种为false。但是class子类时编译不会报错。运行结果为false