如何優雅地打印一個Java對象?

你好呀,我是沉默王二,一個和黃家駒同樣身高,和劉德華同樣顏值的程序員。雖然已經寫了十多年的 Java 代碼,但仍然以爲本身是個菜鳥(請容許我慚愧一下)。css

在一個月黑風高的夜晚,我思前想後,以爲不再能這麼蹉跎下去了。因而痛下決心,準備經過輸出的方式倒逼輸入,以此來修煉本身的內功,從而進階成爲一名真正意義上的大神。與此同時,但願這些文章可以幫助到更多的讀者,讓你們在學習的路上再也不寂寞、空虛和冷。java

爲了更好的輸入,我選擇 Stack Overflow 做爲戰鬥的第一線,畢竟不少前輩都在強烈推薦。本篇文章,咱們來探討一下如何優雅地打印一個Java對象。程序員

真沒想到,這個問題的訪問量像阿爾泰山同樣高,訪問量足足有 29+ 萬次,這不得了啊!說明有不少不少的程序員被這個問題困擾過。web

來回顧一下提問者的問題吧。數組

提問者定義了這樣一個類:bash

public class Cmower {
    private String name;

    public Cmower(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
複製代碼

而後建立了一個該類的對象,並嘗試打印它:app

Cmower cmower = new Cmower("沉默王二");
System.out.println(cmower);
複製代碼

可是輸出的結果並非他想要的:ide

com.cmower.java_demo.stackoverflow.printObject.Cmower@355da254
複製代碼

除此以外,他在打印數組的時候也出現了類似的問題:學習

Cmower [] cmowers = {new Cmower("沉默王二"), new Cmower("沉默王三")};
System.out.println(cmowers);
複製代碼

輸出結果爲:ui

[Lcom.cmower.java_demo.stackoverflow.printObject.Cmower;@4dc63996
複製代碼

Cmower@355da254[LCmower;@4dc63996 這樣的輸出結果表明着什麼意思呢?怎麼樣才能把 Cmower 類的 name 打印出來呢?以及如何打印一個對象的列表(數組或者集合)呢?

若是你們也被這樣的問題困擾過,或者正在被困擾,就請隨來,我們肩並肩手拉手一塊兒梳理一下這個問題,並找出最佳答案。Duang、Duang、Duang,打怪進階嘍!

0一、究竟發生了什麼?

全部的 Java 對象都默認附帶了一個 toString() 的方法,當咱們嘗試打印這個對象的時候,該方法就會被調用。

System.out.println(object);  // 調用 object.toString()
複製代碼

toString() 方法由 Object 類(全部 Java 對象的超類)定義,該方法會返回一個看起來晦澀難懂的字符串:

1)Class 名,由包名和類名組成,好比 com.Cmower
2)@ 鏈接符;
3)十六進制的哈希碼。

來看一下該方法的源碼:

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
複製代碼

數組和普通的 Java 對象相似,只有一點點不一樣——追蹤 Class 類的 getName() 方法就能夠印證這一點。

If this class object represents a class of arrays, then the internal form of the name consists of the name of the element type preceded by one or more '[' characters representing the depth of the array nesting.

大體的意思就是,若是是一個數組的話,Class 名的前面會有一個或者多個英文中括號「[」,表示數組的維度(一維數組爲一個「[」,二維數組爲兩個「[」),而後再緊跟一個元素的類型首字母。

這就是爲何對象數組的前綴是「[L」的緣由。是否是有一種恍然大悟的感受?

0二、自定義輸出

若是想在打印的時候輸出本身預期的結果,就必須在自定義類中重寫 toString() 方法,來看例子。

public class Cmower {
    private String name;
    // 省略構造方法和 getter/setter

    @Override
    public String toString() {
        return name;
    }
}
複製代碼

當咱們再次打印 Cmower 對象時,輸出結果就再也不是 com.Cmower@355da254 了。

沉默王二
複製代碼

可是這樣的結果並不會令咱們滿意,它有些突兀,無法表示對象的類型。更優雅的作法是這樣的:

public class Cmower {
    private String name;
    // 省略構造方法和 getter/setter

    @Override
    public String toString() {
        return getClass().getSimpleName() + "[name=" + name + "]";
    }
}
複製代碼

再次打印 Cmower 對象,輸出結果爲:

Cmower[name=沉默王二]
複製代碼

這樣的形式不只看起來美觀,還可以在調試的時候給出有用的信息。可是,有時候咱們不想重寫 toString() 方法(想保留原有的打印格式 ClassType@123121),又想打印該對象的信息,那麼最好定義一個新的方法,好比說 toMyString() 方法。

0三、自動化輸出

IDE(Eclipse 或者 Intellj IDEA) 一般會提供一種針對類的字段的輸出格式,用來覆蓋 toString() 方法。

@Override
public String toString() {
    return "Cmower{" +
            "name='" + name + '\'' +
            '}';
}
複製代碼

另外,一些開源的第三方類庫也會提供這樣的功能,好比說:

1)Apache Commons Lang 的 ToStringBuilder。

使用方法:

@Override
public String toString() {
    return ToStringBuilder.reflectionToString(this);
}
複製代碼

輸出結果:

com.cmower.printObject.Cmower@355da254[name=沉默王二]
複製代碼

2)Google Guava 的 MoreObjects

使用方法:

@Override
public String toString() {
    return MoreObjects.toStringHelper(this)
            .add("name", getName())
            .toString();
}
複製代碼

輸出結果:

Cmower{name=沉默王二}
複製代碼

3)Lombok 的 @toString 註解(IDE 須要先安裝 Lombok 的插件)

使用方法:

@ToString
public class Cmower {

    private String name;

    // 省略構造方法和 getter/setter
}
複製代碼

只須要一個 @toString 註解,不須要覆蓋 toString() 方法。

輸出結果:

Cmower(name=沉默王二)
複製代碼

0四、打印對象列表(數組或者集合)

上述內容已經把打印單個對象的事情嘮明白了,are you ok?接下來,咱們來講道說道打印對象列表的事兒。

1)數組

Arrays.toString() 能夠將任意類型的數組轉成字符串,包括基本類型數組和引用類型數組。代碼示例以下。

Cmower[] cmowers = {new Cmower("沉默王二"), new Cmower("沉默王三")};
System.out.println(Arrays.toString(cmowers));
複製代碼

輸出結果:

[Cmower{name='沉默王二'}, Cmower{name='沉默王三'}]
複製代碼

2)集合

對於集合來講,能夠直接打印就能輸出咱們預期的結果。代碼示例以下。

List<Cmower> list = new ArrayList<>();
list.add(new Cmower("沉默王二"));
list.add(new Cmower("沉默王三"));
System.out.println(list);
複製代碼

輸出結果:

[Cmower{name='沉默王二'}, Cmower{name='沉默王三'}]
複製代碼

0五、鳴謝

好了,我親愛的讀者朋友,以上就是本文的所有內容了。能在疫情期間堅持看技術文,二哥必需要伸出大拇指爲你點個贊👍。

原創不易,若是以爲有點用的話,請不要吝嗇你手中點贊的權力——由於這將是我寫做的最強動力。

相關文章
相關標籤/搜索