1. 概述
在 Java 中,所有类都直接或间接继承自 Object
类,因此任何对象都可以调用 toString()
方法来获取其字符串表示形式。
本文将带你了解 toString()
的默认行为,并教你如何根据实际需要对其进行重写,使输出更具可读性和调试价值。
2. 默认行为
当我们打印一个对象引用时,实际上是在调用该对象的 toString()
方法。如果我们没有显式地在类中定义这个方法,那么就会使用 Object
类提供的默认实现:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
举个例子,我们定义一个简单的 Customer
类(未重写 toString()
):
public class Customer {
private String firstName;
private String lastName;
// 标准 getter 和 setter,无 toString()
}
当我们打印这个类的实例时,输出会类似这样:
com.baeldung.tostring.Customer@6d06d69c
✅ 结论:默认的 toString()
只输出类名和哈希码,对调试和日志记录几乎没有帮助。
3. 重写默认行为
上面的输出显然不够直观。我们更关心的是对象内部的字段值,而不是它的内存地址。
通过重写 toString()
方法,我们可以自定义对象的字符串表示形式,使其更具有业务含义和调试价值。
接下来我们通过几个常见场景来演示如何重写 toString()
。
4. 处理基本类型和字符串字段
假设我们的 Customer
对象包含基本类型和字符串字段,比如余额(balance):
public class CustomerPrimitiveToString extends Customer {
private long balance;
@Override
public String toString() {
return "Customer [balance=" + balance + ", getFirstName()=" + getFirstName()
+ ", getLastName()=" + getLastName() + "]";
}
}
单元测试如下:
@Test
public void givenPrimitive_whenToString_thenCustomerDetails() {
CustomerPrimitiveToString customer = new CustomerPrimitiveToString();
customer.setFirstName("Rajesh");
customer.setLastName("Bhojwani");
customer.setBalance(110);
assertEquals("Customer [balance=110, getFirstName()=Rajesh, getLastName()=Bhojwani]",
customer.toString());
}
✅ 输出结果更直观,便于调试。
5. 处理复杂对象字段
如果 Customer
包含一个复杂对象,比如 Order
,我们不仅要重写 Customer
的 toString()
,也要确保 Order
有良好的字符串表示。
public class CustomerComplexObjectToString extends Customer {
private Order order;
@Override
public String toString() {
return "Customer [order=" + order + ", getFirstName()=" + getFirstName()
+ ", getLastName()=" + getLastName() + "]";
}
}
Order
类也需要重写:
public class Order {
private String orderId;
private String desc;
private long value;
private String status;
@Override
public String toString() {
return "Order [orderId=" + orderId + ", desc=" + desc + ", value=" + value + "]";
}
}
测试代码:
@Test
public void givenComplex_whenToString_thenCustomerDetails() {
CustomerComplexObjectToString customer = new CustomerComplexObjectToString();
customer.setFirstName("Rajesh");
customer.setLastName("Bhojwani");
Order order = new Order();
order.setOrderId("A1111");
order.setDesc("Game");
order.setStatus("In-Shipping");
customer.setOrder(order);
assertEquals("Customer [order=Order [orderId=A1111, desc=Game, value=0], " +
"getFirstName()=Rajesh, getLastName()=Bhojwani]", customer.toString());
}
⚠️ 注意:如果 Order
没有重写 toString()
,输出会变成 Order@<hashcode>
,毫无意义。
6. 处理对象数组
如果 Customer
中包含一个 Order
数组,直接打印会输出类似 [Lcom.baeldung.Order;@hashcode
这样的内容。
为了解决这个问题,我们可以使用 Arrays.toString()
:
public class CustomerArrayToString extends Customer {
private Order[] orders;
@Override
public String toString() {
return "Customer [orders=" + Arrays.toString(orders)
+ ", getFirstName()=" + getFirstName()
+ ", getLastName()=" + getLastName() + "]";
}
}
测试代码:
@Test
public void givenArray_whenToString_thenCustomerDetails() {
CustomerArrayToString customer = new CustomerArrayToString();
customer.setFirstName("Rajesh");
customer.setLastName("Bhojwani");
Order order = new Order();
order.setOrderId("A1111");
order.setDesc("Game");
order.setValue(0);
customer.setOrders(new Order[] { order });
assertEquals("Customer [orders=[Order [orderId=A1111, desc=Game, value=0]], " +
"getFirstName()=Rajesh, getLastName()=Bhojwani]", customer.toString());
}
✅ 使用 Arrays.toString()
可以优雅地展示数组内容。
7. 处理包装类、集合和 StringBuffer
对于以下类型的字段:
- 包装类(如
Integer
,Long
) - 集合类(如
List
,Map
) StringBuffer
/StringBuilder
这些类已经重写了 toString()
,所以我们无需手动处理:
public class CustomerWrapperCollectionToString extends Customer {
private Integer score;
private List<String> items;
private StringBuffer fullname;
@Override
public String toString() {
return "Customer [score=" + score + ", items=" + items + ", fullname=" + fullname
+ ", getFirstName()=" + getFirstName() + ", getLastName()=" + getLastName() + "]";
}
}
测试代码:
@Test
public void givenWrapperCollectionStrBuffer_whenToString_thenCustomerDetails() {
CustomerWrapperCollectionToString customer = new CustomerWrapperCollectionToString();
customer.setFirstName("Rajesh");
customer.setLastName("Bhojwani");
customer.setScore(8);
customer.setItems(Arrays.asList("Book", "Pen"));
StringBuffer fullname = new StringBuffer();
fullname.append("Bhojwani, Rajesh");
customer.setFullname(fullname);
assertEquals("Customer [score=8, items=[Book, Pen], fullname=Bhojwani, Rajesh, getFirstName()=Rajesh, "
+ "getLastName()=Bhojwani]", customer.toString());
}
✅ 这些类型可以直接使用,无需额外处理。
8. 总结
在这篇文章中,我们探讨了 Java 中 toString()
方法的默认行为及其重写的最佳实践。
- ✅ 重写
toString()
是一种良好的编程习惯,有助于调试和日志输出 - ✅ 对于复杂对象、数组、集合等,要特别注意其字符串表示方式
- ⚠️ 如果不重写,调试时输出的信息几乎无用
记住一句话:
toString()
不只是给程序员看的,更是给未来的自己看的。写得好,调试省一半时间。