1 概述
白盒测试时,工具类最容易对付,直接调用类方法即可;public修饰的实例方法也还行,先实例化后调用;但是遇到private修饰的方法就头大了,虽然可以在类里写个main方法调用,但破坏了代码本身的完整性,功能代码与测试代码混在一起
不过,反射让我们彻底告别这个问题
2 基本成员
由class展开,类有构造函数(Constructor),类有成员变量(Field),类还有方法(Method)
然后使用public、private、protected、static、final等来修饰
当然还有注解,参数,返回值等,并且有相应的类型
所有这些,就像零件一样,组装成一个完整的class文件
反射正是将整个class拆散,来获取一个个零部件
3 简单API
测试样本
public class App {
private static int COUNT = 0;
public final int ONE = 1;
public App() {
COUNT++;
}
public int inc(int... nums) {
return COUNT + ONE * nums.length;
}
@Deprecated
public static int count() {
return COUNT;
}
}
3-1 Constructor
public class AppTest {
static void log(String desc, Object ret) {
System.out.println(String.format("%-20s : %s", desc, ret));
}
public static void main(String[] args) throws Exception {
Class<App> clz = App.class;
for (Constructor constructor : clz.getConstructors()) {
log("getName", constructor.getName());
log("getDeclaringClass", constructor.getDeclaringClass());
log("getModifiers", constructor.getModifiers());
log("getParameterCount", constructor.getParameterCount());
log("getParameterTypes", Arrays.toString(constructor.getParameterTypes()));
}
App app = clz.getConstructor(null).newInstance(null);
log("app.inc()", app.inc());
}
}
--------------
getName : App
getDeclaringClass : class App
getModifiers : 1
getParameterCount : 0
getParameterTypes : []
app.inc() : 1
3-2 Method
public class AppTest {
static void log(String desc, Object ret) {
System.out.println(String.format("%-20s : %s", desc, ret));
}
public static void main(String[] args) throws Exception {
Class<App> clz = App.class;
for(Method method: clz.getDeclaredMethods()) {
System.out.println(method.getName());
log("\tgetModifiers", Modifier.toString(method.getModifiers()));
log("\tgetParameterCount", method.getParameterCount());
log("\tgetParameterTypes", Arrays.toString(method.getParameterTypes()));
log("\tgetAnnotations", Arrays.toString(method.getAnnotations()));
log("\tisVarArgs", method.isVarArgs());
}
Method m = clz.getDeclaredMethod("inc", int[].class);
int[] param = {1, 2, 3};
int result = (int)m.invoke(new App(), new Object[]{param});
log("invoke inc", result);
}
}public class AppTest {
static void log(String desc, Object ret) {
System.out.println(String.format("%-20s : %s", desc, ret));
}
public static void main(String[] args) throws Exception {
Class<App> clz = App.class;
for(Method method: clz.getDeclaredMethods()) {
System.out.println(method.getName());
log("\tgetModifiers", Modifier.toString(method.getModifiers()));
log("\tgetParameterCount", method.getParameterCount());
log("\tgetParameterTypes", Arrays.toString(method.getParameterTypes()));
log("\tgetAnnotations", Arrays.toString(method.getAnnotations()));
log("\tisVarArgs", method.isVarArgs());
}
Method m = clz.getDeclaredMethod("inc", int[].class);
int[] param = {1, 2, 3};
int result = (int)m.invoke(new App(), new Object[]{param});
log("invoke inc", result);
}
}
-----------
count
getModifiers : public static
getParameterCount : 0
getParameterTypes : []
getAnnotations : [@java.lang.Deprecated()]
isVarArgs : false
inc
getModifiers : public transient
getParameterCount : 1
getParameterTypes : [class [I]
getAnnotations : []
isVarArgs : true
invoke inc : 4
3-3 Field
public class AppTest {
static void log(String desc, Object ret) {
System.out.println(String.format("%-20s : %s", desc, ret));
}
public static void main(String[] args) throws Exception {
App app = new App();
Class<App> clz = App.class;
Field field = clz.getDeclaredField("ONE");
log("getInt", field.getInt(app));
log("getModifiers", Modifier.toString(field.getModifiers()));
}
}
----------------
getInt : 1
getModifiers : public final
4 访问私有方法
-
调用getDeclaredMethod获取方法,
-
再使用setAccessible(true)使方法可访问
public class App {
private void privateMethod() {
System.out.println("private method");
}
}
public class AppTest {
public static void main(String[] args) throws Exception {
App app = new App();
Class<App> clz = App.class;
Method method = clz.getDeclaredMethod("privateMethod");
method.setAccessible(true);
method.invoke(app);
}
}
5 变参方法
- 当变参类型为int时,invoke传入int[]可行
- 当变参类型为String时,传入String[]却不行,需要用new Object[] {}包装
public class App {
public void sum(int... nums) {
int total = Arrays.stream(nums).sum();
System.out.println(String.format("sum(%s) = %s", Arrays.toString(nums), total));
}
public void log(String... hobby) {
System.out.println(String.format("Jack like %s", Arrays.toString(hobby)));
}
public static void main(String[] args) throws Exception {
App app = new App();
Class<App> clz = App.class;
for(Method method: clz.getDeclaredMethods()) {
if (method.getName().equals("sum")) {
int[] nums = {1, 2, 3, 4, 5};
// method.invoke(app, 1, 2, 3, 4, 5); //error
method.invoke(app, nums);
} else if (method.getName().equals("log")) {
String[] hobby = new String[] {"coding", "game", "girl"};
// method.invoke(app, hobby); //error
method.invoke(app, new Object[]{hobby});
}
}
}
}
网友评论