您的位置:首页 > 其它

使用反射操作类的私有属性(Field)和私有方法(Method)

2017-02-24 13:29 459 查看
刚刚了解了反射,看到了使用反射操作类的私有属性,在操作私有变量时,需要使用Field类,我们来看一个例子:

// field访问私有变量
private void useFeildOfReflect() throws Exception {
//获取类的class对象
Class<?> cls = Class.forName("com.xiao.reflectclass.User");
//获取User类的实例
User user = (User) cls.newInstance();
//使用Field中的方法获取User的私有属性
Field fieldName = cls.getDeclaredField("userName");//Field访问私有变量
fieldName.setAccessible(true);//允许访问私有变量
user.setUserName("yxs");//设置私有变量的值

Field fieldAge = cls.getDeclaredField("age");//Feild访问私有变量
fieldAge.setAccessible(true);
user.setAge(25);

Field fieldSex = cls.getDeclaredField("sex");//Field访问私有变量
fieldSex.setAccessible(true);
user.setSex("男");

LogSystem.print(user.toString() + "\n私有属性操作");

}
User类如下:

public class User implements Serializable{

public User() {
super();
}

public User(String userName, int age, String sex) {
this.userName = userName;
this.age = age;
this.sex = sex;
}

private String userName;

private int age;

private String sex;

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getSex() {
return sex;
}

public void setSex(String sex) {
this.sex = sex;
}

@Override
public String toString() {
return "年龄"+age+"\n"+"姓名"+userName+"\n"+"性别"+sex;
}
}


下面说一下,使用反射调用类的私有方法(Method).

我们先来写一个类,类里面有两个公开方法和一个私有方法.

public class LogSystem {
public static String Tag = "yxs";
public static void print(String msg){
Log.i(Tag,msg);
}

public void cal(int f,int s){
print((f+s)+"结果");
}

private void cal(int f,int s,int t){
print((f*s*t)+"私有结果");
}
}


代码很简单,写好之后,我们开始使用反射:

第一步.我们获取类的class对象:

Class<?> cls = Class.forName("com.xiao.reflectclass.LogSystem");


也可以这样获取:

Class<?> cls = new LogSystem().getClass();


或者这样获取:

Class<?> cls3 =   LogSystem.class;


java提供了三种方法获取类的class对象,上面三种就是,我们通过类的class对象来操作程序.

获取到类的class对象之后,我们通过class对象来获取类的实例:

LogSystem ls = (LogSystem) cls.newInstance();//获得类的实例


我们需要明白在JAVA中任何class都要装载在虚拟机上才能运行,而forName就是装载类用的,这和new不一样,要分清楚哦。

A a = (A)Class.forName(“package.A”).newInstance();
和
A a = new A;两行代码是等价的。


第三步,我们通过cls获取类的共有方法:

//公有方法1
Method method = cls.getMethod("print", String.class);
method.invoke(ls, "hahahahah");


还记得第一步我们写的那个类吗?去看一下吧,我们获取到print方法,print方法有一个参数,这个参数是String类型的,getMethod方法,要求我们传入方法的名称(String类型),和方法需要传入的参数的class对象,我们来看一下源码:

public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
return getMethod(name, parameterTypes, true);
}


第二参数要我们传入参数,允许传入多个参数,但是他抛出了异常,因为我们可能是非法操作类的方法,然后,该方法调用另一个getMethod方法,返回匹配指定的方法对象:

private Method getMethod(String name, Class<?>[] parameterTypes, boolean recursivePublicMethods)
throws NoSuchMethodException {
if (name == null) {
throw new NullPointerException("name == null");
}
if (parameterTypes == null) {
parameterTypes = EmptyArray.CLASS;
}
for (Class<?> c : parameterTypes) {
if (c == null) {
throw new NoSuchMethodException("parameter type is null");
}
}
Method result = recursivePublicMethods ? getPublicMethodRecursive(name, parameterTypes)
: getDeclaredMethodInternal(name, parameterTypes);
// Fail if we didn't find the method or it was expected to be public.
if (result == null ||
(recursivePublicMethods && !Modifier.isPublic(result.getAccessFlags()))) {
throw new NoSuchMethodException(name + " " + Arrays.toString(parameterTypes));
}
return result;
}


代码做了一些判断,包括方法名是否为空,参数是否为空,最后因该是判断此方法是私有还是公有方法,这里recursivePublicMethods 默认是为true的,也就是说,我们调用getMethod方法时,就已经确定了调用的是公有方法.

我们使用Method对象来接受返回的数据,因为最终返回的是method类型,也就是方法,然后调用invoke来执行方法,看一下源码中invoke方法的解释:

/**
* Invokes the underlying method represented by this {@code Method}
* object, on the specified object with the specified parameters.
* Individual parameters are automatically unwrapped to match
* primitive formal parameters, and both primitive and reference
* parameters are subject to method invocation conversions as
* necessary.
*
* <p>If the underlying method is static, then the specified {@code obj}
* argument is ignored. It may be null.
*
* <p>If the number of formal parameters required by the underlying method is
* 0, the supplied {@code args} array may be of length 0 or null.
*
* <p>If the underlying method is an instance method, it is invoked
* using dynamic method lookup as documented in The Java Language
* Specification, Second Edition, section 15.12.4.4; in particular,
* overriding based on the runtime type of the target object will occur.
*
* <p>If the underlying method is static, the class that declared
* the method is initialized if it has not already been initialized.
*
* <p>If the method completes normally, the value it returns is
* returned to the caller of invoke; if the value has a primitive
* type, it is first appropriately wrapped in an object. However,
* if the value has the type of an array of a primitive type, the
* elements of the array are <i>not</i> wrapped in objects; in
* other words, an array of primitive type is returned.  If the
* underlying method return type is void, the invocation returns
* null.
*
* @param receiver  the object the underlying method is invoked from
* @param args the arguments used for the method call
* @return the result of dispatching the method represented by
* this object on {@code obj} with parameters
* {@code args}
*
* @exception IllegalAccessException    if this {@code Method} object
*              is enforcing Java language access control and the underlying
*              method is inaccessible.
* @exception IllegalArgumentException  if the method is an
*              instance method and the specified object argument
*              is not an instance of the class or interface
*              declaring the underlying method (or of a subclass
*              or implementor thereof); if the number of actual
*              and formal parameters differ; if an unwrapping
*              conversion for primitive arguments fails; or if,
*              after possible unwrapping, a parameter value
*              cannot be converted to the corresponding formal
*              parameter type by a method invocation conversion.
* @exception InvocationTargetException if the underlying method
*              throws an exception.
* @exception NullPointerException      if the specified object is null
*              and the method is an instance method.
* @exception ExceptionInInitializerError if the initialization
* provoked by this method fails.
*/
public native Object invoke(Object receiver, Object... args)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;


我们把多余注释去掉,再看一下:

/* @param receiver  the object the underlying method is invoked from
* @param args the arguments used for the method call
* @return the result of dispatching the method represented by
* this object on {@code obj} with parameters
*/
public native Object invoke(Object receiver, Object... args)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;


*看看每个参数的意思:

第一个代表了(调用方法的对象),我认为谁的方法谁调用

第二个参数代表:需要的参数列表,逗号分隔.

所以我们再使用method调用类的方法时,需要传入类的实例和参数列表!*

公有方法说完了,说一下私有方法.

前面说到:在调用公共方法时:

//公有方法1
Method method = cls.getMethod("print", String.class);
method.invoke(ls, "hahahahah");


使用类的class对象调用getMethod方法,这个方法默认是公共方法,但是访问私有方法时不是这样的,我们需要调用另一个方法:

//执行私有方法
method = cls.getDeclaredMethod("cal", int.class, int.class, int.class);
method.setAccessible(true);
method.invoke(ls, 2, 3, 4);


虽然方法名称不同,但是他们的代码中却调用了同一个方法,就是getMethod方法,千万不要误会,这个getMethod方法是一个重载方法。与我们直接调用的是同名不同参的方法.

//源码:
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
return getMethod(name, parameterTypes, false);
}


在调用公有方法时,在上面getMethod方法的第三个参数,源码中默认填写了true,但是此时却是false,因为我们此时调用的是一个私有方法。

同时我们需要调用:

method.setAccessible(true);


来设置可访问性。我们来看一下此方法解释:

Set the accessible flag for this object to the indicated boolean value.  A value of true indicates that the reflected object should suppress Java language access checking when it is used.  A value of  false indicates that the reflected object should enforce Java language access checks.
将此对象的可访问标志设置为所指示的布尔值。true值表明反射的对象应当压制java语言访问检查时。false表示反映的对象应执行java语言访问检查。


如果我们设置true,表示我们访问私有方法时尽量不受到权限检查,如果设置为false,代表我们要接受java权限检查,所以我们填写true,不接受权限检查。

在调用getDeclaredMethod方法时,和调用getMethod方法一样,我们需要传入:方法名,每个参数对应的class对象。

最后执行invoke方法,传入类的实例,和真正的参数!

method.invoke(ls, 2, 3, 4);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: