java中反射的地位可想而知,有的人说反射就是程序员的春天,现在关于java的框架,反射几乎都会用到,因为确实很方便,说的那么多,那就开始一起探讨吧。
如果用过jdbc的话,我们是怎么加载驱动的呢?我们都会写这样一串代码
Class.forName("com.mysql.jdbc.Driver");
其实这里就是用到反射了。这是开场白,那我们就慢慢深入吧,首先新建一个类用于操作
public class Student{
public String name;
public Integer age;
public String sex;
}
很简单的一个类,我们仿照jdbc加载驱动的方式,我们自己也操作一遍.我是在android环境下测试的。
try {
Class<?> aClass = Class.forName("com.myapplication.Student");
Log.i("data-->", aClass.getSimpleName());
} catch (Exception e) {
e.printStackTrace();
}
打印出来的就是Student,Class.forName里的就是这个类在这个项目里的真实路径,很显然加载出了这个类,那我们就通过这种方式实例化这个类。
try {
Class<?> aClass = Class.forName("com.myapplication.Student");
Object o = aClass.newInstance();
Log.i("data-->", String.valueOf(o.getClass()));
} catch (Exception e) {
e.printStackTrace();
}
打印出来的就是Student这个类的名称,这就实例化了这个类了,那我们如何通过反射往这个类里设置值呢?
封装了一个方法主要为了方便
private void showLog(Object name) {
Log.i("data-->", String.valueOf(name));
}
首先就是如何知道类里的所有的属性,其实也很简单
try {
Class<?> aClass = Class.forName("com.myapplication.Student");
Object o = aClass.newInstance();
Field[] fields = aClass.getFields();
for (int i = 0; i < fields.length; i++) {
showLog(fields[i].getName());
}
} catch (Exception e) {
e.printStackTrace();
}
但是这种方法只能针对属性是public类型的,如果是私有的话这种方法就不行了,因为这种方法只能获取public类型的,其实我们首先要了解反射中的两个方法的区别
getFields()只能获取public的字段,包括父类的,而getDeclaredFields()只能获取自己声明的各种字段,包括public,protected,private。
try {
Class<?> aClass = Class.forName("com.myapplication.Student");
Object o = aClass.newInstance();
Field[] fields = aClass.getDeclaredFields();
for (Field field : fields) {
//赋予获取
field.setAccessible(true);
showLog(field.getName());
}
} catch (Exception e) {
e.printStackTrace();
}
这样的话就可以了。那就开始设置值了.
try {
Class<?> aClass = Class.forName("com.myapplication.Student");
Object o = aClass.newInstance();
Field name = aClass.getDeclaredField("name");
name.setAccessible(true);
name.set(o,"小明");
Student student = (Student) o;
showLog(student.name);
} catch (Exception e) {
e.printStackTrace();
}
打印出了小明.但是我们如果在不知道类的属性名称的时候又该怎么设置值呢?
try {
Class<?> aClass = Class.forName("com.myapplication.Student");
Object o = aClass.newInstance();
Field[] declaredFields = aClass.getDeclaredFields();
for (Field field : declaredFields) {
field.setAccessible(true);
if(field.getGenericType().toString().equals("class java.lang.Integer")){
showLog("");
field.set(o,12);
}else if(field.getGenericType().toString().equals("class java.lang.String")){
field.set(o,"小明");
}
}
Student student = (Student) o;
showLog(student.toString());
} catch (Exception e) {
e.printStackTrace();
}
getGenericType()方法是获取属性类型的,不同类型的属性要转换,不然会报类型转换异常的.
但是我们在使用反射的时候一定要注意一个问题就是一个类一定要有无参构造方法,不然就报错,为什么这样说呢,就是因为我们用反射实例化对象,就是调用无参构造方法,
现在我们只是采用属性名,那通过set和get方法又如何操作呢?
try {
Class<?> aClass = Class.forName("com.myapplication.Student");
Object o = aClass.newInstance();
Method setName = aClass.getDeclaredMethod("setName", String.class);
setName.setAccessible(true);//这个是赋予权限的,可以获取私有的
setName.invoke(o,"小明");
Student student = (Student) o;
showLog(student.toString());
} catch (Exception e) {
e.printStackTrace();
}
又如何根据构造方法实例化对象呢?
try {
Class<?> aClass = Class.forName("com.myapplication.Student");
Constructor<?> constructor = aClass.getDeclaredConstructor(String.class, Integer.class, String.class);
//getDeclaredConstructor里的参数就是构造方法的参数类型
Object o1 = constructor.newInstance("小明", 12, "男");//通过这种方式传参
Student student = (Student) o1;
showLog(student.toString());
} catch (Exception e) {
e.printStackTrace();
}
其实不管用哪个方式,会发现有个共性,不管哪种方式,都会有两种方法,一个根据类型获取,一个获取全部,只需要我们记住一个,另一个也能猜到的,反射的应用场景很多,我们现在就举个例子就是怎么解析json,然后把数据封装成java类.
try {
String json = "{\"name\":\"小明\",\"age\":12,\"sex\":\"男\"}";
JSONObject jsonObject = new JSONObject(json);
Class<?> aClass = Class.forName("com.myapplication.Student");
Object obj = aClass.newInstance();
Field[] declaredFields = aClass.getDeclaredFields();
for (Field field : declaredFields) {
field.setAccessible(true);
if (field.getGenericType().toString().equals(String.class.toString())) {
field.set(obj, jsonObject.getString(field.getName()));
} else if (field.getGenericType().toString().equals(Integer.class.toString())) {
field.set(obj, jsonObject.getInt(field.getName()));
}
}
Student student = (Student) obj;
showLog(student);
} catch (Exception e) {
e.printStackTrace();
}
很简单的就实现了,其实这是可以封装起来的,利用泛型那我们就封装一下,
public static <T> T getJsonClass(String json, Class<T> clzz) {
try {
JSONObject jsonObject = new JSONObject(json);
Object obj = clzz.newInstance();
Field[] declaredFields = clzz.getDeclaredFields();
for (Field field : declaredFields) {
field.setAccessible(true);
if (field.getGenericType().toString().equals(String.class.toString())) {
//json的值也要判空的,不然数据为空的话,会出现莫名其妙的错误,我太仓促没加,写程序就要时刻记得判空,才能提高效率
field.set(obj, jsonObject.getString(field.getName()));
} else if (field.getGenericType().toString().equals(Integer.class.toString())) {
field.set(obj, jsonObject.getInt(field.getName()));
}
//其他的类型可以自行加入,这样就可以解析json,然后转换成实体类
}
return (T) obj;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
其实调用就只是一行代码就可以了,是不是简单很多
Student student = getJsonClass(json, Student.class);
反射和泛型可谓是很好的组合,因为框架都会用到的,还有一个就是注解的知识,之后我会讲述的,利用注解、反射和泛型实现一个简单的实体类注入的项目,相信接触过Spring的小伙伴都知道吧。