zoukankan      html  css  js  c++  java
  • 如何唯一确定一个 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 中其实是不存在的。

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

  • 相关阅读:
    有点忙啊
    什么是协程
    HDU 1110 Equipment Box (判断一个大矩形里面能不能放小矩形)
    HDU 1155 Bungee Jumping(物理题,动能公式,弹性势能公式,重力势能公式)
    HDU 1210 Eddy's 洗牌问题(找规律,数学)
    HDU1214 圆桌会议(找规律,数学)
    HDU1215 七夕节(模拟 数学)
    HDU 1216 Assistance Required(暴力打表)
    HDU 1220 Cube(数学,找规律)
    HDU 1221 Rectangle and Circle(判断圆和矩形是不是相交)
  • 原文地址:https://www.cnblogs.com/chanshuyi/p/how_to_confirm_an_unique_class.html
Copyright © 2011-2022 走看看