Java反射(三)之编写一个供所有类使用的toString方法

toString方法相信大家都不陌生,在日常开发中我们经常需要调用它来打印类的相关的信息,虽然可以通过重写toString方法以达到目的,但是每个类都去写一个toSting有的时候也是一件麻烦的事情,所以本文将通过Java的反射机制来编写一个可以供任意类使用的通用的toString方法。

具体的实现方式是先通过getDeclaredFileds获取所有的数据域,然后使用setAccessible将所有的域设置为可访问的。对于每个域,获得到了名字和值,再通过toString方法将每个值转换成字符串。

其次,泛型toString方法需要解释几个复杂的问题。循环引用将有可能导致无限递归。因此,ObjectAnalyzer将记录已经被访问过的对象。

下面是实现代码:

public static class ObjectAnalyzer {

    //为了避免循环引用而导致的无限递归
    private ArrayList 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 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以下)。

你可能感兴趣的:(Java反射(三)之编写一个供所有类使用的toString方法)