Java打印对象信息(一)

       在日常开发中,我们总需要打印日志,记录程序中一些关键对象的信息,大大提高bug的排查速度。但是如果类的设计不规范,这些类的对象信息是不能被Log框架或者System.out.println()语句规范地打印出来的。比如下面这个Person类,这种只有成员变量以及成员变量的get/set函数的类,常被用作VO、DTO、DO等,如果直接按照以下方式,其对象信息打印出来对日志分析基本没有用处。(本文代码运行环境采用JDK版本为JDK9)

《一》采用System.out.println()方式打印对象信息:

public class Person {
    private int age;

    private String name;

    public Person(){
    }

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public static void main(String []args){
        Person p = new Person(22,"Allen");
        Person p1 = new Person(18,"Peter");
        System.out.println(p);
        System.out.println(p1);
    }
}

我们看一下输出结果:

Person@64c64813
Person@3ecf72fd

可见根本没有打印出我们想看到的对象p的成员变量信息,这个打印结果是怎么来的呢?我们先看一下System.out.println()的内部实现(java.io.PrintStream类):

public void println(Object x) {
        String s = String.valueOf(x);
        synchronized (this) {
            print(s);
            newLine();
        }
    }

可以看出先是打印String.valueOf(x)的信息,然后再打印一个换行,而String.valueOf(x)的内部实现如下:

    /**
     * Returns the string representation of the {@code Object} argument.
     *
     * @param   obj   an {@code Object}.
     * @return  if the argument is {@code null}, then a string equal to
     *          {@code "null"}; otherwise, the value of
     *          {@code obj.toString()} is returned.
     * @see     java.lang.Object#toString()
     */
    public static String valueOf(Object obj) {
        return (obj == null) ? "null" : obj.toString();
    }

如果对象为null就返回字符串null,否则返回这个对象的toString()函数的结果,我们需要注意toString()这个函数是定义在Object类中的,其在Object类中的定义如下:

 /**
     * Returns a string representation of the object. In general, the
     * {@code toString} method returns a string that
     * "textually represents" this object. The result should
     * be a concise but informative representation that is easy for a
     * person to read.
     * It is recommended that all subclasses override this method.
     * <p>
     * The {@code toString} method for class {@code Object}
     * returns a string consisting of the name of the class of which the
     * object is an instance, the at-sign character `{@code @}', and
     * the unsigned hexadecimal representation of the hash code of the
     * object. In other words, this method returns a string equal to the
     * value of:
     * <blockquote>
     * <pre>
     * getClass().getName() + '@' + Integer.toHexString(hashCode())
     * </pre></blockquote>
     *
     * @return  a string representation of the object.
     */
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

从中我们不难看到每个类如果没有重写toString()函数的话,默认是返回该类的名称 + “@” + 该对象hashCode值得十六进制数字;所以JDK官方是推荐我们所有的类都应该重写此类(It is recommended that all subclasses override this method.)。如果我们在上面的Person类中重写该方法,那么System.out.println语句就会按照我们的重写的toString()方法打印对象信息,如下:

public class Person {
    private int age;

    private String name;

    public Person() {
    }

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person(age:" + age + ",name:" + name + ")";
    }

    public static void main(String[] args) {
        Person p = new Person(22, "Allen");
        Person p1 = new Person(18, "Peter");
        System.out.println(p);
        System.out.println(p1);
    }
}

输出结果:

Person(age:22,name:Allen)
Person(age:18,name:Peter)

这样就可以清晰地打印出每个Person对象的成员信息了。

       如果你使用了Lombok插件,那么直接使用它的@Data注解,无需实现toString()函数的复写了,如下:

import lombok.Data;

@Data
public class Person {
    private int age;

    private String name;

    public Person() {
    }

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public static void main(String[] args) {
        Person p = new Person(22, "Allen");
        Person p1 = new Person(18, "Peter");
        System.out.println(p);
        System.out.println(p1);
    }
}

代码运行结果:

Person(age=22, name=Allen)
Person(age=18, name=Peter)

这里使用的Lombok插件版本为1.16.10,可以看出插件帮我们完成了对一个对象的描述功能。如果我们既使用了Lombok插件,又自己重写了一下toString()函数,会有什么的情况呢?我们试一下:

import lombok.Data;

@Data
public class Person {
    private int age;

    private String name;

    public Person() {
    }

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "年龄:" + age + ";名字:" + name;
    }

    public static void main(String[] args) {
        Person p = new Person(22, "Allen");
        Person p1 = new Person(18, "Peter");
        System.out.println(p);
        System.out.println(p1);
    }
}

输出结果为:

年龄:22;名字:Allen
年龄:18;名字:Peter

可以看到打印信息使用的是我们重写的toString()方式,具体Lombok插件是如何实现的,大家感兴趣可以看一下Lombok的实现机制。

       本次主要讲了System.out.println()这种最基础的的方式打印对象信息需要注意的地方,主要就是toString()函数,下一节我们看一下一些日志框架(例如log4j等)是如何实现对象信息的打印效果的。

猜你喜欢

转载自blog.csdn.net/qq_22076345/article/details/81369281