Java 虚拟机是如何判定两个 Java 类是相同

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

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值