比如有类如下:
class NotifySettings
{
public NotifySettings()
{
notifyParams = new NotifyParams();
}
public String notifierClassName;
NotifyParams notifyParams;
}
class NotifyParams
{
public NotifyParams()
{
}
public String address;
private int port;
protected boolean enabled;
}
现有NotifySettings类的对象settings,若想访问 settings.notifyParams.port,由于port是私有成员,所以无法用直接引用port的方式来访问port成员。利用java的反射机制就可以做到这一点。下面的程序可以用类似文件系统路径的方式指定访问类中的成员,例如,用“notifyParams\port”指定访问settings对象的notifyParams成员的port成员。
import java.lang.reflect.Field;
public class LearnRefelct
{
public LearnRefelct()
{
}
/**
* 从指定的对象中获取由path指定的成员的值。
*
* @param obj 需要获取成员值的对象
* @param path 指定成员在对象中的路径。例如需要获取对象中a成员的b成员的c成员的值,则path应该设置为"a\b\c"
* @return 指定成员的值。若取值失败或成员值为空,则返回null。
*/
public Object getValue(Object obj, String path)
{
Object value;
Field field = getField(obj, path, false); // 获取由path指定的字段对象
if (field != null)
{
Field ownerField = getField(obj, path, true); // 获取path指定的字段的上级字段
try
{
Object ownerObject;
if (ownerField == null) // 若无上级字段,则该字段为顶层字段,因此上级对象即是obj
{
ownerObject = obj;
}
else
{
ownerObject = ownerField.get(obj);
}
field.setAccessible(true); // 用于获取私有成员的访问权
value = field.get(ownerObject);
} catch (IllegalArgumentException | IllegalAccessException e)
{
value = null;
e.printStackTrace();
}
}
else
{
value = null;
}
return value;
}
/**
* 设置对象的由path指定的成员的值
*
* @param obj 待设定值的对象
* @param path 成员的路径字符串。例如需要获取对象中a成员的b成员的c成员的值,则path应该设置为"a\b\c"
* @param value 成员的设定值
* @return 若设定成功,则返回真;否则返回假。
*/
public boolean setValue(Object obj, String path, Object value)
{
boolean success;
Field field = getField(obj, path, false); // 获取由path指定的字段对象
if (field != null)
{
Field ownerField = getField(obj, path, true); // 获取path指定的字段的上级字段
try
{
Object ownerObject;
if (ownerField == null) // 若无上级字段,则该字段为顶层字段,因此上级对象即是obj
{
ownerObject = obj;
}
else
{
ownerObject = ownerField.get(obj);
}
field.setAccessible(true); // 用于获取私有成员的访问权
field.set(ownerObject, value); // 设置字段的值
success = true;
} catch (IllegalArgumentException | IllegalAccessException e)
{
success = false;
e.printStackTrace();
}
}
else
{
success = false;
}
return success;
}
/**
* 获取类的指定的成员对象或其上级对象的字段定义。
*
* @param obj 待获取成员的类对象
* @param path 成员在类中的路径
* @param getOwner 是否获取path指定的对象的上级对象
* @return 指定的对象成员的定义
*/
Field getField(Object obj, String path, boolean getOwner)
{
String[] fieldNames = path.split(splitter); // 分割字段
int deep = fieldNames.length;
if (getOwner)
{
deep --;
}
Field field = null;
Object subObj = obj;
for(int index = 0; index < deep; index ++)
{
try
{
if (subObj != null)
{
field = subObj.getClass().getDeclaredField(fieldNames[index]);
field.setAccessible(true);
subObj = field.get(subObj);
}
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e)
{
field = null;
e.printStackTrace();
}
}
return field;
}
/**
* 获得指定对象的成员定义
*
* @param obj 待获取成员的类对象
* @param path 成员在类中的路径
* @return 指定的对象成员的定义
*/
Field getField(Object obj, String path)
{
Field field = getField(obj, path, false);
return field;
}
/**
* 获取指定对象的上级对象的定义
*
* @param obj 待获取成员的类对象
* @param path 成员在类中的路径
* @return 指定的对象成员的上级成员对象的定义
*/
Field getOwnerField(Object obj, String path)
{
Field field = getField(obj, path, true);
return field;
}
public void run()
{
NotifySettings settings = new NotifySettings();
setValue(settings, "notifierClassName", "org.opensource.loader");
String className = (String)getValue(settings, "notifierClassName");
System.out.println("class: " + className);
setValue(settings, "notifyParams\\address", "192.168.0.1");
String address = (String)getValue(settings, "notifyParams\\address");
System.out.println("address: " + address);
setValue(settings, "notifyParams\\port", 8080);
int port = ((Integer)getValue(settings, "notifyParams\\port")).intValue();
System.out.println("port: " + port);
setValue(settings, "notifyParams\\enabled", true);
boolean enabled = ((Boolean)getValue(settings, "notifyParams\\enabled")).booleanValue();
System.out.println("enabled: " + enabled);
}
public static void main(String[] args)
{
LearnRefelct lr = new LearnRefelct();
lr.run();
}
private static String splitter = "\\\\";
}
上面的代码中,splitter的值可以任意指定,例如可以设定成“:”,那么访问方式为notifyParams:port;若splitter为“.”,则访问方式为"notifyParams.port"。