Java反射学习笔记(一)

Class类的使用

在面向对象的世界中,万事万物皆对象。类也是对象,它是java.lang.Class类的实例对象。这个实例对象称为类的类类型(Class Type)。
现在,有一个类Foo,如何获取它的类类型(Class Type)?有以下三种方式:

  • 直接通过Foo类获取
    1
    Class c1 = Foo.class;
  • 通过Foo类的实例对象获取
    1
    2
    Foo 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
2
3
4
5
6
7
8
9
10
11
12
13
Class Office{
public static main(String args[]){
//new对象 静态加载类
if("word".equals(args[0]){
Word w = new Word();
w.start();
}
if("excel".equals(args[0]){
Excel e = new Excel();
e.start();
}
}
}

看上去没毛病。然而,当你使用javac进行编译时,会报错:Word类和Excel类找不到。好吧,我们来创建Word类:

1
2
3
4
5
6
Class Word(){
public Word();
public void start(){
//启动Word
}
}

再次编译的时候,就只会报Excel类找不到的错误。你会想,再加上Excel类不就行了吗?可问题是,如果Excel模块出了问题,整个程序都编译不通过,自然Word也不能用,而你只想用Word,并不care Excel有啥问题的时候,你还会觉得这样的程序设计合理吗?如果这个程序里有100个功能,只要一个出了问题,那么剩下的99个功能都用不了,体验会非常差。

动态加载类的实践

如何改进?
首先,我们定义一个接口:

1
2
3
public interface OfficeAble{
void start();
}

并让Word类实现这个接口。
改进后的OfficeBetter类:

1
2
3
4
5
6
7
8
9
10
11
public OfficeBetter{
public static void main(String args[]){
try{
Class c = Class.forName(args[0]);
OfficeAble oa = (OfficeAble)c.newInstance();
oa.start();
}catch(Exception ex){
ex.printStackTrace();
}
}
}

编译:

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 协议进行许可,使用时请注意遵守协议。