如何唯一确定一个 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 中其实是不存在的。
好了,今天的分享到此为止了。喜欢这篇文章的话,那就分享给朋友一起看吧!
如何唯一确定一个 Java 类?的更多相关文章
- mac os intellij如何快路查看一个java类的所有方法,结构
如果是自己写的java类,点击点击导航的project-setting-show members 如果是系统库的,点击structure 再点一下lib中的类,或者快捷键 command+F12
- 【Java.Regex】使用正则表达式查找一个Java类中的成员函数
代码: import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; imp ...
- 如何在tomcat启动的时候运行一个Java类
设置个Listener就好了,在web.xml中指定描述. web.xml其实就是tomcat启动的时候会读取的一个描述文件,比如访问服务器的时候首页等都可以在里面指定,有相应的tag.这里有解释:h ...
- jsp其实是一个java类
我们打开tomcat的D:\Program_Files\apache-tomcat-8.0.32\work\Catalina\localhost\venus\org\apache\jsp, 当我们访问 ...
- 请写一个java类,在任何时候都可以向它查询“你已经创建了多少个对象?”
这个问题解决方法很简单,只要设置一个类的静态整型成员(事例中我设置的是n),初始化值为1,然后在其构造函数中添加语句使其+1(n++),这样需要查询创建了多少个对象时直接查询n的值就可以了,如下: p ...
- Java基础-一个java文件多个类的问题
一个.java文件当然可以包括多个类.但这些类有一个特殊的类与其它的不同,,这个类是带public 属性的类.一个.java类文件中仅有一个public属性的类.而且这个类与文件名相同.
- cas sso单点登录系列3_cas-server端配置认证方式实践(数据源+自定义java类认证)
转:http://blog.csdn.net/ae6623/article/details/8851801 本篇将讲解cas-server端的认证方式 1.最简单的认证,用户名和密码一致就登录成功 2 ...
- kettle中调用java类
kettle中调用java类 有时须要在kettle调用java类,如:验证.查询或自己定义加密等.有时甚至连主要的数据訪问都不那么简单,如获取一个存储文件或使用一个数据库连接,某些数据源可能封装在应 ...
- SSO单点登录系列3:cas-server端配置认证方式实践(数据源+自定义java类认证)
落雨 cas 单点登录 本篇将讲解cas-server端的认证方式 1.最简单的认证,用户名和密码一致就登录成功 2.配置Oracle的jdbc数据源,通过spring动态查询数据库 3.配置orac ...
随机推荐
- LeetCode 题解之Add Digits
1.问题描述 2.问题分析 循环拆分数字,然求和判断. 3.代码 int addDigits(int num) { ) return num; int result = num; do{ vector ...
- Cordova-conifg.xml配置
DisallowOverscroll 布尔值,默认false.如果不想要WebView出现橡皮筋滚动条,则设置为true TopActivityIndicator 字符串值,默认gray.设置顶部 ...
- 学习ASP.NET之旅
1.HTML 2.DIV 3.CSS 4.JS脚本 5.javascript 6.J-Query 7.AJAX 8.c# 9.IIS配置 10.ASP.NET怎样添加控件,与winform的控件添加方 ...
- LevelDB源码分析--Cache及Get查找流程
本打算接下来分析version相关的概念,但是在准备的过程中看到了VersionSet的table_cache_这个变量才想起还有这样一个模块尚未分析,经过权衡觉得leveldb的version相对C ...
- nopcommerce
nopcommerce是国外的一个高质量的开源b2c网站系统,基于EntityFramework4.0和MVC3.0,使用Razor模板引擎,有很强的插件机制,包括支付配送功能都是通过插件来实现的,基 ...
- Jmeter用于接口测试中【接口耦合关联的实现】
Jmeter用于接口测试时,后一个接口经常需要用到前一次接口返回的结果,应该如何获取前一次请求的结果值,应用于后一个接口呢,拿一个登录的例子来说明如何获取. 1.打开jmeter, 使用的3.3的版本 ...
- python基础之os.system函数
前言 os.system方法是os模块最基础的方法,其它的方法一般在该方法基础上封装完成. os的system原理 system函数可以将字符串转化成命令在服务器上运行:其原理是每一条system函数 ...
- 数据仓库四个特点(面向主题的(Subject Oriented)、集成的(Integrate)、相对稳定的(Non-Volatile)、反映历史变化(Time Variant))
1.面向主题. 数据仓库中的数据是按照一定的主题域进行组织. 主题是一个抽象的概念,是指用户使用数据仓库进行决策时所关心的重点方面,一个主题通常与多个操作型信息系统相关.而操作型数据库的数据组织面向事 ...
- 在Ubuntu17.04中遇到无法清空回收站解决方法
在Ubuntu17.04下,遇到清空回收站文件时报错,提示”Failed to delete the item from the trash”,无法清空回收站. 回收站其实就是一个文件夹,存放着被删掉 ...
- yaml格式
yaml中允许表示三种格式,分别为常量值.对象和数组 例如: 其中#作为注释,yaml中只有行注释 基本格式要求: 1.大小写敏感:2.使用缩进代表层级关系: 3.缩进只能使用空格,不能使用tab键, ...