今天偶然想起之前和朋友讨论过的一个问题: 如何唯一确定一个 Java 类? 我相信大多数朋友遇到这个问题的回答都是: 类的全路径呗. 但事实上, 唯一确定一个 Java 类, 单单靠类路径是不够的, 还要多加上一个东西: 类加载器. 也就是说, 类加载器 + 类路径才唯一确定一个 Java 类.
为了证明我所说的, 我们来做一个简单的实验.
在上面这段代码中, 我首先定义了一个自定义类加载器 myLoader, 之后让其去加载 com.chenshuyi.UniqueClass 类, 之后调用 newInstance()获得该类的实例 obj.
接着分别打印输出 obj 对象的类路径, 以及 UniqueClass 类的类路径, 最后使用 instanceof 符号判断 obj 对象是否是 UniqueClass 类的实例. 最后的输出结果是:
上面的结果显示: obj 对象和 UniqueClass 类的类路径完全相同, 都是 com.chenshuyi.UniqueClass. 但是 obj 对象却不是 UniqueClass 类的实例. 这就验证
了我的说法, 即: 类加载器 + 类路径才唯一确定一个 Java 类.
其实在 Java 语言中, 还有一个与之非常类似的情况: 如何唯一确定类中的一个方法? 按照我们一直以来的直觉, 我们会回答: 方法名, 形参类型, 形参个数. 例如下面的两个方法虽然方法名相同, 但是参数类型和个数不同, 所以他们是不同的方法.
但下面两个方法虽然返回类型不同, 但他们的方法名和参数类型是一致的, 所以他们无法通过编译.
但是其实对于 JVM 来说, 在同一个类中是可以存在方法名相同并且参数类型相同的方法名的. 也就是说, 在 JVM 中判断一个方法的要素是: 类名, 方法名以及方法描述符. 与 Java 源码中的不同在于方法描述符这个概念. 方法描述符由方法的参数类型和返回类型所构成. 例如下面的这个方法, 方法描述符就是 name 这个参数, 以及 String 这个返回类型.
为了证明我上面的观点, 我们再做一个简单的实验.
下面的代码声明了一个方法 a 和 方法 b, 方法名不同, 返回类型不同.
为了证明在 JVM 对于方法唯一性判断, 我将通过修改字节码的方式, 让 UniqueMethod 字节码变成下面这样. 即有两个相同的 a 方法, 它们的方法名, 形参类型, 形参个数都相同, 但是返回参数类型不同.
那么实验开始了!
首先我们用 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 中其实是不存在的.
为什么某些人会一直比你优秀, 是因为他本身就很优秀还一直在持续努力变得更优秀, 而你是不是还在满足于现状内心在窃喜,
来源: http://www.jianshu.com/p/c09b5e5db390