您的位置:首页 > 编程语言 > Java开发

Java反射 二三事

2015-10-10 10:03 399 查看

为什么需要反射

关于反射有很多场景可能会用到,这里用到是因为有一些私有方法必须要写单元测试。
关于为什么会对私有方法进行单元测试,也许这并不是常见的需求。然而当外部环境变化很大(比如网站的url)时,保证你的函数正确有助于快速排错。

对私有方法反射的主要流程

1.首先获得方法A所在类的Class object,即通过Class.forName(ClassName)以及 object.newInstance()获得。

注意这里的ClassName包含类所在的包名。

2.利用刚才得到的Class object调用getDeclaredMethod(String name, Class<?>... parameterTypes)方法。前一个参数是nonpublic方法A的名字,后面的参数是包含方法A的各个参数的类型的Class Object。这样就可以得到方法A的object,原文描述为:Returns a Method object that reflects the specified declared method of the class or interface
represented by this Class object。

Method A’= getDeclaredMethod(“A”,Class<?>... parameterTypesOf”A”) 也可以保留方法A原名,这里为了区分命名为A’。

3.设置方法A’的可见性A’.setAccessible(true);

4.调用A’方法,注意一定要invoke()====>A’.invoke(A所在类的Classobject, A的参数)。

对私有字段反射的主要流程

1.和私有方法的反射类似

2.利用刚才得到的Class object调用getDeclaredField(String fieldName)获取字段对象fieldObj。

3.设置字段可见性fieldObj.setAccessible(true);

4.设置字段fieldObj.set(object, value)

获取字段的值fieldObj.get(object)

一个基本和典型的私有方法反射示例

(1)写个类
package xxx.yyy.zzz
public class Simple {
private void displayMessage(String strMsg) {
System.out.println("message is:" + strMsg);
}
}


(2)反射调用
import java.lang.reflect.*;
public class Test {
public static void main(String[] args) throws ClassNotFoundException,
SecurityException, NoSuchMethodException, InstantiationException,
IllegalAccessException, IllegalArgumentException,
InvocationTargetException {

Class simpleClass = Class.forName("xxx.yyy.zzz.Simple");
Object simpelObject = simpleClass.newInstance();
Class[] args1 = new Class[1];
args1[0] = String.class;

Method simpleMethod = simpleClass.getDeclaredMethod("displayMessage",arg1);

simpleMethod.setAccessible(true);

String[] argments = new String[1];
argments[0] = "Hello,world";
simpleMethod.invoke(simpelObject, argments);

}
}


复杂参数类型的反射示例

这里才是重点!
通常invoke返回值是Object对象,通过对它强制类型转换可以得到复杂类型的返回值,比如数组,集合等等(当然前提是你强制转换的类型和你的函数定义一致)。
先放一个数组的例子,假设有如下的类:
package org.apache.nutch.parse.html;

public class TestOnly {
private String[] array= new String[4];

private String[] getStringArray(){
return array;
}
private void setStringArray(String[] values){
array=values;
}

}


它的array数据和对应的set/get方法都是私有的
package org.apache.nutch.parse.html;

import org.junit.Test;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;

import static org.junit.Assert.*;

public class TestOnlyTest {
@Test
public void testGetStringArray() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException,
IllegalAccessException, InstantiationException, MalformedURLException, NoSuchFieldException {
Class testObj = Class.forName("org.apache.nutch.parse.html.TestOnly");
Object obj = testObj.newInstance();
/*test set method.*/
Method setArray = testObj.getDeclaredMethod("setStringArray", new Class[]{String[].class});
setArray.setAccessible(true);

String[] name = {"hello", "world"};
setArray.invoke(obj, new Object[]{name});

/*test get method.*/
Method getArray = testObj.getDeclaredMethod("getStringArray");
getArray.setAccessible(true);
String[] result = (String[])getArray.invoke(obj);

assertEquals("hello",result[0]);
assertEquals("world",result[1]);

/*test private field visit.*/
Field array =testObj.getDeclaredField("array");
array.setAccessible(true);
array.set(obj, new String[]{"come","here","baby"});
result = (String[])array.get(obj);
assertEquals("come",result[0]);
assertEquals("here",result[1]);
assertEquals("baby",result[2]);
}
}
当然不用单元测试,也可以用通过打印语句来验证结果集的正确性。

再放一个工作中实际用到的小例子。
自定义nutch的解析类 实现Parser接口定义的抽象方法如下:
@Override
public ArrayList<Outlink> filterOutlinks(ArrayList<Outlink> list) {
ArrayList<Outlink> filterList = new ArrayList<Outlink>();
Outlink filterLink = null;
for (Outlink outlink : list) {
if (isCrawlOutLineUrl(outlink.getToUrl())) {
try {
filterLink = new Outlink(outlink.getToUrl(), "ydss");
} catch (MalformedURLException e) {
dMsg.warn("MalformedURLException:", e);
continue;
}
if (!filterList.contains(filterLink)) {
filterList.add(filterLink);
}
}
}
return filterList;
}


其中isCrawlOutLineUrl是一个判断是否为合法出链的私有方法,具体逻辑忽略,此外用到了一个私有字段url。
对应的单元测试如下:
//    @Test
public void testIsCrawlOutLineUrl() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException,
IllegalAccessException, InstantiationException, NoSuchFieldException{
String outLinkUrl = "http://bbs.ydss.cn/thread-199593-1-1.html";

Class ypObj = Class.forName("org.apache.nutch.parse.html.YdssParser");
Object yp = ypObj.newInstance();
Method isCrawlOutLineUrl = ypObj.getDeclaredMethod("isCrawlOutLineUrl", new Class[]{String.class});
isCrawlOutLineUrl.setAccessible(true);

Field url = ypObj.getDeclaredField("url");
url.setAccessible(true);
url.set(yp, "http://bbs.ydss.cn/forum.php?mod=viewthread&tid=505896&extra=page%3D1%26filter%3Dauthor%26orderby%3Ddateline");
assertFalse((Boolean) isCrawlOutLineUrl.invoke(yp, outLinkUrl));
outLinkUrl = "http://bbs.ydss.cn/thread-505896-1-1.html";
assertFalse((Boolean) isCrawlOutLineUrl.invoke(yp, outLinkUrl));
}


以上是最近写私有方法和字段的单元测试所获的一些心得,分享,备忘。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: