在日常開發中,我們總需要打印日誌,記錄程序中一些關鍵對象的信息,大大提高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等)是如何實現對象信息的打印效果的。