在 Java 类中定义 toString 方法(学习 Java 编程语言 050)

在 Object 中还有一个重要的 toString 方法,它会返回表示对象值的一个字符串。下面是一个典型的例子。Point 类的 toString方法将返回下面这样的字符串:
java.awt.Point[x=10,y=20]

绝大多数(但不是全部)的 toString 方法都遵循这样的格式:类的名字,随后是一对方括号括起来的字段值。下面是 Employee 类中的 toString 方法的实现:

public class Employee {
    private String name;
    private double salary;
    private LocalDate hireDay;

    ...
    @Override
    public String toString() {
        return "Employee[name=" + name +
            ",salary=" + salary +
            ",hireDay=" + hireDay + "]";
    }
}

实际上,还可以设计得更好一些。最好通过调用 getClass().getName() 获得类名的字符串,而不要将类名硬编码写到 toString 方法中。

public class Employee {
    private String name;
    private double salary;
    private LocalDate hireDay;

    ...
    @Override
    public String toString() {
        return getClass().getName() + "[name=" + name +
                ",salary=" + salary +
                ",hireDay=" + hireDay + "]";
    }
}

这样的 toString 方法也可以由子类调用。

设计子类的程序员应该定义自己的 toString 方法,并加入子类的字段。如果超类使用了 getClass().getName(),那么子类只要调用 super.toString() 就可以了。例如,下面是 Manager 类中的 toString 方法:

public class Manager extends Employee {
    private double bonus;

    ...
    @Override
    public String toString() {
        return super.toString() + 
            "[bonus=" + bonus + "]";
    }
}

现在,Manager 对象将打印输出如下所示的内容:
Manager[name=...,salary=...,hireDay=...] [bonus=...]

随处可见 toString 方法的主要原因是:只要对象与一个字符串通过操作符 “+” 连接起来,Java 编译器就会自动地调用 toString 方法来获得这个对象的字符串描述。

提示: 可以不写 x.toString(),而写作 "" + x。这条语句将一个空串与 x 的字符串表示(也就是 x.toString())相连接。与 toString 不同的是,即使 x 是基本类型,这条语句照样能够执行。

如果 x 是一个任意对象,并调用
System.out.println(x)
println 方法就会简单地调用 x.toString(),并打印输出得到的字符串。

Object 类定义了 toString 方法,可以打印对象的类名和散列码。例如,调用:
System.out.println(System.out);
将输出:
java.io.PrintStream@7852e922
得到这样的结果原因是 PrintStream 类的设计者没有覆盖 toString() 方法。

警告: 令人烦恼的是,数组继承了 Object 类的 toString 方法,更有甚者,数组类型将采用一种古老的格式打印。例如:
int[] luckyNumger = {2, 3, 5, 7, 11, 13}
String s = "" + luckyNumger;
会生成字符串 “[I@52af6cff”(前缀 [I 表明是一个整型数组)。补救的方法是调用静态方法 Arrays.toString。代码:
String s = Arrays.toString(luckyNumger);
将生成字符串:
[2, 3, 5, 7, 11, 13]
要想打印多维数组(即,数组的数组),则需要调用 Arrays.deepToString 方法。

toString 方法是一种非常有用的调试工具。在标准类库中,许多类都定义了 toString 方法,以便用户能够获得一些有关对象状态的有用信息。

像下面这样显示日志信息非常有益:
System.out.println("Current position = " + position);

提示: 强烈建议为自定义的每一个类增加 toString 方法。这样做不仅自己受益,所有使用这个类的程序员也会从这个日志记录支持中受益匪浅。

java.long.Object 1.0

  • Class getClass()

    返回包含对象信息的类对象。

  • boolean equals(Object otherObject)

    比较两个对象是否相等,如果两个对象指向同一块存储区域,方法返回 true;否则方法返回 false。要在自定义的类中覆盖这个方法。

  • String toString()

    返回表示对象值的字符串。要在自定义的类中覆盖这个方法。

java.lang.Class 1.0

  • String getName()

    返回这个类的名字。

  • Class getSuperclass()

    以 Class 对象的形式返回这个类的超类信息。

示例代码

import java.time.LocalDate;
import java.util.Objects;
/**
 * 
 * @author bpx
 * @version 1.0.0
 * @since 1.7.1
 * 
 * 
 */
public class Employee {
    // 姓名
    private String name;
    // 薪资
    private double salary;
    // 认知日期
    private LocalDate hireDay;

    /**
     * Employee 构造方法
     * @param name 员工姓名
     * @param salary 员工工资
     * @param year 入职年
     * @param month 入职月
     * @param day 入职日
     */
    public Employee(String name, double salary, int year, int month, int day) {
        Objects.requireNonNull(name, "The name cannot be null");
        this.name = name;
        this.salary = salary;
        hireDay = LocalDate.of(year, month, day);
    }

    /**
     * 获得员工姓名
     * @return 用户姓名
     */
    public String getName() {
        return name;
    }
    /**
     * 获得员工工资
     * @return 工资
     */
    public double getSalary() {
        return salary;
    }
    /**
     * 获得用户入职日期
     * @return 入职日期
     * @throws NullPointerException
     */
    public LocalDate getHireDay() {
        return hireDay;
    }
    /**
     * 增加工资
     * @param byPercent 百分比
     */
    public void raiseSalary(double byPercent) {
        double raise = salary * byPercent / 100;
        salary += raise;
    }

    @Override
    public boolean equals(Object otherObject)
    {
        // a quick test to see if the objects are identical
        if (this == otherObject) return true;

        // must return false if the explicit parameter is null
        if (otherObject == null) return false;

        // if the classes don't match, they can't be equal
        if (getClass() != otherObject.getClass())
            return false;
        
        // now we know otherObject is a non-null Employee
        Employee other = (Employee) otherObject;

        // test whether the fields have identical values
        return Objects.equals(name, other.name)
                && salary == other.salary
                && Objects.equals(hireDay, other.hireDay);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, salary, hireDay);
    }
    @Override
    public String toString() {
        String str = "%s[name=%s, salary=%.2f, hireDay=%s]";
        return String.format(str, getClass().getName(), name, salary, hireDay);
    }
}
import java.util.Objects;

public class Manager extends Employee {

    private double bonus;

    public Manager(String name, double salary, int year, int month, int day) {
        super(name, salary, year, month, day);
        this.bonus = 0;
    }

    public void setBonus(double bonus) {
        this.bonus = bonus;
    }

    public double getSalary() {
        double baseSalary = super.getSalary();
        return baseSalary + bonus;
    }

    @Override
    public boolean equals(Object otherObject)
    {
        if (!super.equals(otherObject)) return false;
        Manager other = (Manager) otherObject;
        // super.equals checked that this and otherObject belong to the same class
        return bonus == other.bonus;
    }

    @Override
    public int hashCode() {
        return Objects.hash(super.hashCode(), bonus);
    }
 
    @Override
    public String toString() {
        return super.toString() + 
            "[bonus=" + bonus + "]";
    }
}
public class Testing {

    public static void main(String[] args) {
        Employee alicel = new Employee("Alice Adams" , 75000 , 1987, 12, 15);
        Employee alice2 = alicel;
        Employee alice3 = new Employee("Alice Adams", 75000, 1987, 12, 15);
        Employee bob = new Employee("Bob Brandson" , 50000, 1989, 10, 1);
        
        System.out.println("alicel == alice2: " + (alicel == alice2));        
        System.out.println("alicel == alice3: " + (alicel == alice3)) ;        
        System.out.println("alicel.equals(alice3): " + alicel.equals(alice3));        
        System.out.println("alicel.equals(bob): " + alicel.equals(bob)) ;        
        System.out.println("bob.toString(): " + bob);
        
        Manager carl = new Manager("Carl Cracker", 80000 , 1987, 12, 15) ;
        Manager boss = new Manager("Carl Cracker", 80000 , 1987, 12, 15) ;
        boss.setBonus(5000);
        
        System.out.println("boss.toString(): " + boss);
        System.out.println("carl.equals(boss): " + carl.equals(boss)) ;
        System.out.println("alicel.hashCode(): " + alicel.hashCode()) ;
        System.out.println("alice3.hashCode(): " + alice3.hashCode()) ;
        System.out.println("bob.hashCode(): " + bob.hashCode());
        System.out.println("carl.hashCode(): " + carl.hashCode());
    }
}

输出:

alicel == alice2: true
alicel == alice3: false
alicel.equals(alice3): true
alicel.equals(bob): false
bob.toString(): com.xiang117.corejava.Employee[name=Bob Brandson, salary=50000.00, hireDay=1989-10-01]
boss.toString(): com.xiang117.corejava.Manager[name=Carl Cracker, salary=80000.00, hireDay=1987-12-15][bonus=5000.0]
carl.equals(boss): false
alicel.hashCode(): -808853550
alice3.hashCode(): -808853550
bob.hashCode(): -624019882
carl.hashCode(): -2004699436
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容