toString
方法相信大家都不陌生,在日常开发中我们经常需要调用它来打印类的相关的信息,虽然可以通过重写toString
方法以达到目的,但是每个类都去写一个toSting
有的时候也是一件麻烦的事情,所以本文将通过Java的反射机制来编写一个可以供任意类使用的通用的toString
方法。
具体的实现方式是先通过getDeclaredFileds
获取所有的数据域,然后使用setAccessible
将所有的域设置为可访问的。对于每个域,获得到了名字和值,再通过toString
方法将每个值转换成字符串。
其次,泛型toString
方法需要解释几个复杂的问题。循环引用将有可能导致无限递归。因此,ObjectAnalyzer
将记录已经被访问过的对象。
下面是实现代码:
public static class ObjectAnalyzer {
//为了避免循环引用而导致的无限递归
private ArrayList<Object> visited = new ArrayList<>();
/**
* 将任意对象toString
*
* @param object object
* @return String
*/
@SuppressWarnings("WeakerAccess")
public String toString(Object object) {
if (object == null) return "null";
if (visited.contains(object)) return "...";
visited.add(object);
Class<?> c1 = object.getClass();
if (c1 == String.class) return (String) object;
//是否为数组
if (c1.isArray()) {
StringBuilder r = new StringBuilder(c1.getComponentType() + "[]{");
for (int i = 0; i < Array.getLength(object); i++) {
if (i > 0) {
r.append(",");
}
Object val = Array.get(object, i);
//Class.isPrimitive 判断是否为原始类型(boolean char byte short int long float double )
if (c1.getComponentType().isPrimitive()) {
r.append(val);
} else {
r.append(toString(val));
}
}
return r.toString() + "}";
}
StringBuilder r = new StringBuilder(c1.getName());
do {
r.append("[");
//获取所有的域(属性)
Field[] fields = c1.getDeclaredFields();
//设置对象数组可访问 true表示屏蔽Java语言的访问检查,使得私有属性也可被查询和设置
AccessibleObject.setAccessible(fields, true);
for (Field field : fields) {
if (!Modifier.isStatic(field.getModifiers())) {
if (!r.toString().endsWith("[")) r.append(",");
r.append(field.getName()).append("=");
try {
Class<?> type = field.getType();
Object val = field.get(object);
//是否是原始类型
if (type.isPrimitive()) {
r.append(val);
} else {
r.append(toString(val));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
r.append("]");
c1 = c1.getSuperclass();
} while (c1 != null);
return r.toString();
}
}
测试类:
public class ReflectionTest {
public static void main(String[] args) {
ArrayList<Integer> squares = new ArrayList<>();
for (int i = 0; i <= 5; i++) {
squares.add(i * i);
}
System.out.println(new ReflectionUtils.ObjectAnalyzer().toString(squares));
}
}
打印结果:
java.util.ArrayList[elementData=class java.lang.Object[]{java.lang.Integer[value=0][][],java.lang.Integer[value=1][][],java.lang.Integer[value=4][][],java.lang.Integer[value=9][][],java.lang.Integer[value=16][][],java.lang.Integer[value=25][][],null,null,null,null},size=6][modCount=6][][]
注:该方法在JDK 1.9
及以上版本会报WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
警告:
暂时没有弄清楚具体的原因,想要消除此警告可以降低JDK版本(降到JDK 1.9以下)。