一: Class 类的使用
1: Class 类介绍
在 Java 中除了基本数据类型和静态的东西都是对象, 都是 Java.lang.class 的对象,
在上面的 Code 我们创建了一个 Food 类, 实例化了一个 food 的对象, 其实 Food 这个类也是一个对象, 是 Java.lang.class 的对象 . 我们知道, 通过 new A(A 任意一个类)来得到一个 A 类的对象, 同样任意一个类都是 class 的对象, 我们也可以获取到 Class 的对象
获方式如下:
- Class c1 = Food.class()
- Class c2 = food.getClass();
- Classc3 = Class.forName("com.xxx.xx");
2: 动态加载类
程序都分为编译期和运行期 2 个时期.
编译期: 是指编译器将源代码翻译为机器能识别的代码, java 为编译为 jvm 认识的字节码文件.
运行期: 则是指 java 代码的运行过程.
在我们的 FactoryFood 中, 我们需要根据实际情况引很多个类, 而且我们需要编译期编译很多类, 但实际可能只会用到 1,2 个类.
在我们的 DynamicFactory 中, 我们没有引入任何实体类, 而是由程序动态的加载和创建对象. 需要什么类就传入什么类的包. 类名即可
总结: 通过动态加载类的方式, 我们可以随意的添加和删除类而不需要重新编译. 然而静态类却不行, 每次新增类都需要重新编译程序.
二: 获取方法信息
下面让我们通过反射的方式来获取类的名称, 方法, 变量, 函数的参数, 返回值
getMethods(): 获取所有的 public 方法, 包括从父类继承过来的, 如上面的 wait 方法(在 java 中我们新建的类默认继承 Object, 所以 Food 类里面就获取到了 warit 方法)
getDeclareMethods(): 获取类自己申明的方法包括 (public, private, protect) 通过上面的输出我们可以看出来, 使用 getDeclareMethods 获取到了 updateFood(private)但是没有获取到 wait(从父类继承的)
三: 获取成员变量
前面我们介绍了方法的获取方法, 那成员变量依然可以获取, 而且成员变量也是对象是 Java.lang.Field 的对象,
我们先在类中添加一个 public Character character 的成员变量
下面演示获取成员变量信息的例子
getFields()只能获取 public 的成员变量, 在输出信息中, 我们可以看到只输出了一个 character 的成员变量
getDeclareField()可以获取该类所有自己声明的成员变量包括 public 的, private 的和 protected, 以及 default 类型的
四: 获取构造函数信息
类是对象, 成员变量是对象, 同样构造函数也是对象, 是 Java.lang.Constructor 的对象.
下面让我们来获取构造函数的参数列表和参数类型
getConstructors(): 获取所有的 public 的构造函数(也有 private 的构造函数, 比如: 单例模式中就存在)
getDeclareConstructors(): 获取所有自己申明的构造函数(构造函数只能是自己申明的)
同样我们也可以通过 getInterface, getSuperClass 获取接口和父类
注: 只要我们得到类的类型, 我们就可以得到该类的所有信息
五: 方法的反射
前面讲述了一大堆获取方法, 变量, 参数, 返回值的操作, 接下来就来实际操作通过反射来调用我们的方法, 指令 method.invoke();
首先, 我们要调用方法就要先获取方法信息, 要获取方法信息就要获取类信息, 就是获取类类型
首先我们在 Food 类中添加如下三个方法:
然后我们来反射调用这三个方法:
在上面的 code 中, 我们先调用 setDefaultName 来给 Food 命名, 然后在调用 getFoodName 来检查我们是否成功的通过反射完成了设值, 然后我们再调用有参数的方法 setFoodNumber, 来重新设置 name 和 number 然后再调用 getFoodName 来检查我们是否成功的通过反射完成了设值
从输出的信息, 我么可以得知我们成功的通过反射来完成了 Food 方法的调用, 但是我们发现, 第一次的输出是 null, 因为我们的第一次调用的函数是无返回值的.
注: 当有返回值, Object 为具体的返回值, 无返回值时, Object 为 null 函数有参数时, 调用时传入具体的参数, 多个参数, 传入一个 Object 数组; 无参数调用时不传参数
深入理解:
反射是编译之后的操作, 前面的文章我们讲泛型, 说泛型是编译期的安全检查, 那我们就可以通过反射来给泛型插入数据而不出现编译错误:
通过上面的输出结果我们可以发现我们定义了 String 的 ArrayList, 添加了一个 "First", 这时候 size 是 1, 然后又通过反射来添加一个 22 到 ArrayList 中, 输出的 Size 是 2. 所以通过反射可以绕过泛型的检查(因为编译器在编译期会做泛型擦除).
来源: http://www.jianshu.com/p/603484feee2a