Class类的使用
在面向对象的世界中,万事万物皆对象。类也是对象,它是java.lang.Class类的实例对象。这个实例对象称为类的类类型(Class Type)。
现在,有一个类Foo,如何获取它的类类型(Class Type)?有以下三种方式:
- 直接通过Foo类获取
1
Class c1 = Foo.class;
- 通过Foo类的实例对象获取
1
2Foo foo = new Foo();
Class c2 = foo.getClass(); - 通过静态方法Class.forName()获取
1
Class c3 = Class.forName(Foo);
一个类只可能是Class类的一个实例对象,c1,c2,c3是相等的,可输出验证。
- 此外,我们还可以通过类的类类型(Class Type)创建该类的实例对象:
1
Foo foo = (Foo)c1.newInstance();
动态加载类
- 区分动态加载和静态加载
编译时加载类是静态加载类,运行时加载类是动态加载类。
静态加载类的实践
假设我们要写个程序,实现输入”word”时启动Word,输入”excel”时启动Excel。常规的思路如下:
1 | Class Office{ |
看上去没毛病。然而,当你使用javac进行编译时,会报错:Word类和Excel类找不到。好吧,我们来创建Word类:
1 | Class Word(){ |
再次编译的时候,就只会报Excel类找不到的错误。你会想,再加上Excel类不就行了吗?可问题是,如果Excel模块出了问题,整个程序都编译不通过,自然Word也不能用,而你只想用Word,并不care Excel有啥问题的时候,你还会觉得这样的程序设计合理吗?如果这个程序里有100个功能,只要一个出了问题,那么剩下的99个功能都用不了,体验会非常差。
动态加载类的实践
如何改进?
首先,我们定义一个接口:
1 | public interface OfficeAble{ |
并让Word类实现这个接口。
改进后的OfficeBetter类:
1 | public OfficeBetter{ |
编译:
1 | javac OfficeBetter |
编译通过,不会报错。
运行:
1 | java OfficeBetter Word |
也不会报错。
注意,此时我们仍然没有Excel这个类。如果你试着带上参数“Excel”去运行OfficeBetter,它才会报错。这意味着,如果你项目中的Excel模块出了问题,你仍能正常编译运行,并使用Word模块。使用这种设计的另一个意义是,如果你项目里写Word和Excel这两个模块的程序员跑路了,新来的也不用改原来的类和接口,只需要加上自己的类,实现原来的接口就行。此外,版本升级的时候也不用重新编译。而这种设计的实现正依赖于类的动态加载。
Author: kpt
Permalink: http://kpt.ink/2019/11/21/java-reflection/
文章默认使用 CC BY-NC-SA 4.0 协议进行许可,使用时请注意遵守协议。