Java 虚拟机不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样。只有两者都相同的情况,才认为两个类是相同的。
对于 Java 虚拟机来说,如果两个类不同,试图对这两个类的对象进行相互赋值,会抛出运行时异常 ClassCastException
。
//文件Sample.java
package com.luke;
public class Sample
{
private Sample instance;
public void setSample(Object instance) {
this.instance = (Sample) instance;
}
public void out(String msg)
{
System.out.println(msg);
}
}
//文件Test.java
package com.luke;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
class MyClassLoader extends ClassLoader
{
public Class loadClassMy(String path, String name, boolean resolve) throws ClassNotFoundException
{
Class klass = null;
try
{
klass = findLoadedClass(name); //检查该类是否已经被装载。
if (klass != null)
{
System.out.println("该类已经加载了");
return klass;
}
byte[] bs = getClassBytes(path, name);//从一个特定的信息源寻找并读取该类的字节。
if (bs != null && bs.length > 0)
{
klass = defineClass(name, bs, 0, bs.length);
}
if (klass == null)
{ //如果读取字节失败,则试图从JDK的系统API中寻找该类。
klass = findSystemClass(name);
}
if (resolve && klass != null)
{
resolveClass(klass);
}
}
catch (IOException e)
{
throw new ClassNotFoundException(e.toString());
}
System.out.println("klass == " + klass);
return klass;
}
private byte[] getClassBytes(String path, String className) throws IOException
{
//String path = System.getProperty("java.class.path") + File.separator;
path += className.replace('.', File.separatorChar) + ".class";
System.out.println(path);
FileInputStream fis = null;
try
{
fis = new FileInputStream(path);
}
catch (FileNotFoundException e)
{
System.out.println(e);
return null; //如果查找失败,则放弃查找。捕捉这个异常主要是为了过滤JDK的系统API。
}
byte[] bs = new byte[fis.available()];
fis.read(bs);
return bs;
}
}
public class Test
{
/**
* @description
* @param
* @return void
* @author luke
* @date 2013-5-23
* @version
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
//System.out.println(System.getProperty("java.class.path") + File.separatorChar);
try
{
MyClassLoader loader = new MyClassLoader();
MyClassLoader loader1 = new MyClassLoader();
//--------------测试1
/**
* 1.sun.misc.Launcher$AppClassLoader@190d11################sun.misc.Launcher$AppClassLoader@190d11
* sun.misc.Launcher$AppClassLoader@190d11****************sun.misc.Launcher$AppClassLoader@190d11
*/
Class c = loader.loadClass("com.luke.Sample");
Class c1 = loader1.loadClass("com.luke.Sample");
/*Class c = Sample.class;
Class c1 = Class.forName("com.luke.Sample");*/
System.out.println(c.getClassLoader() + "################" + c1.getClassLoader());
/**
*Test.class与Sample.class都在classpath路径下;
* 因为Test的加载器是系统加载器即sun.misc.Launcher$AppClassLoader@190d11,
* 根据加载器代理委托机制,c和c1的加载器也是sun.misc.Launcher$AppClassLoader@190d11,
* 类的全名又相同, 所以类相同, 可以强制转换,Test类也能看见Sample类.
*/
Sample o = (Sample)c.newInstance();
Sample o1 = (Sample)c1.newInstance();
System.out.println(o.getClass().getClassLoader() + "****************" + o1.getClass().getClassLoader());
o.out("hello world!");
Method setSampleMethod = c.getMethod("setSample", java.lang.Object.class);
setSampleMethod.invoke(o, o1);
Method m = c.getMethod("out", java.lang.String.class);
m.invoke(o, "你好");
//--------------测试1
//----------------------------测试2
/**
* 2.com.luke.MyClassLoader@10b30a7################com.luke.MyClassLoader@10b30a7
* com.luke.MyClassLoader@10b30a7****************com.luke.MyClassLoader@10b30a7
*/
Class c = loader.loadClassMy("D:\\workspace\\SSHDemo\\Web\\WEB-INF\\classes\\", "com.luke.Sample", false);
Class c1 = loader.loadClassMy("D:\\workspace\\SSHDemo\\Web\\WEB-INF\\classes\\", "com.luke.Sample", false);
System.out.println(c.getClassLoader() + "################" + c1.getClassLoader());
/**
*Test.class在classpath路径下,但Sample.class在D:\workspace\SSHDemo\Web\WEB-INF\classes\com\luke\下;
* 因为Test的加载器是系统加载器即sun.misc.Launcher$AppClassLoader@190d11,
* 根据加载器代理委托机制,c和c1的加载器都是自定义加载器com.luke.MyClassLoader@10b30a7,
* 虽然类的全名相同,Test与右边的c与c1的加载器不同, c与c1的加载器的父加载器是系统加载器,父加载器加载的类不能看见子加载器加载的类, 下面的实例不可以转换, 会报异常ClassCastException.
*/
/*Sample o = (Sample)c.newInstance();
Sample o1 = (Sample)c1.newInstance();
((Sample)o).out("hello world!");*/
Object o = c.newInstance();
Object o1 = c1.newInstance();
System.out.println(o.getClass().getClassLoader() + "****************" + o1.getClass().getClassLoader());
/**
* c和c1的加载器都是自定义加载器即com.luke.MyClassLoader@10b30a7,
* 类的全名又相同, 所以类相同, o与o1类型相同, 可以赋值.
*/
Method setSampleMethod = c.getMethod("setSample", java.lang.Object.class);
setSampleMethod.invoke(o, o1);
Method m = c.getMethod("out", java.lang.String.class);
m.invoke(o, "你好");
//----------------------------测试2
//----------------------------测试3
/**
* 2.com.luke.MyClassLoader@10b30a7################com.luke.MyClassLoader@1b67f74
* com.luke.MyClassLoader@10b30a7****************com.luke.MyClassLoader@1b67f74
*/
/* Class c = loader.loadClassMy("D:\\workspace\\Demo\\bin\\", "com.luke.Sample", false);
Class c1 = loader1.loadClassMy("D:\\workspace\\SSHDemo\\Web\\WEB-INF\\classes\\", "com.luke.Sample", false); */
Class c = loader.loadClassMy("D:\\workspace\\SSHDemo\\Web\\WEB-INF\\classes\\", "com.luke.Sample", false);
Class c1 = loader1.loadClassMy("D:\\workspace\\SSHDemo\\Web\\WEB-INF\\classes\\", "com.luke.Sample", false);
System.out.println(c.getClassLoader() + "################" + c1.getClassLoader());
/**
* 因为Test的加载器是系统加载器即sun.misc.Launcher$AppClassLoader@190d11,
* 根据加载器代理委托机制,c的加载器是com.luke.MyClassLoader@10b30a7,
* c1的加载器是com.luke.MyClassLoader@1b67f74,
* 虽然类的全名相同, c与c1的加载器的父加载器是系统加载器,父加载器加载的类不能看见子加载器加载的类, 下面的实例不可以转换, 会报异常ClassCastException.
*/
/*Sample o = (Sample)c.newInstance();
Sample o1 = (Sample)c1.newInstance();
((Sample)o).out("hello world!");*/
Object o = c.newInstance();
Object o1 = c1.newInstance();
System.out.println(o.getClass().getClassLoader() + "****************" + o1.getClass().getClassLoader());
/**
* c的加载器是com.luke.MyClassLoader@10b30a7,
* c1的加载器是com.luke.MyClassLoader@1b67f74,
* 虽然类的全名相同, 但是c和c1的加载器不同,所以类不相同, o与o1类型不同, 不可以赋值, 会报异常ClassCastException.
*/
/*Method setSampleMethod = c.getMethod("setSample", java.lang.Object.class);
setSampleMethod.invoke(o, o1); */
Method m = c.getMethod("out", java.lang.String.class);
m.invoke(o, "你好");
//----------------------------测试3
}
catch (ClassNotFoundException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (InstantiationException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IllegalAccessException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (SecurityException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IllegalArgumentException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
分享到:
相关推荐
MiniJavaVM—个Java虚拟机的设计和实现 在本篇文章中,我们将详细介绍 MiniJavaVM 的设计和实现,包括其总体架构、功能、运行环境和开发工具,以及具体的实现步骤。 第一章绪论 Java 虚拟机(Java Virtual ...
这时候又在国内著名Java社区ItEye中结识了另外两名译者吴璞渊和冶秀刚,我们在随后的两个多月的时间里共同完成了其余章节的翻译和校对。 《Java虚拟机规范》并非某一款虚拟机实现的说明书,它是一份保证各个公司...
本书是近年来国内出版的唯一一本与Java虚拟机相关的专著,也是唯一一本同时从核心理论和实际运用这两个角度去探讨Java虚拟机的著作,不仅理论分析得透彻,而且书中包含的典型案例和最佳实践也极具现实指导意义。
每次启动一个Java程序,都会创建一个Java虚拟机实例,该实例将管理Java程序的生命周期。Java虚拟机实例的生命周期结束时,该程序也将退出。 Java虚拟机体系结构 Java虚拟机的体系结构主要由四个部分组成:子系统、...
自己看书整理的 java虚拟机精品知识点 java内存区域与内存溢出处理 虚拟机栈和本地方法栈区别 对象定位访问 垃圾收集器GC管理 虚拟机GC垃圾回收收集算法(内存回收方法论) 虚拟机GC垃圾回收收集器(内存回收具体实现...
本书是近年来国内出版的唯一一本与Java虚拟机相关的专著,也是唯一一本同时从核心理论和实际运用这两个角度去探讨Java虚拟机的著作,不仅理论分析得透彻,而且书中包含的典型案例和最佳实践也极具现实指导意义
JAVA虚拟机内存分配与回收机制 JAVA虚拟机的内存分配与回收机制是JAVA语言的核心机制之一。该机制将内存划分为两种:栈内存和堆内存。 栈内存是用于存放基本类型的变量和对象的引用变量。在函数中定义的变量和对象...
下载安装R.E....最后要将两个文件的权限改成和其他文件一样,如图所示。 重启就可以使用了。 注意:R.E.管理器 需要最高权限 R.E.管理器 需要挂载为读写才能复制文件到系统文件夹,和更改文件权限。
由于附件不能大于20M,所以分两个上传,都下载后解压即可
“Java虚拟机”和“Java本地接口”是本支持库中最重要的两个数据类型。由“Java虚拟机”负责真实Java虚拟机的生命周期管理,而“Java本地接口”则提供Java类库的访问接口。使用时,可直接通过“Java本地接口”的各类...
Java语言规范和JVM虚拟机规范.zip,Oracle官方文档,包括Java语法的定义等内容。Java语言规范包含JDK678三个版本,JVM虚拟机规范包含78两个版本。
在本篇内容中小编给大家分享的是关于java虚拟机中多线程的知识点总结内容,需要的朋友们参考学习下。
“Java虚拟机”和“Java本地接口”是本支持库中最重要的两个数据类型。由“Java虚拟机”负责真实Java虚拟机的生命周期管理,而“Java本地接口”则提供Java类库的访问接口。使用时,可直接通过“Java本地接口”的各类...
着重于Scala的两个重要概念的编译:mixin继承和运行时类型。 编译技术是在Java虚拟机的上下文中提出的,但可以轻松地适应其他类似环境。
使用步骤: 1.用R.E.管理器rootexplorer将下载的libjbedvm....最后要将两个文件的权限改成和其他文件一样,如图(设置权限)。 重启就可以使用了。 有的手机可能会需要虚拟按键。这里也提供给大家。 swkey306汉化版.apk
Java复习题,附答案。选择题中红色选项为答案项。对于考试比较具有代表性。
java.rmi.registry 提供 RMI 注册表的一个类和两个接口。 java.rmi.server 提供支持服务器端 RMI 的类和接口。 java.security 为安全框架提供类和接口。 java.security.acl 此包中的类和接口已经被 java.security...
JAVA使用Unicode编码,每个字符占两个字节,可以用十六进制编码形式表示。 整数型可以分为byte、short、int、long四种,分别占1、2、4、8个字节。浮点型可以分为float和double两种,分别占4、8个字节。需要注意的是...