在使用Java的多态机制时,常常使用的一个特性便是子类和父类之间的对象转换。从子类向父类的转换称为向上转换(upcasting),通过向上转换,我们能够在编写程序时采用通用程序设计的思想,在需要使用子类对象的时候,通过把变量定义为父类型,我们可以通过一个变量,使用该父类型的所有子类型实例;从父类型向子类型的转换称为向下转换(downcasting),通过向下转换,我们能在必要的时候,将父类型变量转换成子类型变量,使用一些通过子类型才能够使用的方法。以下是我对于对象转换的一些个人理解,如有不对,欢迎指正,虚心向大神们请教。

  首先是从子类向父类的向上转换。向上转换比较直观,总是能够将一个子类的实例转换为一个父类的对象,从继承链的角度,这个特性很容易理解:继承是一种“是一种”的关系,从父类派生出的子类,我们都能理解为,子类总是父类的一个实例。比如说,Fruit类派生出了Orange类,Apple类,Orange和Apple都是Fruit;Animal类派生出了Tiger类和Lion类,Tiger和Lion都是Animal。因此,从子类向父类的转换不需要什么限制,只需直接将子类实例赋值给父类变量即可,这也是Java中的多态的实现机制。

//Test.java
public class Test {
public static void main(String args[]) {
Animal tiger = new Tiger();
Animal lion = new Lion();
}
} class Animal {
String name;
Animal() {
name = "animal";
}
Animal(String name) {
this.name = name;
}
}
class Tiger extends Animal {
Tiger() {
super("tiger");
}
}
class Lion extends Animal {
Lion() {
super("lion");
}
}

  

  然而从父类向子类的向下转换就稍微复杂一些了。在讲述向下转换之前,也许有些刚学java的朋友会有点不解为什么要使用向下转换,使用多态和动态绑定机制通过父类型变量使用子变量不就可以了么(比如我就曾对此感到疑惑)。这就要考虑到,在继承关系中,有一些方法是不适合由父类定义并由子类继承并重写的,有些方法是子类特有的,不应该通过继承得到,且子类可能也会有自己特有的成员变量,那么在使用多态机制的时候,若我们要通过父类型变量使用到这些子类特有的方法和属性的话,就需要将服类型变量转换成对应的子类型变量。一个典型例子便是标准库中的数据类型包装类:Integer类,Double类,Long类等,它们都继承自Number类,且它们都有一个方法叫做compareTo用于比较两个同样的类型。然而这个方法是这些子类通过实现Comparable接口来实现的,在Number类中并没有该方法的实现,因此若要通过Number类型变量来使用compareTo方法,就要先将Number类转换成子类的对象。

  从父类向子类的转换就有限制了。首先,父类变量向子类转换必须通过显式强制类型转换,采取和向上转换相同的直接赋值方式是不行的,;并且,当把一个父类型变量实例转换为子类型变量时,必须确保该父类变量是子类的一个实例,从继承链的角度来理解这些原因:子类一定是父类的一个实例,然而父类却不一定是子类的实例;比如说,Fruit未必是Orange,它可能是Apple;Animal也不一定是Tiger,它可能是Lion。用代码来解释一下:

Animal tiger = new Tiger();
Animal lion = new Lion();

  在前面向上转换的代码示例当中,main方法中的这两行代码,意思就是父类型变量tiger是子类Tiger的一个实例,lion是Lion的一个实例。 
  也就是说,如果要把tiger转换为Tiger类型,必须保证tiger本身是Tiger的一个实例,在上例中,如果要把tiger转换成Lion类型,或是把Lion类型转换为Tiger类型,都是行不通的,在运行时,这会抛出一个运行异常ClassCastException,表示类转换异常。因此,在进行父类向子类的转换时,一个好的习惯是通过instanceof运算符来判断父类变量是否是该子类的一个实例:

  

Tiger t = null;
if(tiger instanceof Tiger)
t = (Tiger)tiger;

  如果要通过父类调用子类变量的方法,那么要注意要将父类型变量和强制转换用括号括起来:

Number i = new Integer(3);
System.out.println(
((Integer)i).compareTo(new Integer(4))
);

  因为成员访问运算符.的优先级大于类型转换,所以要用括号括起来保证类型转换先于成员访问进行运算。 
  前面说到用instanceof判断父类是否是子类的一个实例是一个好习惯,如果不养成这个习惯的话很容易出问题,请看下面这段代码:

Animal animal = new Tiger();
Lion lion = (Lion)tiger;

  前面说到,这段代码会在运行时抛出ClassCastException异常,然而,这段代码却是能够编译成功的。原因是因为,Java编译器并没有聪明到能够在编译阶段就知道父类型变量是哪一个子类的实例,所以,将animal转换为Lion类型的代码:(Lion)animal是能够编译通过的,即使事实上我们能看到animal是Tiger的一个实例,因为Animal类型确实能转换成Lion类型,所以这条语句是合法的。所以,如果没有使用instanceof防止不同子类型之间的对象转换,而又不能指望编译器检查出这种转换逻辑错误的话,就很容易犯错了。

(转载) Java子类与父类之间的对象转换的更多相关文章

  1. Java子类与父类之间的类型转换

    1.向上转换 父类的引用变量指向子类变量时,子类对象向父类对象向上转换.从子类向父类的转换不需要什么限制,只需直接蒋子类实例赋值给父类变量即可,这也是Java中多态的实现机制. 2.向下转换 在父类变 ...

  2. Java拾遗(一):浅析Java子类和父类的实例化顺序 及 陷阱

    本文主要介绍Java里经常使用的子类和父类的变量实例化顺序及陷阱,并结合一个Android实例来探讨此问题.日后编程中应尽量避免此陷阱. 首先看以下一段代码: 定义一个虚类Server.java pa ...

  3. c++ 子类与父类之间的类型转换

    子类与父类之间的类型转换 先给一段代码 class Base { public: int a = 10; }; class pub_Derv : public Base { Base *getBase ...

  4. java 子类重写父类的方法应注意的问题

    若想实现一个合格重写方法,而不是重载,那么必须同时满足下面的要求! A.重写规则之一:    重写方法不能比被重写方法限制有更严格的访问级别.(但是可以更广泛,比如父类方法是包访问权限,子类的重写方法 ...

  5. Java子类和父类的初始化执行顺序

    要明白子类和父类的初始化执行顺序,只需要知晓以下三点,就不会再弄错了. 1.创建子类对象时,子类和父类的静态块和构造方法的执行顺序为:父类静态块->子类静态块->父类构造器->子类构 ...

  6. java 子类、父类中静态代码块、字段,非静态代码块、字段以及构造函数的初始化顺序和次数

    一个类中的数据初始化顺序是面试官非常喜欢出的面试题之一,本文用一个实例来介绍java中子类.父类中静态代码块.字段,非静态代码块.字段以及构造函数的执行顺序和次数. 一.包结构

  7. Java子类对于父类中static方法的继承

    今天看到了Java中子类继承父类的一个相关讨论,在网上综合了各家的观点,写下了一篇简短的总结. 问题如下,在父类中含有staic修饰的静态方法,那么子类在继承父类以后可不可以重写父类中的静态方法呢? ...

  8. Java 子类 instanceof 父类

    1.结论: 子类 instanceof 父类 == true 父类 instanceof 子类 == false 2.测试代码 @Test public void testInstanceof() { ...

  9. Java子类访问父类的私有成员变量

    /**子类会继承父类所有的属性和方法. * 但是根据不同的权限标识符,子类不可见父类的私有变量,但可以通过父类的公共方法访问私有变量 * 所以对于重名变量,子类和父类都各有一份. * 对于子类和父类中 ...

随机推荐

  1. Software development process

    一.Development process 1.Business/User Requirement 2.Architecture Proposal,Solution Proposal 3.Functi ...

  2. SpringMVC + Spring + Mybatis+ Redis +shiro以及MyBatis学习

    SpringMVC + Spring + Mybatis+ Redis +shiro http://www.sojson.com/shiro MyBatis简介与配置MyBatis+Spring+My ...

  3. react-native绑定优酷SDK播放视频-附效果和git源码

    ReactNative绑定优酷SDK需要用到两部分知识: 优酷本身的sdk绑定: RN与原生界面的交互: 效果: RN版本:0.49.3 代码更新日期:2017.10.26 下文也根据绑定优酷需要的两 ...

  4. JAVA基础知识总结:三

    一.Java语句的执行结构 1.顺序语句 按照顺序从上往下依次执行的语句,中间没有任何的判断和跳转 2.分支语句 根据不同的条件来产生不同的分支 if语句.switch语句 3.循环语句 重复执行某句 ...

  5. tamcat的使用

    tomcat的基础知识 一.tomcat的定义 apache的官网是这么说的:使用Apache Tomcat ®软件了Java Servlet,JavaServer页,Java表达式语言和Java的W ...

  6. idea配置jetty服务器,通过mvn实现

    今天想试试除了tomcat之外的另一个服务器jetty的使用: 关于项目在tomcat中的启动大概有多种,尤其是在本地环境下,ide关于tomcat的优化做的很好,但是在idea上面部署tomcat总 ...

  7. 安装mayavi和VTK库的血泪史

    一开始安装VTK库是从官网上下载,但是怎么都找不到whl文件,只有exe文件(vtkpython-7.1.1-Windows-64bit.exe).下载安装之后再PyCharm中import vtk出 ...

  8. python 进程池(multiprocessing.Pool)和线程池(threadpool.ThreadPool)的区别与实例

    一般我们是通过动态创建子进程(或子线程)来实现并发服务器的,但是会存在这样一些缺点: 1.动态创建进程(或线程)比较耗费时间,这将导致较慢的服务器响应.  2.动态创建的子进程通常只用来为一个客户服务 ...

  9. IdentityServer4 SigningCredential(RSA 证书加密)

    IdentityServer4 默认提供了两种证书加密配置: services.AddIdentityServer() .AddDeveloperSigningCredential() .AddTem ...

  10. 阿里云RDS for SQL Server使用的一些最佳实践

    了解RDS的概念 这也是第一条,也是最重要的一条,在使用某项产品和服务之前,首先要了解该产品或服务的功能与限制,就像你买一个冰箱或洗衣机,通常也只有在阅读完说明书之后才能利用起来它们的所以功能,以及使 ...