如何在Java中打印对象而不出现“SomeType@2f92e0f4”的情况?

410

我有一个定义如下的类:

public class Person {
  private String name;

  // constructor and getter/setter omitted
}

我尝试打印我的类的实例:

System.out.println(myPerson);

但是我得到了以下输出:com.foo.Person@2f92e0f4

当我尝试打印一个Person对象的数组时,发生了类似的事情:

Person[] people = //...
System.out.println(people); 

我得到了输出:[Lcom.foo.Person;@28a418fc

这个输出是什么意思?我如何更改输出,使其包含我的人员姓名?我如何打印我的对象集合?

注意:这被认为是关于此主题的规范问答。


3
你可以使用GSON库将对象转换为JSON,反之亦然。非常有用于调试。 - Ashish Rawat
请参考 https://dev59.com/cIbca4cB1Zd3GeqPU1-9 - Raedwald
14个回答

512

背景

所有Java对象都有一个toString()方法,当您尝试打印该对象时会调用此方法。

System.out.println(myObject);  // invokes myObject.toString()

此方法定义在Object类中(所有Java对象的超类)。Object.toString()方法返回一个相当丑陋的字符串,由类名、@符号和对象的十六进制哈希码组成。代码如下:
// Code of Object.toString()
public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

因此,像com.foo.MyType@2f92e0f4这样的结果可以解释为:
  • com.foo.MyType - 类的名称,即在包com.foo中的类是MyType
  • @ - 将字符串连接在一起
  • 2f92e0f4 - 对象的哈希码。

数组类的名称看起来有些不同,在Class.getName()的Javadocs中有很好的解释。例如,[Ljava.lang.String表示:

  • [ - 一个单维数组(与[[[[[等相对)
  • L - 数组包含一个类或接口
  • java.lang.String - 数组中对象的类型

自定义输出

如果你想在调用System.out.println(myObject)时输出不同的内容,你需要重写你自己类中的toString()方法。以下是一个简单的例子:

override
public class Person {

  private String name;
  
  // constructors and other methods omitted
  
  @Override
  public String toString() {
    return name;
  }
}

现在,如果我们打印一个Person,我们将看到他们的名字,而不是com.foo.Person@12345678
请记住,toString()只是将对象转换为字符串的一种方式。通常,此输出应以清晰简洁的方式完全描述您的对象。对于我们的Person类,更好的toString()可能是:
@Override
public String toString() {
  return getClass().getSimpleName() + "[name=" + name + "]";
}

这将打印出例如Person[name=Henry]的信息,对于调试/测试非常有用。

如果您只想关注对象的某个方面或包含很多时髦的格式设置,则最好定义一个单独的方法,例如String toElegantReport() {...}


自动生成输出

许多 IDEs 都提供了自动生成 toString() 方法的支持,基于类中的字段。例如,可以查看 EclipseIntelliJ 的文档。

一些流行的 Java 库也提供了此功能。例如:


打印对象组

所以,您已经为类创建了一个好的 toString() 方法。如果将该类放入数组或集合中会发生什么?

数组

如果您有一个对象数组,则可以调用 Arrays.toString() 以产生数组内容的简单表示。例如,考虑这个 Person 对象数组:

Person[] people = { new Person("Fred"), new Person("Mike") };
System.out.println(Arrays.toString(people));

// Prints: [Fred, Mike]

注意:这是对Arrays类中名为toString()静态方法的调用,与我们上面讨论的内容不同。

如果您有一个多维数组,您可以使用Arrays.deepToString()来实现相同类型的输出。

集合

大多数集合将基于对每个元素调用.toString()产生漂亮的输出。

List<Person> people = new ArrayList<>();
people.add(new Person("Alice"));
people.add(new Person("Bob"));    
System.out.println(people);

// Prints [Alice, Bob]

所以你只需要确保你的列表元素定义了一个好的 toString(),如上所述。


91

我认为Apache提供了一个更好的Util类,其中包含一个获取字符串的函数。

ReflectionToStringBuilder.toString(object)

11
这样做的好处是不需要编辑类,有时这是不可能的。但是,我该如何递归打印嵌套对象呢? - lukas84
5
@lukas84 ReflectionToStringBuilder.toString(input, new RecursiveToStringStyle()); 这会将嵌套对象一并打印出来。 - Viraj Nimbalkar
@lukas84 如果类不能被编辑并且声明为final,那么这肯定很有用。否则,您可以扩展它并实现自定义的toString()方法。 - Amine

40

Java中每个类默认都有toString()方法,如果你将该类的某个对象传递给System.out.println()方法,则会调用toString()方法。默认情况下,该方法返回该对象的className@hashcode。

{
    SomeClass sc = new SomeClass();
    // Class @ followed by hashcode of object in Hexadecimal
    System.out.println(sc);
}

您可以重写类的toString方法以获得不同的输出。请参见此示例

class A {
    String s = "I am just a object";
    @Override
    public String toString()
    {
        return s;
    }
}

class B {
    public static void main(String args[])
    {
        A obj = new A();
        System.out.println(obj);
    }
}

4
这是一个简洁明了的答案。但为了澄清为什么 OP 得到 [Lcom.foo.Person;@28a418fc 作为输出,需要说明一下:这是 toString() 方法的输出结果之一,但是它是在运行时为类型 Person[] 生成的类中实现的,而不是 Person 类本身(参见 https://dev59.com/0Woy5IYBdhLWcg3wcNrh#8546532)。 - gvlasov

18

在Eclipse中,

进入你的类,

右键->source->Generate toString();

它将覆盖toString()方法并打印该类的对象。


14

我更喜欢使用一个工具函数,该函数使用GSON将Java对象反序列化为JSON字符串。

/**
 * This class provides basic/common functionalities to be applied on Java Objects.
 */
public final class ObjectUtils {

    private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();

    private ObjectUtils() {
         throw new UnsupportedOperationException("Instantiation of this class is not permitted in case you are using reflection.");
    }

    /**
     * This method is responsible for de-serializing the Java Object into Json String.
     *
     * @param object Object to be de-serialized.
     * @return String
     */
    public static String deserializeObjectToString(final Object object) {
        return GSON.toJson(object);
    }
}

7

默认情况下,Java中的每个对象都有toString()方法,它输出ObjectType@HashCode。

如果你想要更有意义的信息,那么你需要在你的类中覆盖toString()方法。

public class Person {
  private String name;

  // constructor and getter/setter omitted

  // overridding toString() to print name
  public String toString(){
     return name;  
  }
}

现在,当你使用System.out.prtinln(personObj);打印person对象时,它会打印出该人的名字而不是类名和哈希码。
在第二种情况下,当您尝试打印数组时,它会打印出[Lcom.foo.Person;@28a418fc数组类型及其哈希码。
如果你想打印人的名字,有许多方式。你可以编写一个自己的函数来迭代每个人并打印。
void printPersonArray(Person[] persons){
    for(Person person: persons){
        System.out.println(person);
    }
}

你可以使用Arrays.toString()将其打印出来。在我看来,这是最简单的方法。
 System.out.println(Arrays.toString(persons));
 System.out.println(Arrays.deepToString(persons));  // for nested arrays  

你可以使用Java 8的方式打印它(使用流和方法引用)。
 Arrays.stream(persons).forEach(System.out::println);

可能还有其他方法。希望这可以帮到你。 :)


7
在Intellij中,您可以通过按下alt + insert然后选择toString()自动生成toString方法。这是一个测试类的输出:
public class test  {
int a;
char b;
String c;
Test2 test2;

@Override
public String toString() {
    return "test{" +
            "a=" + a +
            ", b=" + b +
            ", c='" + c + '\'' +
            ", test2=" + test2 +
            '}';
 }
}

从代码中可以看出,它通过连接类的几个属性来生成一个字符串,对于原始类型,它将打印它们的值,对于引用类型,它将使用它们的类类型(在此示例中为Test2的toString方法)。


5
如果您直接打印Person对象,代码会输出ClassName@HashCode。在您的情况下,会打印com.foo.Person@2f92e0f4。其中Person是对象所属的类,2f92e0f4是对象的hashCode。
public class Person {
  private String name;

  public Person(String name){
  this.name = name;
  }
  // getter/setter omitted

   @override
   public String toString(){
        return name;
   }
}

现在,如果您尝试使用Person对象,它将打印名称。
Class Test
 {
  public static void main(String... args){
    Person obj = new Person("YourName");
    System.out.println(obj.toString());
  }
}

4

在进行“深层次”toString()时,有一种替代基于JSON的答案(Jackson,GSON等)的方法:Apache Commons Lang 3库中的ReflectionToStringBuilder,使用RecursiveToStringStyleMultilineRecursiveToStringStyle。代码示例:

System.out.println("My object: " +
    ReflectionToStringBuilder.toString(theObject, new RecursiveToStringStyle()));

输出示例:

// RecursiveToStringStyle
Person@7f54[name=Stephen,age=29,smoker=false,job=Job@43cd2[title=Manager]]

// MultilineRecursiveToStringStyle
Person@7f54[
  name=Stephen,
  age=29,
  smoker=false,
  job=Job@43cd2[
    title=Manager
  ]
]

3
如果您查看Object类(Java中所有类的父类),toString()方法的实现如下:
    public String toString() {
       return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

每当你在Java中打印任何对象时,都会调用toString()。现在由你决定是否重写toString()方法,那么你的方法将调用其他Object类方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接