概述
“传统的” RTTI 机制在编译时就已经知道了所有类的类型;“反射”机制允许在运行时发现和使用类的信息。依靠反射机制,可以动态的创建一个类的对象和调用对象的方法。
反射的优点是增加灵活性,可以在运行时动态获取对象实例。缺点是反射的效率很低,而且会破坏封装,通过反射可以访问类的私有方法,不安全。
反射是无孔不入的,无论是私有方法还是私有内部类的方法,哪怕是匿名类的方法,也无法逃脱反射的调用。对于私有域来说也一样,只有 final 域,才不会被修改。反射可以说是给我们的程序留了一道后门,但是总的来说,从反射给我们带来的优劣对比上看,利大于弊。
Class 类和 java.lang.reflect 类库共同构成了对反射的支持,该类库主要包含了 Field, Method 以及 Constructor 类。
与Java反射相关的类如下:
类名 | 用途 |
---|---|
Class类 | 代表类的实体,在运行的Java应用程序中表示类和接口 |
Field类 | 代表类的成员变量(成员变量也称为类的属性) |
Method类 | 代表类的方法 |
Constructor类 | 代表类的构造方法 |
Class 方法中有大量的方法可供调用,包括 getName()(获取类的完整路径名字)、newInstance()(创建类的实例)、getFields()(获取所有公有的属性对象)、getDeclaredMethods()(获取该类所有方法)等;
Field 类代表类的成员变量,包括 get(Object obj) (获得 obj 中对应的属性值)、set(Object obj, Object value) (设置 obj 中对应属性)等方法。
Method代表类的方法,invoke(Object obj, Object… args) (传递 object 对象及参数调用该对象对应的方法)方法。
Constructor代表类的构造方法,其中 newInstance(Object… initargs) 函数用于根据传递的参数创建类的对象。
应用
测试不同排序方法的时间消耗,如下所示:
package Algorithm.Sort;
import java.lang.reflect.Method;
import java.util.*;
public class TimeCostForSortTest {
// 待排序数组长度
private static final int N = 100000;
// 生成随机数的最大值
private static final int randValue = 10000;
// 排序方法个数
private static final int numOfSortAl = 7;
// 各种排序的方法名
public static final String[] sort = {"selectSort", "bubbleSort", "insertSort", "shellSort",
"mergeSort", "quickSort", "heapSort"};
// main 函数
public static void main(String[] args) {
TimeCostForSortTest t = new TimeCostForSortTest();
// 生成待排序数组
List<int[]> list = t.initialArrayList();
for (int i = 0; i < numOfSortAl; i++) {
String al = sort[i];
int[] nums = list.get(i);
long startTime = System.currentTimeMillis();
try {
// 调用某个排序方法
t.callForSortByName(al, nums);
} catch (Exception e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
// 打印出排序所用的时间
System.out.println(al + ": " + (endTime - startTime) + "ms");
}
}
/**
* 调用指定的方法
* @param al 方法名
* @param nums 参数
* @throws Exception 异常
*/
public void callForSortByName(String al, int[] nums) throws Exception {
if (al == null || al.length() == 0)
throw new NumberFormatException();
// 使用装载当前类的类装载器来装载指定的类
Class<?> alClass = Class.forName("Algorithm.Sort.TimeCostForSortTest");
// 获得指定的对象中的方法
Method method = alClass.getMethod(al, int[].class);
// 调用 this 对象的 method 方法
method.invoke(this, new Object[]{nums});
}
/**
* 生成待排序数组,数字随机
* 列表中每一个待排序数组完全相同
* @return 生成的数组列表
*/
private List<int[]> initialArrayList() {
int[] nums = new int[N];
Random rand = new Random();
for (int j = 0; j < N; j++)
nums[j] = rand.nextInt(randValue) + 1;
List<int[]> list = new ArrayList<>();
list.add(nums);
for (int i = 1; i < numOfSortAl; i++) {
list.add(Arrays.copyOf(nums, nums.length));
}
return list;
}
/**
* 选择排序
* @param nums 待排序数组
*/
public void selectSort(int[] nums) {
int n = nums.length;
for (int i = 0; i < n - 1; i++) {
int index = i;
for (int j = index; j < n; j++) {
if (nums[j] < nums[index])
index = j;
}
swap(nums, i, index);
}
}
/**
* 冒泡排序
* @param nums 待排序数组
*/
public void bubbleSort(int[] nums) {
int n = nums.length;
for (int i = n; i > 0; i--) {
for (int j = 0; j + 1 < i; j++) {
if (nums[j] > nums[j + 1])
swap(nums, j, j + 1);
}
}
}
/**
* 插入排序
* @param nums 待排序数组
*/
public void insertSort(int[] nums) {
int n = nums.length;
for (int i = 1; i < n; i++) {
for (int j = i; j > 0 && nums[j - 1] > nums[j]; j--) {
swap(nums, j - 1, j);
}
}
}
/**
* shell 排序
* @param nums 待排序数组
*/
public void shellSort(int[] nums) {
if (nums == null || nums.length <= 1) {
return;
}
int incrementNum = nums.length / 2;
while (incrementNum >= 1) {
for (int i = 0; i < incrementNum; i++) {
//冒泡排序
for (int j = i; j < nums.length - incrementNum; j = j + incrementNum) {
if (nums[j] > nums[j + incrementNum]) {
int temple = nums[j];
nums[j] = nums[j + incrementNum];
nums[j + incrementNum] = temple;
}
}
}
incrementNum = incrementNum / 2;
}
}
/**
* 归并排序
* @param nums 待排序数组
*/
public void mergeSort(int[] nums) {
if (nums == null || nums.length == 0)
return;
helper = new int[nums.length];
mergeSortHelp(nums, 0, nums.length - 1);
}
/**
* 快速排序
* @param nums 待排序数组
*/
public void quickSort(int[] nums) {
if (nums == null || nums.length == 0)
return;
quickSortHelp(nums, 0, nums.length - 1);
}
/**
* 堆排序
* @param nums 待排序数组
*/
public void heapSort(int[] nums) {
if (nums == null)
return;
for (int i = nums.length / 2 - 1; i > 0; i--)
adjustHeap(nums, i, nums.length);
for (int i = nums.length - 1; i > 0; i--) {
swap(nums, 0, i);
adjustHeap(nums, 0, i);
}
}
private void swap(int[] nums, int x, int y) {
int tmp = nums[x];
nums[x] = nums[y];
nums[y] = tmp;
}
private int[] helper;
private void mergeSortHelp(int[] nums, int l, int r) {
if (l < r) {
int mid = (l + r) / 2;
mergeSortHelp(nums, l, mid);
mergeSortHelp(nums, mid + 1, r);
merge(nums, l, mid, r);
}
}
private void merge(int[] nums, int l, int mid, int r) {
for (int i = l; i <= r; i++)
helper[i] = nums[i];
int p1 = mid;
int p2 = r;
int index = r;
while (p1 >= l && p2 >= mid + 1) {
if (helper[p1] > helper[p2])
nums[index--] = helper[p1--];
else
nums[index--] = helper[p2--];
}
while (p1 >= l)
nums[index--] = helper[p1--];
while (p2 >= mid + 1)
nums[index--] = helper[p2--];
}
private void quickSortHelp(int[] nums, int l, int r) {
if (l < r) {
int index = partition(nums, l, r);
quickSortHelp(nums, l, index - 1);
quickSortHelp(nums, index + 1, r);
}
}
private int partition(int[] nums, int l, int r) {
int pivot = r;
while (l < r) {
while (l < r && nums[l] < nums[pivot]) l++;
while (l < r && nums[r] >= nums[pivot]) r--;
swap(nums, l, r);
}
swap(nums, l, pivot);
return l;
}
private void adjustHeap(int[] nums, int i, int length) {
int temp = nums[i];
for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {
if (k + 1 < length && nums[k] < nums[k + 1])
k = k + 1;
if (temp < nums[k]) {
nums[i] = nums[k];
i = k;
} else
break;
}
nums[i] = temp;
}
}
用到反射的地方是 callForSortByName 方法中,根据方法名(字符串)调用具体的方法这一部分。
测试结果显示程序正常运行。希尔排序、归并排序、快速排序、堆排序效果较好。
反射在 RPC(远程过程调用)中有广泛的应用。当客户端调用的时候通过动态代理向服务提供方发送调用的信息,服务提供方收到后根据客户端需要调用的方法,调用本地方法,拿到结果组装返回。
参考