ClassCastException深入分析

ClassCastException是JVM在检测到两个类型间转换不兼容时引发的运行时异常。此类错误通常会终止用户请求。在执行任何子系统的应用程序代码时都有可能发生ClassCastException异常。通过转换,可以指示Java编译器将给定类型的变量作为另一种变量来处理。对基础类型和用户定义类型都可以转换。Java语言规范定义了允许的转换,其中大多数可在编译时进行验证。不过,某些转换还需要运行时验证。如果在此运行时验证过程中检测到不兼容,JVM就会引发ClassCastException异常。例如:

Fruit f;

Apple a = (Apple)f;

当出现下列情况时,就会引发ClassCastException异常:

1.        Fruit和Apple类不兼容。当应用程序代码尝试将某一对象转换为某一子类时,如果该对象并非该子类的实例,JVM就会抛出ClassCastException异常。

2.        Fruit和Apple类兼容,但加载时使用了不同的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的理解,和在碰到问题时提供一种解决问题的思路。

ClassCastException and ClassLoader Puzzle

2/21/2013  
PIERRE-HUGUES CHARBONNEAU  
2 COMMENTS

The following question and puzzle will test your knowledge on Java class loaders and more precisely on one of the Java language specifications. It will also help you better troubleshoot problems such asjava.lang.NoClassDefFoundError.

I highly suggest that you do not look at the explanation and solution until you review the code and come up with your own explanation.

You can download the Java program source code and binaries (compiled with JDK 1.7) here. In order to run the program, simply use the following command:

<JDK 1.7 HOME>\bin\java -classpath MainProgram.jar org.ph.javaee.training9.ChildFirstCLPuzzle

** Make sure that you also download the 3 JAR files below before you run the program.

  • MainProgram.jar contains the main program along with super classProgrammingLanguage.
  • ProgrammingLanguage.jar contains the super class ProgrammingLanguage.
  • JavaLanguage.jar contains the implementation class JavaLanguage, which extendsProgrammingLanguage.

Question (puzzle)

Review closely the program source and packaging along with the diagram below reflecting the class loader delegation model used for this program.

Why can’t we cast (ChildFirstCLPuzzle.java @line 53) the Object javaLanguageInstance of type
JavaLanguage
, into 
ProgrammingLanguage
?

...............................

// Finally, cast the object instance into ProgrammingLanguage

/** Question: why is the following code failing with ClassCastException given the fact JavaLanguage is indeed a ProgrammingLanguage?? **/

ProgrammingLanguage programmingLanguage = (ProgrammingLanguage)javaLanguageInstance;

...............................

Propose a solution to allow the above cast to succeed without changing the original source code. Hint: look again at the class loader delegation model, packaging and diagram.

Answer & solution

The Java program is attempting to demonstrate a Java language specification rule related to class loaders: two classes loaded by different class loaders are considered to be distinct and hence incompatible.

If you review closely the program source, packaging and diagram, you will realize the following facts:

  • Our main program is loaded to the parent class loader e.g. $AppClassLoader.
  • The super class ProgrammingLanguage is also loaded to the parent class loader since it is referenced by our main program at line 53.
  • The implementation class JavaLanguage is loaded to our child class loader e.g. ChildFirstClassLoader which is following a “child first” delegation model.
  • Finally, the super class ProgrammingLanguage is also loaded to our child class loader.

The key point to understand is that the super class is loaded by 2 different class loaders. As per Java language specification, two classes loaded by different class loaders are considered to bedistinct and hence incompatible. This means that ProgrammingLanguage loaded from the “child firs” class loader is different and not compatible with ProgrammingLanguage loaded from the parent classloader. This is why the cast attempted at line 53 failed with the error below:

ChildFirstCLPuzzle execution failed with ERROR: java.lang.ClassCastException: org.ph.javaee.training9.JavaLanguage cannot be cast to org.ph.javaee.training9.ProgrammingLanguage

Now how can we fix this problem without actually changing the source code? Well please keep in mind that we are using a “child first” delegation model here. This is why we end up with 2 versions of the same ProgrammingLanguage class. The solution can be visualized as per below.

In order to fix this problem, we can simply ensure that we have only one version of ProgrammingLanguage loaded. Since our main program is referencing ProgrammingLanguage directly, the solution is to remove the ProgrammingLanguage.jar file from the child class loader. This will force the child class loader to look for the class from the 
parent
 class loader, problem solved! In order to test the solution, simply remove the ProgrammingLanguage.jar from your testing folder and re-run the program.

I hope you appreciated this puzzle related to “child first” class loader delegation model and class loader rules. This understanding is especially important when you are dealing with complex Java EE deployments involving many class loaders, exposing you to this type of problems at runtime.

Please do not hesitate to post any comment or question on this puzzle.

ClassCastException深入分析

时间: 2024-11-04 16:31:45

ClassCastException深入分析的相关文章

深入分析:Fragment与Activity交互的几种方式(三,使用接口)

第一步:我们需要在Fragment中定一个接口,并确保我们的容器Activity实现了此接口: public interface onTestListener { public void onTest(String str); } @Override public void onAttach(Activity activity) { super.onAttach(activity); // 这个方法是用来确认当前的Activity容器是否已经继承了该接口,如果没有将抛出异常 try { mCal

详细深入分析 ClassLoader 工作机制

申明:本文首发于 详细深入分析 Java ClassLoader 工作机制 ,如有转载,注明原出处即可,谢谢配合. 详细深入分析 Java ClassLoader 工作机制 什么是 ClassLoader ClassLoader 作用 1ClassLoader 类结构分析 2ClassLoader 的等级加载机制 Java默认提供的三个ClassLoader ClassLoader加载类的原理 原理介绍 2为什么要使用双亲委托这种模型呢 3 但是JVM在搜索类的时候又是如何判定两个class是相

深入分析ClassLoader工作机制

ClassLoader 较为深入分析. from <深入分析Java Web> 加载CLASS到JVM中,审查每个类应该由谁加载,父优先的等级加载机制. 加载机制 ClassLoader类结构分析 ClassLoader抽象类,有很多子类,一般在实现自己的ClassLoader时候,一般都会继承URLClassLoader这个子类,因为这个类已经实现了大部分的工作,就像Servlet通过会直接HttpServlet一样. 打开源码: 几个重要的方法 protected final Class

深入分析:Fragment与Activity交互的几种方式(一,使用Handler)

这里我不再详细介绍那写比较常规的方式,例如静态变量,静态方法,持久化,application全局变量,收发广播等等. 首先我们来介绍使用Handler来实现Fragment与Activity 的交互. 第一步,我们需要在Activity中定义一个方法用来设置Handler对象. public void setHandler(Handler handler) { mHandler = handler; } 第二步,在Fragment中的回调函数onAttach()中得到Fragment所在Acti

Android开发艺术探索——第七章:Android动画深入分析

Android开发艺术探索--第七章:Android动画深入分析 Android的动画可以分成三种,view动画,帧动画,还有属性动画,其实帧动画也是属于view动画的一种,,只不过他和传统的平移之类的动画不太一样的是表现形式上有点不一样,view动画是通过对场景的不断图像交换而产生的动画效果,而帧动画就是播放一大段图片,很显然,图片多了会OOM,属性动画通过动态的改变对象的属性达到动画效果,也是api11的新特性,在低版本无法使用属性动画,但是我们依旧有一些兼容库,OK,我们还是继续来看下详细

错误 java.lang.ClassCastException: com.ylpw.sms.YZZYSenderUtil cannot be cast to ResourceBundle

出现错误: java.lang.ClassCastException: com.ylpw.sms.YZZYSenderUtil cannot be cast to ResourceBundle 百度搜索错误,没有结果.谷歌搜索:http://stackoverflow.com/questions/5694017/specify-java-localization-file 解决方法,修改了一行代码 prop = ResourceBundle.getBundle(this.getClass().g

深入分析 Java I/O 的工作机制

I/O 问题可以说是当今互联网 Web 应用中所面临的主要问题之一,因为当前在这个海量数据时代,数据在网络中随处流动.这个流动的过程中都涉及到 I/O 问题,可以说大部分 Web 应用系统的瓶颈都是 I/O 瓶颈.本文的目的正是分析 I/O 的内在工作机制,你将了解到:Java 的 I/O 类库的基本架构:磁盘 I/O 工作机制:网络 I/O 的工作机制:其中以网络 I/O 为重点介绍 Java Socket 的工作方式:你还将了解到 NIO 的工作方式,还有同步和异步以及阻塞与非阻塞的区别,最

深入分析java序列化

概念 先来点简单的概念: what?why? 什么是序列化?为什么要序列化? 答曰:将java对象转成字节序列,用以传输和保存 where? 使用场景是什么? 答曰:对象的传输:状态的备份,例如jvm的dump文件: 好了,不装*了,下面说的详细点.其实对象的序列化主要有两种用途: 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中 在网络上传送对象的字节序列 在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存.比如最常见的是Web服务器中的Sessio

Spring MVC 教程,快速入门,深入分析

资源下载: Spring_MVC_教程_快速入门_深入分析V1.1.pdf SpringMVC核心配置文件示例.rar 作者:赵磊 博客:http://elf8848.iteye.com 目录  一.前言二.spring mvc 核心类与接口三.spring mvc 核心流程图 四.spring mvc DispatcherServlet说明 五.spring mvc 父子上下文的说明 六.springMVC-mvc.xml 配置文件片段讲解 七.spring mvc 如何访问到静态的文件,如j