1. 程式人生 > >認識Object中的幾個經常需要覆蓋的方法——toString方法

認識Object中的幾個經常需要覆蓋的方法——toString方法

學習Java少不了對Object的認知,所有類都會繼承它的屬性,真正的超類。這一個系列,我會對Object中的幾個方法,也就是我們自定義類的時候需要重寫的幾個方法做一個介紹。下面是這一個系列的主要內容:

  • toString方法
  • clone方法
  • 自定義類時考慮實現Comparable介面

本系列內容源於對《Effective Java》中文第二版第8條到第12條的學習記錄。所有內容的準確性均以原書為準。

1,引言

toString方法在平時是我們用的相對來說最多的,所以它的意義也更加重要,雖然重寫它的時候沒有約束,但是我們應該利用它達到我們想要的目的,下面就來看一個官方API對其的介紹和建議:

public String toString()

返回該物件的字串表示。通常,toString 方法會返回一個“以文字方式表示”此物件的字串。結果應是一個簡明但易於讀懂的資訊表示式。建議所有子類都重寫此方法。

Object 類的 toString 方法返回一個字串,該字串由類名(物件是該類的一個例項)、at 標記符“@”和此物件雜湊碼的無符號十六進位制表示組成。換句話說,該方法返回一個字串,它的值等於:

getClass().getName() + '@' + Integer.toHexString(hashCode())
 

返回:

該物件的字串表示形式。

從官方的介紹我們可以總結出兩點:

  • 建議所有子類重寫該方法
  • 預設的格式是類名+“@”+雜湊碼值的無符號16進製表示

2,分析

我們下面就對上述的兩條進行驗證,看看對於Java平臺類庫中的類是不是都重寫toString方法,如果沒有,是不是符合第二條的組成規則。

  • 驗證沒有重寫的類

我在API中隨便找了一個類:

public final class Parameter

org/omg/Dynamic/Parameter.java。由 "3.2" 版的 IDL-to-Java 編譯器(可移植)從 ../../../../src/share/classes/org/omg/PortableInterceptor/Interceptors.idl 生成,生成時間為 2006 年 6 月 30 日,星期五上午 12:40:09 (GMT-08:00)。

它就沒有重寫toString方法,測試如下:

可見並不是所有java平臺類庫中的所有類都重寫了toSTring,但是不可否認我們經常使用的比如String,檔案相關API,集合相關API等等都是重寫了的

  • 驗證Object中的toString方法預設輸出組成

這裡為了驗證,我建立了兩個Object物件,測試結果如下:

於是我們還會有下面兩個猜測

  • 如果我們自己重寫了hashCode方法還會保持上面的規律嗎?
  • 如果想保持一致該腫麼辦?

我們使用如下的Person類測試第一個猜測:

package hfut.edu;

/**  * Date:2018年10月1日 上午11:05:45 Author:why  */

public class Person {

    private int hash=1;     int age;     String name;     String sex;

    public Person(int age, String name, String sex) {         super();         this.age = age;         this.name = name;         this.sex = sex;     }     public int getAge() {         return age;     }     public void setAge(int age) {         this.age = age;     }     public String getName() {         return name;     }

    public void setName(String name) {         this.name = name;     }

    public String getSex() {         return sex;     }

    public void setSex(String sex) {         this.sex = sex;     }

    @Override     public boolean equals(Object obj) {         // TODO Auto-generated method stub

        if (!(obj instanceof Person))             return false;

        Person p = (Person) obj;         return this.age == p.age && this.name.equals(p.name) && this.sex.equals(p.sex);

    }

         @Override     public int hashCode() {         // TODO Auto-generated method stub             hash=hash*3+this.age;         hash=hash*3+this.name.hashCode();         hash=hash*3+this.sex.hashCode();         return hash;     } }  

測試結果如下:

到這裡我們就驗證了第一條是否定的。但是我覺得還是有點問題,因為我們觀察Object類中的toString方法

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

我們想了一下,它呼叫的就是我們的hashCode方法轉為16進位制字串顯示,為什麼我們轉回10進位制就不對了,這裡有一個問題就是我們呼叫了兩次hashCode方法,第一次呼叫的時候就修改了物件的hash引數的值,第二次是在此基礎上進行運算的,所以出現了上述的錯誤,我在上一篇部落格中也出現了這樣的錯誤(現在已修改),所以,Person類的hashCode方法應該這樣定義:

@Override     public int hashCode() {         // TODO Auto-generated method stub         int hash=1;         hash = hash * 3 + this.age;         hash = hash * 3 + this.name.hashCode();         hash = hash * 3 + this.sex.hashCode();         return hash;     }

同時,刪除Person類中的hash成員變數即可,這個時候我們再次測試:

現在就沒有任何問題了。

上面也說了,重寫toString沒有什麼約束條約,所以可以隨便定義,但是我們必須要明確其用途和意義,這裡,我們就可以效仿Java平臺類庫了,比如File:

   /**      * Returns the pathname string of this abstract pathname.  This is just the      * string returned by the <code>{@link #getPath}</code> method.      *      * @return  The string form of this abstract pathname      */     public String toString() {         return getPath();     }

比如Calender:

 /**      * Return a string representation of this calendar. This method      * is intended to be used only for debugging purposes, and the      * format of the returned string may vary between implementations.      * The returned string may be empty but may not be <code>null</code>.      *      * @return  a string representation of this calendar.      */     @Override     public String toString() {         // NOTE: BuddhistCalendar.toString() interprets the string         // produced by this method so that the Gregorian year number         // is substituted by its B.E. year value. It relies on         // "...,YEAR=<year>,..." or "...,YEAR=?,...".         StringBuilder buffer = new StringBuilder(800);         buffer.append(getClass().getName()).append('[');         appendValue(buffer, "time", isTimeSet, time);         buffer.append(",areFieldsSet=").append(areFieldsSet);         buffer.append(",areAllFieldsSet=").append(areAllFieldsSet);         buffer.append(",lenient=").append(lenient);         buffer.append(",zone=").append(zone);         appendValue(buffer, ",firstDayOfWeek", true, (long) firstDayOfWeek);         appendValue(buffer, ",minimalDaysInFirstWeek", true, (long) minimalDaysInFirstWeek);         for (int i = 0; i < FIELD_COUNT; ++i) {             buffer.append(',');             appendValue(buffer, FIELD_NAME[i], isSet(i), (long) fields[i]);         }         buffer.append(']');         return buffer.toString();     }

可見只要新增好toString方法備註,無論是繼承還是擴充套件都明白你輸出的內容即可。到這裡,關於toString的介紹就結束了。