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

刚刚了解了反射,看到了使用反射操作类的私有属性,在操作私有变量时,需要使用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);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值