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等)是如何實現對象信息的打印效果的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章