Java通过反射获给Java对象赋值为null
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
/**
*
*java通过反射获给java对象赋值为null
* @param obj 对象
* @param propertyName 字段
* @return java.lang.Object
*/
public static Object setPropertyNull(Object obj, String propertyName) {
//利用反射获取类属性
Field[] field = obj.getClass().getDeclaredFields();
for (int i = 0; i < field.length; i++) {
Field f = field[i];
f.setAccessible(true);
//获取属性名
String name = field[i].getName();
name = name.substring(0, 1).toUpperCase() + name.substring(1);
Method method = null;
//获取属性类型
String type = field[i].getGenericType().toString();
try {
//
if (type.equals("class java.lang.String") && propertyName.equalsIgnoreCase(name)) {
method = obj.getClass().getMethod("set" + name, String.class);
method.invoke(obj, (Object)null);
}
if (type.equals("class java.lang.Integer") && propertyName.equalsIgnoreCase(name)) {
method = obj.getClass().getMethod("set" + name, Integer.class);
method.invoke(obj, (Object)null);
}
if (type.equals("int") && propertyName.equalsIgnoreCase(name)) {
method = obj.getClass().getMethod("set" + name, int.class);
method.invoke(obj, (Object)null);
}
if (type.equals("class java.lang.Long") && propertyName.equalsIgnoreCase(name)) {
method = obj.getClass().getMethod("set" + name, Long.class);
method.invoke(obj, (Object)null);
}
if (type.equals("class java.math.BigDecimal") && propertyName.equalsIgnoreCase(name)) {
method = obj.getClass().getMethod("set" + name, BigDecimal.class);
method.invoke(obj, (Object)null);
}
} catch (Exception e) {
e.printStackTrace();
}
}
return obj;
}```
|
使用PropertyDescriptor反射调用setter/getter方法
有时候我们只知道一个对象的字段,我们想通过反射的方式将此字段赋值,可直接写反射又太浪费时间,还需要自己手动拼接方法名,而java为我们提供了一个很方便的类(PropertyDescriptor)来操作这一过程。使用很简单,直接看代码:
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
import com.pibgstar.demo.bean.User;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author pibigstar
* @create 2018-12-03 18:36
* @desc
**/
public class TestPropertyDescriptor {
public static void main(String[] args){
TestPropertyDescriptor test = new TestPropertyDescriptor();
User user = new User();
user.setId("123");
Object id = test.getAttribute("id", user);
System.out.println("id:"+id.toString());
test.setAttribute("name",user,"pibigstar");
System.out.println(user);
}
/**
* @Author:pibigstar
* @Description: 根据字段获取属性值
*/
private Object getAttribute(String filed,Object obj) {
try {
PropertyDescriptor pd = new PropertyDescriptor(filed,obj.getClass());
// 获取getter方法
Method method = pd.getReadMethod();
Object result = method.invoke(obj);
return result;
} catch (IntrospectionException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
/**
* @Author:pibigstar
* @Description: 设置属性字段值
*/
private void setAttribute(String filed,Object obj,Object value) {
try {
PropertyDescriptor pd = new PropertyDescriptor(filed,obj.getClass());
// 获取setter方法
Method method = pd.getWriteMethod();
method.invoke(obj, value);
} catch (IntrospectionException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
|
运行结果
1
2
|
id:123
User{id='123', name='pibigstar'}
|
反射中Method类的invoke() 和getMethod()
就是调用类中的方法,最简单的用法是可以把方法参数化。
1
2
|
invoke(class, method);
Method Class.getMethod(String name, Class<?>... parameterTypes)的作用是获得对象所声明的公开方法
|
该方法的第一个参数name是要获得方法的名字,第二个参数parameterTypes是按声明顺序标识该方法形参类型。
1
2
3
4
|
person.getClass().getMethod("Speak", null);
//获得person对象的Speak方法,因为Speak方法没有形参,所以parameterTypes为null
person.getClass().getMethod("run", String.class);
//获得person对象的run方法,因为run方法的形参是String类型的,所以parameterTypes为String.class
|
如果对象内的方法的形参是int类型的,则parameterTypes是int.class
1
2
3
4
5
|
//getMethod第一个参数是方法名,第二个参数是该方法的参数类型,
//因为存在同方法名不同参数这种情况,所以只有同时指定方法名和参数类型才能唯一确定一个方法
Method method = XXX.getClass().getMethod(methodName,new Class[0]);
//第一个参数是具体调用该方法的对象
//第二个参数是执行该方法的具体参数
|
如一个函数 int Test(int a, String str);
对应的getMethod方法:
-
getMethod(“Test”,int.class,String.class);
-
getMethod(“Test”,new Class[]{int.class,String.class});
1
2
3
4
5
6
7
8
|
//Method类的invoke(Object obj,Object args[])方法接收的参数必须为对象,返回值总是对象。
//如果参数为基本类型数据,必须转换为相应的包装类型的对象。
public Object invoke(Object obj,
Object... args)
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
|
InvokeObj.java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public class InvokeObj {
public void show() {
System.out.println("无参show()方法");
}
public void show(String name) {
System.out.println("show方法:" + name);
}
public String[] arrayShow(String[] arr) {
return arr;
}
public String stringShow(String str) {
return str;
}
public int intShow(int sum) {
return sum;
}
}
|
MethodInvokeTest.java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MethodInvokeTest {
public static void main(String[] args) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
Class<InvokeObj> clazz = InvokeObj.class;
Method[] methods = clazz.getMethods();
//输出了Class类型的所有方法。
System.out.println("以下输出InvokeObj类的方法");
for (Method method : methods) {
System.out.println(method);
}
System.out.println();
System.out.println("InvokeObj类的无参show()方法:");
Method method1 = clazz.getMethod("show", null);
//会执行无参show()方法
Object obj = method1.invoke(new InvokeObj(), null);
System.out.print("输出无参show()方法的返回值:" + obj);
System.out.println();
System.out.println();
System.out.println("InvokeObj类的show()方法: ");
Method method2 = clazz.getMethod("show", String.class);
Object obj1 = method2.invoke(new InvokeObj(), "hello,world");
// System.out.println("输出有参show()方法: ");
System.out.println();
System.out.println("InvokeObj类的arrayShow()方法: ");
Method method3 = clazz.getMethod("arrayShow", String[].class);
String[] strs = new String[]{"hello", "world", "!"};
//数组类型的参数必须包含在new Object[]{}中,否则会报IllegalArgumentException
String[] strings = (String[]) method3.invoke(new InvokeObj(), new Object[]{strs});
for (String str : strings) {
System.out.println("arrayShow的数组元素:" + str);
}
System.out.println();
System.out.println("InvokeObj类的StringShow()方法: ");
Method method4 = clazz.getMethod("stringShow", String.class);
String string = (String) method4.invoke(new InvokeObj(), "Thinking in java");
System.out.println("stringShow()方法的返回值: " + string);
System.out.println();
System.out.println("InvokeObj类的intShow()方法: ");
Method method5 = clazz.getMethod("intShow", int.class);
int num = (Integer) method5.invoke(new InvokeObj(), 89);
System.out.println("intShow()方法的返回值: " + num);
}
}
|
Java反射通过class获取父类泛型类型
在Java的反射技术中,通过Method和Field对象很容易就可以取出泛型类型的具体类型。但是普通类的泛型参数在编译期会被擦除,比如定义了一个List对象,在运行期我们可以拿到的信息就只剩下List,并不知道List中的元素类型是什么。这也说明了,为什么fun(List list)和 fun(List)会被编译器认为是相同的函数。
但是,对于获取父类的泛型类型,还是会有办法的。在Class类中,有这样一个方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
/**
* Returns the {@code Type} representing the direct superclass of
* the entity (class, interface, primitive type or void) represented by
* this {@code Class}.
*
* <p>If the superclass is a parameterized type, the {@code Type}
* object returned must accurately reflect the actual type
* parameters used in the source code. The parameterized type
* representing the superclass is created if it had not been
* created before. See the declaration of {@link
* java.lang.reflect.ParameterizedType ParameterizedType} for the
* semantics of the creation process for parameterized types. If
* this {@code Class} represents either the {@code Object}
* class, an interface, a primitive type, or void, then null is
* returned. If this object represents an array class then the
* {@code Class} object representing the {@code Object} class is
* returned.
*
* @throws java.lang.reflect.GenericSignatureFormatError if the generic
* class signature does not conform to the format specified in
* <cite>The Java™ Virtual Machine Specification</cite>
* @throws TypeNotPresentException if the generic superclass
* refers to a non-existent type declaration
* @throws java.lang.reflect.MalformedParameterizedTypeException if the
* generic superclass refers to a parameterized type that cannot be
* instantiated for any reason
* @return the superclass of the class represented by this object
* @since 1.5
*/
public Type getGenericSuperclass() {
if (getGenericSignature() != null) {
// Historical irregularity:
// Generic signature marks interfaces with superclass = Object
// but this API returns null for interfaces
if (isInterface())
return null;
return getGenericInfo().getSuperclass();
} else
return getSuperclass();
}
|
此方法返回的是具有泛型信息的父类,如果没有则返回Object对象。具体什么样的类都返回什么样的结果,来看测试代码
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public void printGenericType(){
Class clazz1 = new ArrayList<Integer>().getClass();
Class clazz2 = new String[0].getClass();
Class clazz3 = String.class;
Class clazz4 = Object.class;
Class clazz5 = new IntList().getClass();
System.out.println(clazz1.getGenericSuperclass());
System.out.println(clazz2.getGenericSuperclass());
System.out.println(clazz3.getGenericSuperclass());
System.out.println(clazz4.getGenericSuperclass());
System.out.println(clazz5.getGenericSuperclass());
}
|
结果:
1
2
3
4
5
|
java.util.AbstractList<E>
class java.lang.Object
class java.lang.Object
null
java.util.ArrayList<java.lang.Integer>
|
上面的测试代码中分别打印出了ArrayList,数组,普通类,Object类和自定义的一个类通过getGenericSuperclass()方法获取到的结果。普通类以及数组的父类都是Object,所以打印出的结果是Object。Object没有父类,打印出的是null。普通的ArrayList打印出的结果是java.util.AbstractList,第五个class打印出了带有泛型信息的java.util.ArrayList<java.lang.Integer>。IntList的定义是这样的
1
2
|
public class IntList extends ArrayList<Integer> {
}
|
ArrayList的定义是这样的
1
2
|
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
|
两处的不同在于继承父类的时候有没有知道父类泛型的具体类型,如果指定了,则可以通过上面的方法得到这个具体的类型。否则不能得到,这两者一定还存在 其他的区别联系,下面打印出获得的具体类型是什么
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public void getGenericType(){
Class clazz1 = new ArrayList<Integer>().getClass();
Type type = clazz1.getGenericSuperclass();
ParameterizedType parameterizedType = (ParameterizedType) type;
System.out.println(parameterizedType);
System.out.println(parameterizedType.getActualTypeArguments()[0].getClass());
Class clazz2 = new IntList().getClass();
Type type2 = clazz2.getGenericSuperclass();
ParameterizedType parameterizedType2 = (ParameterizedType) type2;
System.out.println(parameterizedType2);
System.out.println(parameterizedType2.getActualTypeArguments()[0].getClass());
}
|
结果
1
2
3
4
|
java.util.AbstractList<E>
class sun.reflect.generics.reflectiveObjects.TypeVariableImpl
java.util.ArrayList<java.lang.Integer>
class java.lang.Class
|
原来如此,如果在继承父类的时候没有指定父类的泛型类型,则这时候获取到的类型是TypeVariableImpl;而指定了父类的泛型类型则这时候就能获取到对应的Class
反射应用–通用的toString方法,泛型数组代码
反射应用 - toString方法/泛型数组代码
我们常用的toString方法都是显式地调用其域,当需要时,要在类中重写toString方法(基本上一个类一个),而通用toString方法什么都不需要知道,直接调用即可。
泛型数组代码不需要记,是java.util.Arrays类中copyOf(T[] original, int newLength) 方法实现底层中使用了反射机制,这里介绍了一下而已。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
public class ObjectAnalyzer{
private ArrayList<Object> vistied =new ArrayList<>();
public String toString(Object obj) {
if (obj == null) return "null"; //对象为null,返回“null”
if (vistied.contains(obj)) return "..."; //
vistied.add(obj);
Class<? extends Object> c1 = obj.getClass();
if(c1 == String.class) return (String) obj; //对象类型为字符串,直接打印
//对象类型为数组
if(c1.isArray()) {
String r = c1.getComponentType() + "[]{"; //getComponentType返回数组类型
for(int i=0;i<Array.getLength(obj);i++) { //getLength返回数组长度
if(i>0) r +=","; //逗号分隔元素
Object val =Array.get(obj, i); //返回数组中指定索引的值
if(c1.getComponentType().isPrimitive()) //若数组中类型为基本类型,直接添加于字符串r中
r += val;
else
r += toString(val); //递归,执行本方法中‘对象类型非数组’部分
}
return r+"}";
}
//对象类型为非数组
String r = c1.getName(); //获取对象所属的类
do {
r += "[";
Field[] fields = c1.getDeclaredFields(); //获取本类所有域
AccessibleObject.setAccessible(fields, true); //开放域的访问限制
for(Field f: fields) {
if(!Modifier.isStatic(f.getModifiers())) { //非静态域
if(!r.endsWith("[")) r += ",";
r += f.getName() + "=";
try { //处理异常
Class<?> t = f.getType(); //获取域的类型
Object val = f.get(obj); //获取域值
if(t.isPrimitive())
r+= val; //若域类型为基本数据类型,直接加入域值
else
r += toString(val); //若域类型为对象,调用本方法
}catch(Exception e){
e.printStackTrace();
}
}
}
r += "]";
c1 = c1.getSuperclass(); //开始获取超类的域
}while(c1 != null);
return r;
}
//泛型数组代码
public static Object copyOf(Object a,int newLength) { //参数类型定义为Object,而不可以是Object[],是因为基本类型数组不可转换为Object[],但可以是Object
Class<? extends Object> c1 = a.getClass();
if(!c1.isArray()) return null; //不是数组,返回null
Class<?> componentType = c1.getComponentType(); //获取数组类型
int length = Array.getLength(a); //获取数组长度
Object newArray = Array.newInstance(componentType, newLength);
System.arraycopy(a, 0, newArray, 0, Math.min(length,newLength));
return newArray;
}
}
|
调用
1
2
3
|
//调用toString方法
Employee em = new Employee(2,3,4,5);
System.out.println(new ObjectAnalyzer().toString(em));
|
java.lang.reflect.Array 相关API 表
返回类型 |
方法名和参数 |
解释 |
static Object |
get(Object array, int index) |
返回存储在指定index上给定数组的内容 |
static xxxx |
getXxxx(Object array, int index) |
若index索引处的类型为基本类型,可直接调用相关方法,那样得到的结果就不用强转了 |
static void |
set(Object array, int index, Object value) |
将新值存储到给定位置上的给定数组中 |
static void |
setXxxx(Object array, int index, Object value) |
若index索引处的类型为基本类型,可直接调用相关方法 |
static int |
getLength(Object array) |
返回指定数组对象的长度 |
static Object |
newInstance(Class<?> componentType, int length) |
返回具有给定类型,给定长度的新数组 |