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