(探讨)无包名类加载原理(附赠)反射调用无包名类及其方法
前言
废话:事情的起因是程序员的固有属性懒。
一天我在测试代码的时候新建了Test
类,由于懒我直接建在根目录src
下(如图1)。于是悲剧发生了,无论我怎么做都不能调用Test
类里的方法(如图2)。
俗话说“婶能忍,叔不能忍,三大爷我也忍不了啊”,于是开启了不撞南墙不回头之旅,走投无路之际想到了类加载器,终于从中找到了思路并用反射撞倒了南墙。
类加载器
反射调用无包名类及其方法
// 通过全类名,没有包名就直接是类名,有包名就要加上包名,比如:com.xiaostudy.TLStringCoding
Class clazz = Class.forName("Test");
// 获取构造方法,没有参数就是无参构造,如果要获取有参构造,比如TLStringCoding(String name, Integer age)就用clazz.getConstructor(String.class, Integer.class)
Constructor constructor = clazz.getConstructor();
// 实例化一个对象
Object newInstance = constructor.newInstance();
// 获取一个方法,第一个参数是方法名称,后面的是方法参数类型,没有参数的话可以写null也可以不写
//Method file = clazz.getMethod("encryptFile", String.class, File.class);
Method file = clazz.getMethod("function");
// 使用获取的方法,第一个参数是对象,后面的具体参数,参数类型跟上面一致
//byte[] b = (byte[]) file.invoke(newInstance, key, new File("E:\\工作文件\\自测用例\\Canal.docx"));
file.invoke(newInstance);
Java类加载机制
”加载“是”类加机制”的第一个过程,在加载阶段,虚拟机主要完成三件事:
(1)通过一个类的全限定名
来获取其定义的二进制字节流
(2)将这个字节流所代表的的静态存储结构转化为方法区的运行时数据结构
(3)在堆中生成一个代表这个类的Class对象,作为方法区中这些数据的访问入口。
相对于类加载的其他阶段而言,加载阶段是可控性最强的阶段,因为程序员可以使用系统的类加载器加载,还可以使用自己的类加载器加载。我们在最后一部分会详细介绍这个类加载器。在这里我们只需要知道类加载器的作用就是上面虚拟机需要完成的三件事,仅此而已就好了。
全限定名 = 包名 + 类型
验证加载机制
显然无包名类是不具备全限定名
这个条件的,所以说无包名类并没有被类加载器加载进内存?
事实是否定的。如下图所示,如果类未被加载进内存那么方法是不能被main
执行的。
默认包default
通过查阅资料我发现没有包名类其实是可以被加载的,它们统一被放到了default
默认包下,详见下图中的提示。
通过测试发现,同为默认包下的方法是可以相互调用的,并不需要import
好像也没什么可以导入的。
默认包下类名方法名可以重复
然后我自然想到了不能调用默认包下的类和方法是不是因为可能存在重名现象,因为A.jar
默认包中
可能存在类Test
,引入第三方B.jar
默认包中也可能有Test
方法,而这时编译器并不会报错告诉你有
重名类。
但经过测试发现,加载器会优先选择自己本包下的类和方法,并不会抛出异常,详见下图。
这里JVM执行了本包下的Test
打印的是123
而不是456
。
但是这个地方确实可能存在误解,因为JVM并不确切的知道你想调用哪个Test
里的方法,虽然它并
没报错,但我猜想这可能是禁止调用无包名类的原因之一。
讨论
1.为什么无包名类被加载却无法被有包名类直接调用,难道就是硬性规定?
2.除了反射还有什么方法可以调用无包名类里的方法?
本文地址:https://blog.csdn.net/eden_Liang/article/details/107402223