网上对于java反射的解释已经很多了,很多官方的语言我们并不能很好的理解。
我在这里说的通俗些,java反射的作用就是让我们从java虚拟机里面生成的class来进行反序列化得到这个类的实例。
那么它的用处就更加多了,例如android中startyactivity()方法传入的Intent对象。在内部其实用到的就是java的反射。
首先来写一个测试类 ReflectBean
/**
* 反射实体类
*/
public class ReflectBean implements IReflect {
@RetentionTest(hello = "hai", world = "sb")
private int id;
private String[] names;
private ArrayList<Double> doubles;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public void onButtonClick(View v) {
}
@Override
public void onCheckClick() {
}
public enum reflectEnum {
ONE,
TOW,
THREE;
}
public ReflectBean() {
}
public ReflectBean(String aaa) {
}
public ReflectBean(String[] aaa) {
}
public void reflecteTest() {
}
@RetentionTest(world = "hahahhaa")
public String reflectAdd(String aaa) {
Log.i("test", "reflectAdd方法调用:" + aaa);
return aaa;
}
}
/**
* 测试接口
*/
public interface IReflect {
void onButtonClick(View v);
void onCheckClick();
}
好。接下来我就来讲述一下java反射的使用。
首相定义一个ReflectMobel类
1,反射一个类的三种方法
private static final String className = "com.android.ui.reflect.ReflectBean";
/**
* 反射一个类有三种方法
* */
//1
Class<ReflectBean> class1 = ReflectBean.class;
Log.i("test", class1.getName());
//2
Class class2 = Class.forName(className);
//3
ReflectBean reflectBean = new ReflectBean();
Class class3 = reflectBean1.getClass();
2,获得类加载器
Log.i("test", "类加载器 " + class1.getClass().getClassLoader().getClass().getName());
其实在java中有三种类类加载器。
1,Bootstrap ClassLoader 此加载器采用c++编写,一般开发中很少见。
2,Extension ClassLoader 用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类。
3,AppClassLoader 加载classpath指定的类,是最常用的加载器。同时也是java中默认的加载器。
3,获取class对应的java对象
/**
* 获取对象
* 调用了ReflectMobel的无参数构造方法.即对象
* */
Object object = class1.newInstance();
ReflectBean reflectBean = class1.newInstance();
if (!class1.isInstance(reflectBean)) return;//判断某个实例是否属于这个类
4,获取class中的方法对象
/**
* 获得类中的一个方法
*/
//有参数
Method method = class1.getDeclaredMethod("reflectAdd", String.class);
//无参数
Method method1 = class1.getDeclaredMethod("reflecteTest");
//多个参数
Method method2 = class1.getDeclaredMethod("reflecteTest", new Class[]{String.class,int.class});
Log.i("test", "reflectAdd方法:" + method.getName() + method1.getName());
5,获取class标记的注解
if (method.isAnnotationPresent(RetentionTest.class)) {//判断该类有没有标记这个注解
RetentionTest retentionTest = method.getAnnotation(RetentionTest.class);
Log.i("test", "reflectAdd标记的注解:" + retentionTest.world());
}
6,调用方法
/**
* 调用该方法
* 如果该方法没有参数,则传入new Object[]{}
* 如果该方法有参数,则传入参数
* 返回结果为该方法的返回结果
* */
String aaa = (String) method.invoke(reflectBean, "bbbbbb");
Log.i("test", "reflectAdd方法的返回结果:" + aaa);
method1.invoke(reflectBean);//无参方法
method2.invoke(reflectBean, new Object[]{});//无参方法
7,获取类的信息
//类的修饰符
String classXiushifu = Modifier.toString(class1.getModifiers());
Log.i("test", "类的修饰符:" + classXiushifu);
//类的简单名字,指不带包路径的名字
String classSimpleName = class1.getSimpleName();
Log.i("test", "类的简单名字,指不带包路径的名字:" + classSimpleName);
//类的带包路径的名字
String className = class1.getName();
Log.i("test", "类的带包路径的名字:" + className);
8,获得全部的构造函数
//取得全部的构造函数
Constructor<?> cons[] = class1.getConstructors();
Constructor constructor = class1.getConstructor();//无参构造函数
Constructor constructor1 = class1.getConstructor(String.class);//带String参数的构造函数
Constructor constructor2 = class1.getConstructor(new Class[]{String.class, double.class});//带String参数的构造函数
Log.i("test", "类的构造函数:" + constructor1.getName());
for (int i = 0; i < cons.length; i++) {
Log.i("test", "类的构造函数:" + cons[i].getName());
}
9,获取类中所有的接口
//保存所有的接口
Class<?> intes[] = class1.getInterfaces();
for (int i = 0; i < intes.length; i++) {
Log.i("test", "类的接口:" + intes[i].getName());
}
10,获得当前类的父类
//取得父类
Class<?> temp = class1.getSuperclass();
11,获取类中的属性,获取全部属性
/**
* 获取属性
* 分为所有的属性和指定的属性
* 1,获得所有属性
* */
//获取所有的属性
Field[] fields = class1.getDeclaredFields();
for (Field field : fields) {
Log.i("test", "获得属性的修饰符:" + Modifier.toString(field.getModifiers()));//获得属性的修饰符,例如public,static等等
Log.i("test", "属性的类型的名字:" + field.getType().getSimpleName());//属性的类型的名字
Log.i("test", "属性的名字:" + field.getName());//属性的名字
}
12,获取类中的属性,获取指定属性
/**
* 2,获取特定的属性
* */
Field idF = class1.getDeclaredField("id");
idF.setAccessible(true); //使用反射机制可以打破封装性,导致了java对象的属性不安全。设置可访问性
idF.set(reflectBean, 111);
Log.i("test", "属性的名字:" + idF.getName());//id的值
Log.i("test", "属性的值:" + idF.get(reflectBean).toString());//string 值
Field stringsF = class1.getDeclaredField("names");
stringsF.setAccessible(true); //使用反射机制可以打破封装性,导致了java对象的属性不安全。设置可访问性
stringsF.set(reflectBean, new String[]{"aaa", "bbb", "ccc"});
String[] aaas = (String[]) stringsF.get(reflectBean);
Log.i("test", "属性的名字:" + stringsF.getName());//string 值
Log.i("test", "属性的值:" + aaas[0]);//string 值
Log.i("test", "属性的值:" + Array.get(aaas, 1));//string 值
Log.i("test", "数组长度 " + Array.getLength(aaas));
Log.i("test", "数组的第一个元素: " + Array.get(aaas, 0));
注意事项:
当我们用构造器获得类构造方法的时候,如下:
//2、根据构造器参数类型获取相应的构造器对象
Constructor csr = cls.getConstructor(String[].class);
//3、创建实体对象
Object entity = csr.newInstance(new String[]{});
如果构造方法中需要传入且只传入一个数组的时候会报错。
那么,错误的原因是什么呢?
由于类构造方法(String strs[])只有一个String数组的参数所以这是编译器会把字符串数组当作一个可变长度参数传 给对象strs,而我们取得方法只有一个参数,所以就会出现wrong number of arguments的异常,我们只要把字符串数组强制转换为一 个Object对象就可以解决这个异常了, 。
正确的写法是:
//2、根据构造器参数类型获取相应的构造器对象
Constructor csr = cls.getConstructor(String[].class);
String str[]={"111","123"};
//3、创建实体对象
Student entity = (Student)csr.newInstance((Object)str);
还有一个细节需要注意
//有参数
Method method = class1.getDeclaredMethod("reflectAdd", String.class);
//无参数
Method method1 = class1.getDeclaredMethod("reflecteTest");
//多个参数
Method method2 = class1.getDeclaredMethod("reflecteTest", new Class[]{String.class, int.class});
String aaa = (String) method.invoke(reflectBean, "bbbbbb");
当我们需要去调用某个类中的方法的时候,用invoke去调用方法调用的只有公有的(public),如果调用private类型的会报错。
java反射的常用方法就使这些。我们可以根据反射的特性去做很多事情。例如和注解一起使用能够让自己的程序更加的灵活。
java反射的实际使用场景还需要根据自己的程序使用情况而定。