• 如何唯一确定一个 Java 类?


    今天偶然想起之前和朋友讨论过的一个问题:如何唯一确定一个 Java 类?我相信大多数朋友遇到这个问题的回答都是:类的全路径呗。但事实上,唯一确定一个 Java 类,单单靠类路径是不够的,还要多加上一个东西:类加载器。也就是说,类加载器 + 类路径才唯一确定一个 Java 类。

    为了证明我所说的,我们来做一个简单的实验。

    //自定义一个类加载器
    ClassLoader myLoader = new ClassLoader() {
        @Override
        public Class<?> loadClass(String name) throws ClassNotFoundException {
            try {
                String fileName=name.substring(name.lastIndexOf(".")+1)+".class";
                InputStream is=getClass().getResourceAsStream(fileName);
                if( is == null ){
                    return super.loadClass(name);
                }
                byte[] bytes = new byte[is.available()];
                is.read(bytes); //通过自定义类加载器读取class文件的二进制流
                return defineClass(name, bytes, 0,bytes.length);
            } catch (IOException e) {
                e.printStackTrace();
                throw new ClassNotFoundException(name);
            }
        }
    };
    //比较类是否相同
    Object obj = myLoader.loadClass("com.chenshuyi.UniqueClass").newInstance();
    System.out.println(obj.getClass());
    System.out.println(UniqueClass.class);
    System.out.println(obj instanceof UniqueClass);
    

    在上面这段代码中,我首先定义了一个自定义类加载器 myLoader,之后让其去加载com.chenshuyi.UniqueClass类,之后调用newInstance()获得该类的实例 obj。

    接着分别打印输出 obj 对象的类路径,以及 UniqueClass 类的类路径,最后使用 instanceof 符号判断 obj 对象是否是 UniqueClass 类的实例。最后的输出结果是:

    class com.chenshuyi.UniqueClass
    class com.chenshuyi.UniqueClass
    false
    

    上面的结果显示:obj 对象和 UniqueClass 类的类路径完全相同,都是com.chenshuyi.UniqueClass。但是 obj 对象却不是 UniqueClass 类的实例。这就验证了我的说法,即:类加载器 + 类路径才唯一确定一个 Java 类。

    其实在 Java 语言中,还有一个与之非常类似的情况:如何唯一确定类中的一个方法?按照我们一直以来的直觉,我们会回答:方法名、形参类型、形参个数。例如下面的两个方法虽然方法名相同,但是参数类型和个数不同,所以他们是不同的方法。

    public void Hello(String name)
    public void Hello(String name, int age)
    

    但下面两个方法虽然返回类型不同,但他们的方法名和参数类型是一致的,所以他们无法通过编译。

    public void Hello(String name)
    public String Hello(String name)
    

    但是其实对于 JVM 来说,在同一个类中是可以存在方法名相同并且参数类型相同的方法名的。也就是说,在JVM 中判断一个方法的要素是:类名、方法名以及方法描述符。与 Java 源码中的不同在于方法描述符这个概念。方法描述符由方法的参数类型和返回类型所构成。例如下面的这个方法,方法描述符就是 name 这个参数,以及 String 这个返回类型。

    public String Hello(String name)
    

    为了证明我上面的观点,我们再做一个简单的实验。

    下面的代码声明了一个方法 a 和 方法 b,方法名不同,返回类型不同。

    public class UniqueMethod {
        public void a(){}
        public String b(){
            return "b";
        }
        public static void main(String[] args) {
            System.out.println("Hello");
        }
    }
    

    为了证明在 JVM 对于方法唯一性判断,我将通过修改字节码的方式,让 UniqueMethod 字节码变成下面这样。即有两个相同的 a 方法,它们的方法名、形参类型、形参个数都相同,但是返回参数类型不同。

    public class UniqueMethod {
        public void a(){}
        public String a(){
            return "b";
        }
        public static void main(String[] args) {
            System.out.println("Hello");
        }
    }
    

    那么实验开始了!

    首先我们用 javac 命令编译出字节码 class 文件,接着使用 asmtools 工具将 class 文件再转为 jasm 文件。我们打开 jasm 文件看看:

    可以看到里面有三个方法,分别是 a 方法、b 方法和 main 方法。此时我们将 b 方法名称直接修改成 a 方法,接着使用 asmtools 工具将 jasm 文件转为 class 文件。通过这种方式,我们就可以在一个类中拥有两个名为 a 的方法了。这两个 a 方法,它们的方法名、形参类型、形参个数都相同,但是返回参数类型不同。

    生成修改后的 class 文件之后,我们运行 java UniqueMethod命令,顺利打印出字符:Hello。这说明 class 文件并没有任何错误,JVM 对于方法名、形参类型、形参个数都相同,但是返回参数类型不同的方法,是完全接受的。

    让我们再用 javap 命令来看看 class 文件的字节码结构,我们会发现确实是存在了两个名称为 a 的方法的。

    最后让我们来总结一下:在 JVM 中,类路径和类加载器唯一确定一个 Java 类,方法名、形参类型、形参个数、返回参数类型唯一确定一个 Java 类中的方法。

    其实不仅仅是类与方法的唯一性,在很多方面 JVM 和 Java 语言规范真是有很大的差别。很多在 Java 中成立的东西,到了 JVM 其实就不一定成立了。例如:Java 的泛型、Java 的 lambla 表达式等等,其实只在 Java 语言层面存在,而在 JVM 中其实是不存在的。

    好了,今天的分享到此为止了。喜欢这篇文章的话,那就分享给朋友一起看吧!

  • 相关阅读:
    《构建之法》第8、9、10章的读后感和第一个sprint总结
    实验三 进程调度模拟程序
    构建之法第6、7章的读后感
    实验二 作业调度模拟程序
    0415博客园评价
    0414复利计算6.0-----结对编程
    0408~送给小伙伴的汉堡包
    学习进度条
    Sprint three
    sprint one
  • 原文地址:https://www.cnblogs.com/chanshuyi/p/how_to_confirm_an_unique_class.html
Copyright © 2020-2023  润新知