`
banxi1988
  • 浏览: 153726 次
  • 性别: Icon_minigender_1
  • 来自: 桂林
社区版块
存档分类
最新评论

不同ClassLoader加载的类,能cast赋值啊!

阅读更多
我在学习ClassLoader的时候.
参照下面的地址的文章:深入探讨 Java 类加载器
其中讲到下面的这段:
引用

类加载器的代理模式
类加载器在尝试自己去查找某个类的字节代码并定义它时,会先代理给其父类加载器,由父类加载器先去尝试加载这个类,依次类推。在介绍代理模式背后的动机之前,首先需要说明一下 Java 虚拟机是如何判定两个 Java 类是相同的。Java 虚拟机不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样。只有两者都相同的情况,才认为两个类是相同的。即便是同样的字节代码,被不同的类加载器加载之后所得到的类,也是不同的。比如一个 Java 类 com.example.Sample,编译之后生成了字节代码文件 Sample.class。两个不同的类加载器 ClassLoaderA 和 ClassLoaderB 分别读取了这个 Sample.class 文件,并定义出两个 java.lang.Class 类的实例来表示这个类。这两个实例是不相同的。对于 Java 虚拟机来说,它们是不同的类。试图对这两个类的对象进行相互赋值,会抛出运行时异常 ClassCastException。下面通过示例来具体说明。代码清单 3 中给出了 Java 类 com.example.Sample。

其中清单3的代码如下:
package com.example; 
public class Sample { 
    private Sample instance; 

    public void setSample(Object instance) { 
        this.instance = (Sample) instance; 
    } 
}


一个测试方法如下: 即代码清单5.

public void testClassIdentity() { 
    String classDataRootPath = "C:\\workspace\\Classloader\\classData"; 
    FileSystemClassLoader fscl1 = new FileSystemClassLoader(classDataRootPath); 
    FileSystemClassLoader fscl2 = new FileSystemClassLoader(classDataRootPath); 
    String className = "com.example.Sample"; 	
    try { 
        Class<?> class1 = fscl1.loadClass(className); 
        Object obj1 = class1.newInstance(); 
        Class<?> class2 = fscl2.loadClass(className); 
        Object obj2 = class2.newInstance(); 
        Method setSampleMethod = class1.getMethod("setSample", java.lang.Object.class); 
        setSampleMethod.invoke(obj1, obj2); 
    } catch (Exception e) { 
        e.printStackTrace(); 
    } 
}


文章对此的测试结果是如下:
引用

java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at classloader.ClassIdentity.testClassIdentity(ClassIdentity.java:26)
at classloader.ClassIdentity.main(ClassIdentity.java:9)
Caused by: java.lang.ClassCastException: com.example.Sample
    cannot be cast to com.example.Sample
at com.example.Sample.setSample(Sample.java:7)
... 6 more

结果解释,如上,和下面:
引用

从 代码清单 5 给出的运行结果可以看到,运行时抛出了 java.lang.ClassCastException 异常。虽然两个对象 obj1 和 obj2 的类的名字相同,但是这两个类是由不同的类加载器实例来加载的,因此不被 Java 虚拟机认为是相同的。
了解了这一点之后,就可以理解代理模式的设计动机了。代理模式是为了保证 Java 核心库的类型安全。所有 Java 应用都至少需要引用 java.lang.Object 类,也就是说在运行的时候,java.lang.Object 这个类需要被加载到 Java 虚拟机中。如果这个加载过程由 Java 应用自己的类加载器来完成的话,很可能就存在多个版本的 java.lang.Object 类,而且这些类之间是不兼容的。通过代理模式,对于 Java 核心库的类的加载工作由引导类加载器来统一完成,保证了 Java 应用所使用的都是同一个版本的 Java 核心库的类,是互相兼容的。
不同的类加载器为相同名称的类创建了额外的名称空间。相同名称的类可以并存在 Java 虚拟机中,只需要用不同的类加载器来加载它们即可。不同类加载器加载的类之间是不兼容的,这就相当于在 Java 虚拟机内部创建了一个个相互隔离的 Java 类空间。这种技术在许多框架中都被用到,后面会详细介绍。


但是,
但是我测试在机子上测试的时候,运行了N多次都没有见到,异常出来,我是第一次希望出现异常,
但是没有出现.
我测试代码如下,其它的没有变,就是包名变了,和一些路径变了.:
package guet.dream.jvm;

public class Sample {

	private Sample instance;
	
	public void setSample(Object instance){
		this.instance = (Sample)instance;
	}
}



测试方法:
package guet.dream.jvm;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ClassIndentityTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		String classDataRootPath = "guet/dream/jvm/";
		FileSystemClassLoader fscl1 = new FileSystemClassLoader(classDataRootPath);
		FileSystemClassLoader fscl2 = new FileSystemClassLoader(classDataRootPath);
		String className = "guet.dream.jvm.Sample";
		
		try {
			Class<?> clazz1 = fscl1.loadClass(className);
			Class<?> clazz2 = fscl2.loadClass(className);
			
			Object obj1 = clazz1.newInstance();
			Object obj2 = clazz2.newInstance();
			
			Method setSampleMethod = clazz1.getMethod("setSample", Object.class);
			setSampleMethod.invoke(obj1, obj2);
		} catch (ClassNotFoundException e) {
			
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			
			e.printStackTrace();
		} catch (SecurityException e) {
		
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			
			e.printStackTrace();
		}

	}

}


下面是原文中,我稍加修改的ClassLoader.
package guet.dream.jvm;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

public class FileSystemClassLoader extends ClassLoader {

	private String rootDir;
	public FileSystemClassLoader(String rootDir){
		this.rootDir = rootDir;
	}
	
	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException{
		byte[] classData = getClassData(name);
		if(classData == null){
			throw new ClassNotFoundException();
		}else{
			return defineClass(name, classData, 0, classData.length);
		}
	}

	private byte[] getClassData(String className) {
		String path = classNameToPath(className);

			InputStream in;
			try {
				in = new FileInputStream(path);
				ByteArrayOutputStream baos = new ByteArrayOutputStream();
				int bufferSize = 4096;
				byte[] buffer = new byte[bufferSize];
				int bytesNumRead = 0;
				while((bytesNumRead = in.read(buffer)) != -1	){
					baos.write(buffer,0,bytesNumRead);
				}
				return baos.toByteArray();
			} catch (FileNotFoundException e) {
				
				e.printStackTrace();
			} catch (IOException e) {
				
				e.printStackTrace();
			}


		
		return null;
	}

	private String classNameToPath(String className) {
		return rootDir+File.separatorChar+className.replace('.', File.separatorChar)+".class";
	}
}


运行结果没有任何异常,希望,有对这方面了解的,各位网友,测试或者给解答下.
这好像是我发的第一个贴吧!
我的JDK信息如下:
引用

banxi1988@banxi:~$ java -version
java version "1.6.0_22"
OpenJDK Runtime Environment (IcedTea6 1.10.2) (6b22-1.10.2-0ubuntu1~11.04.1)
OpenJDK Server VM (build 20.0-b11, mixed mode)
banxi1988@banxi:~$

分享到:
评论
2 楼 vavi 2013-08-06  
这个原因应该是你的类放在系统的classpath下面,由系统类加载器加载了,而不是你的自定义类加载器.
你仅仅覆写了findClass方法,但是findClass方法时被loadClass方法调用的,loadClass方法仍然是双亲委派的.

你可以尝试将需要加载的类放到非classpath目录下面,应该就会出现报错了.

1 楼 xyg123_1979163.com 2013-07-12  
因为你的FileSystemClassLoader没有指定其父ClassLoader,他默认就会使用当前线程的ContextClassloader作为其父Classloader,因为guet.dream.jvm.Sample类已经被当前线程的ContextClassloader所加载,当你用main中定义的两个FileSystemClassLoader去加载这个类时,都是首先委托给他们的父加载器进行加载,所以得到的Class对象实际上是由当前线程的Classloader加载的,因此不会抛出异常

相关推荐

    ClassLoader类加载机制和原理详解

    在Java编程语言中,ClassLoader是核心组件之一,它负责加载类到JVM(Java虚拟机)中执行。本文将深入探讨ClassLoader的工作原理和类加载机制,帮助开发者理解这个至关重要的概念。 1. 类加载机制概述 Java的类加载...

    ClassLoader运行机制 自己写的

    在Java虚拟机(JVM)中,类加载器(ClassLoader)是至关重要的组成部分,它负责查找和加载类的字节码文件。理解ClassLoader的工作机制对于深入掌握Java应用程序的运行至关重要。这里我们将详细讨论ClassLoader的运行...

    【图解版】深入分析ClassLoader类加载工作机制

    【图解版】深入分析ClassLoader类加载工作机制,从原理到JVM的装载过程,详情分析了ClassLoader加载类以及自定义类加载器的过程,不可用于商业用途,如有版权问题,请联系删除!

    ClassLoader类加载器

    在Java编程语言中,ClassLoader是一个至关重要的组成部分,它负责加载类到JVM(Java虚拟机)中。了解和掌握ClassLoader的工作原理以及如何自定义ClassLoader对于深入理解Java应用程序的运行机制非常有帮助。以下是对...

    JAVA ClassLoader 讲解 (类加载器)

    编译类加载器是一种特殊的类加载器,它在加载类之前会先对源代码进行编译。这种类型的类加载器对于那些需要在运行时动态生成或修改代码的应用场景非常有用。 #### 五、Java 2中的类加载器变化 Java 2平台引入了...

    使用classloader动态加载Class

    Java提供了三个基础类加载器:Bootstrap ClassLoader、Extension ClassLoader和AppClassLoader,它们按照层次结构加载类。 1. **自定义ClassLoader**:Java允许我们创建自定义的ClassLoader,这通常用于实现动态...

    ClassLoader(类加载机制)1

    Java的类加载机制是其运行的核心...理解ClassLoader的工作原理以及如何动态加载类,对于优化程序性能、实现高级功能,如插件系统、模块化等,具有重要意义。开发者可以根据需求自定义加载策略,以满足特定的应用场景。

    ClassLoader类加载机制

    类加载器是 Java 语言的一个创新,也是 Java 语言流行的重要原因之一。它使得 Java 类可以被动态加载到 Java 虚拟机中并执行。类加载器从 JDK 1.0 就出现了,最初是为了满足 Java Applet 的需要而开发出来的。Java ...

    java的ClassLoader类加载器机制

    在 JVM 运行过程中,类加载器会形成一个层次结构,包括引导类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)和系统类加载器(System ClassLoader)。 引导类加载器(Bootstrap ...

    classloader类加载器_基于java类的加载方式详解

    如果父类加载器无法完成加载任务,子类加载器才会尝试自己加载类。 这种模式的优点在于它可以有效防止类的重复加载,确保Java核心库的类型安全。比如,如果用户尝试使用Application ClassLoader加载rt.jar中的类,...

    Java中ClassLoader类加载学习总结

    Java中ClassLoader类加载学习总结 ...ClassLoader类的应用非常广泛,例如在Web应用程序中,ClassLoader类可以用来加载不同类库的类,在Android应用程序中,ClassLoader类可以用来加载不同的apk文件等等。

    Java ClassLoader定制实例

    在Java编程语言中,ClassLoader是一个至关重要的组成部分,它负责加载类到JVM(Java虚拟机)中。理解ClassLoader的工作原理以及如何定制它,对于深入学习Java的运行机制和进行高级应用开发具有重要意义。本篇文章将...

    tomcat 类加载机制 —— ClassLoader

    类加载遵循“双亲委托模型”,即当一个类加载器收到加载类的请求时,它会首先委托父类加载器尝试加载,只有当父类加载器无法找到对应的类时,才会自己尝试加载。这样保证了基础类库的一致性,同时也允许Web应用覆盖...

    JVM ClassLoader简析

    ClassLoader是JVM中的一个重要组件,它的主要任务是加载类的二进制数据,转换为Class对象,并供Java应用程序使用。本文将深入浅出地探讨JVM ClassLoader的工作原理和相关知识点。 首先,ClassLoader可以分为三种...

    ClassLoader加载机制

    该电子书详细介绍了java虚拟机类加载机制,对于深入理解jvm工作原理有很好的帮助作用,对于初学java,有一定工作经验的小伙伴来说是一本提高自身java素养,夯实自己java基本技能的“葵花宝典”。

    S18-ClassLoader类加载流程1

    这是一种设计模式,当类加载器接收到加载类的请求时,它首先会将请求委派给其父类加载器处理,只有在父类加载器无法加载时,子类加载器才会尝试加载。这种模型保证了类的唯一性,避免了类的重复加载和冲突。 4. **...

    java应用程序类加载器,ClassLoader for java Application

    总结来说,Java应用程序类加载器是Java程序执行的关键部分,它负责在运行时找到并加载类,而它的灵活性和可配置性为开发者提供了强大的能力来适应各种复杂的应用场景。理解并掌握类加载器的工作原理,能帮助我们更好...

    ClassLoader动态加载dex

    `DexFile`类提供了从.dex文件加载类的方法,例如`loadDex()`。一旦类加载到内存,就可以像使用普通类一样使用它们。 在实际应用中,动态加载dex文件通常伴随着一些挑战,比如权限问题、多版本兼容、类冲突等。例如...

    类加载机制ClassLoader

    在Java编程语言中,类加载机制(ClassLoader)是理解JVM(Java Virtual Machine)工作原理的关键部分。它负责将.class文件从磁盘加载到内存中,使得Java程序能够执行。这个过程包括加载、验证、准备、解析和初始化五...

    java自定义类加载classloader文档,包括代码

    - **`findClass()`方法**:重写`ClassLoader`中的`findClass()`方法,该方法用于实际加载类的二进制数据。首先调用`loadClassData()`方法从文件系统读取类文件数据,然后使用`defineClass()`方法将二进制数据转换为`...

Global site tag (gtag.js) - Google Analytics