黑马程序员技术交流社区

标题: [哈尔滨java基础二期]多态常见问题之ClassCastException---类型... [打印本页]

作者: Hansion    时间: 2015-12-1 23:42
标题: [哈尔滨java基础二期]多态常见问题之ClassCastException---类型...
昨天在多态的部分练习敲代码,敲完了发现有个问题,编译没问题,运行会出现 java.lang.ClassCastException

就这个问题展开了一下学习:


ClassCastException  的意思是类型转换异常


详细的说,ClassCastExceptionJVM在检测到两个类型间转换不兼容时引发的运行时异常。此类错误通常会终止用户请求。


这种异常通常是怎么产生的呢?


举一个例子:


Animal表示动物,Dog表示狗,是动物的子类,Cat表示猫,是动物的子类。看下面的代码:


Animal a1 = new Dog();  // 第1行 父类引用指向子类对象 向上转型

Animal a2 = new Cat();  // 第2行 父类引用指向子类对象 向上转型

Dog d1 = (Dog)a1;         //第3行 向下转型

Dog d2 = (Dog)a2;        //第4行  向下转型


第3行代码和第4行代码基本相同,从字面意思看都是把动物(Animal)强制转换为狗(Dog),但是第4行代码将产生java.lang.ClassCastException。


原因是什么呢?


因为你要把一个猫(a2这只动物是猫)转换成狗,而第3行中是把狗转换成狗,所以可以。


从上面的例子看,java.lang.ClassCastException是进行向下转型的时候产生的异常,向下转换的前提是父类引用指向子类对象(向上转型)的时候才可以进行强制类型转换,如果父类引用指向的对象的类型不是子类的时候或者向下转型的时候不是转换为自己的子类将产生java.lang.ClassCastException异常。就是上面a1和a2都是动物,但是a1这只动物是一只狗,而a2这只动物是猫,所以要把a1转换成狗可以,因为a1本身就是狗,而a2是一只猫,所以要转换成狗就出错了。


而我是错在哪里呢?下面贴出我的代码:

BaseTeacher和WorkTeacher是Teacher的子类


Teacher t = new BaseTeacher("张三",22);    //第一行:父类引用指向子类对象
BaseTeacher b = (BaseTeacher)t;                 //第二行:向下转型


Teacher t1 = new WorkTeacher("李四",24);     //第三行:父类引用指向子类对象
WorkTeacher w = (WorkTeacher)t;                  //第四行:向下转型


大家能看出错在哪里了吗?


答案是第四行运行时抛出java.lang.ClassCastException异常


因为我在第四行对父类的引用是t1,而在第四行由于自己的马虎写成了t,


而t是在第一行对子类BaseTeacher用的父类引用,
所以被认为是不安全的向下转型,编译无错但会运行会出错


将第四行的t改成t1即可


上面的问题总结来说就是逻辑上的问题,由于个人水平还没达到,所以还有很多其他原因我还没遇到,也无法对此进行详细介绍,对于这类问题我查了些资料.也许以后会用到:



如果你知道要访问的的对象的具体类型,直接转换成该类型即可。如果不能确定类型可以通过下面的两种方式进行处理(假设对象为o):

1、通过o.getClass().getName()得到具体的类型,可以通过输出语句输出这个类型,然后根据类型进行进行具体的处理。

2、通过if(o instanceof 类型)的语句来判断o的类型是什么。




还有一种情况就是两个类是兼容的,但加载时使用了不同的ClassLoader。这是这种异常发生最常见的原因。


在这里,需要了解一下什么是ClassLoader?下面是关于ClassLoader的资料:

ClassLoader是允许JVM查找和加载类的一种Java类。JVM有内置的ClassLoader。不过,应用程序可以定义自定义的ClassLoader。应用程序定义新的ClassLoader通常出于以下两种原因:
          1.自定义和扩展JVM加载类的方式。例如,增加对新的类库(网络、加密文件等)的支持。
          2.划分JVM名称空间,避免名称冲突。例如,可以利用划分技术同时运行同一应用程序的多个版本(基于空间的划分)。此项技术在应用服务器(如WebLogic Server)内的另一个重要用途是启用应用程序热重新部署,即在不重新启动JVM的情况下启动应用程序的新版本(基于时间的划分)。
ClassLoader按层级方式进行组织。除系统BootClassLoader外,其它ClassLoader都必须有父ClassLoader。



在理解类加载的时候,需要注意以下几点


1.永远无法在同一ClassLoader中重新加载类。“热重新部署”需要使用新的ClassLoader。每个类对其ClassLoader的引用都是不可变的:this.getClass().getClassLoader()。
2.在加载类之前,ClassLoader始终会先询问其父ClassLoader(委托模型)。这意味着将永远无法重写“核心”类。
3.同级ClassLoader间互不了解。
4.由不同ClassLoader加载的同一类文件也会被视为不同的类,即便每个字节都完全相同。这是ClassCastException的一个典型原因。
5.可以使用Thread.setContextClassLoader(a)将ClassLoader连接到线程的上下文。
基于以上的基本原理,可以加深大家对ClassCastException的理解,和在碰到问题时提供一种解决问题的思路。




参考文献:

dev2dev专刊 2005年 第二期

j2sdk-1_5_0-doc


作者: 高盖茨    时间: 2015-12-1 23:47
写的不错  很受启发  加油!
作者: yangmimi    时间: 2015-12-13 20:24
很好.赞赞




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2