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();
}
}
}