如何在没有得到 "SomeType@2f92e0f4" 的情况下打印我的 Java 对象?

How do I print my Java object without getting "SomeType@2f92e0f4"?

我有一个class定义如下:

public class Person {
  private String name;

  // constructor and getter/setter omitted
}

我试图打印我的实例 class:

System.out.println(myPerson);

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

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

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

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

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

注意:这是关于这个主题的规范问答。

背景

所有 Java object 都有一个 toString() 方法,当您尝试打印 object.

时调用该方法
System.out.println(myObject);  // invokes myObject.toString()

这个方法定义在object的Object class (the superclass of all Java objects). The Object.toString() method returns a fairly ugly looking string, composed of the name of the class, an @ symbol and the hashcode中的十六进制。代码如下:

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

结果如com.foo.MyType@2f92e0f4因此可以解释为:

  • com.foo.MyType - class 的名称,即 class 在包 com.foo.
  • 中是 MyType
  • @ - 将字符串连接在一起[=13​​2=]
  • 2f92e0f4 object.
  • 的哈希码

数组的名称 classes 看起来有点不同,Class.getName() 的 Java 文档对此有很好的解释。例如,[Ljava.lang.String 表示:

  • [ - single-dimensional 数组(相对于 [[[[[ 等)
  • L - 数组包含一个 class 或接口
  • java.lang.String - 数组
  • 中object的类型

自定义输出

要在您调用 System.out.println(myObject) 时打印不同的内容,您必须 override 您自己的 class 方法中的 toString() 方法。这是一个简单的例子:

public class Person {

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

现在如果我们打印 Person,我们会看到他们的名字而不是 com.foo.Person@12345678

请记住,toString() 只是将 object 转换为字符串的 一种 方式。通常,此输出应以清晰简洁的方式完整描述您的 object。对于我们的 Person class 更好的 toString() 可能是:

@Override
public String toString() {
  return getClass().getSimpleName() + "[name=" + name + "]";
}

这将打印,例如 Person[name=Henry]。这对 debugging/testing.

来说是非常有用的数据

如果您只想专注于 object 的一个方面或包含很多时髦的格式,您最好定义一个单独的方法,例如String toElegantReport() {...}.


Auto-generating 输出

很多IDEs offer support for auto-generating a toString() method, based on the fields in the class. See docs for Eclipse and IntelliJ,例如

几个流行的 Java 库也提供此功能。一些示例包括:


打印组 objects

您已经为您的 class 创建了一个不错的 toString()。如果将 class 放入数组或 collection 中会发生什么?

数组

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

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

// Prints: [Fred, Mike]

注意:这是对数组 class 中名为 toString()static 方法的调用,这与我们一直在讨论的不同以上。

如果你有一个 multi-dimensional 数组,你可以使用 Arrays.deepToString() 来实现同样的输出。

Collections

大多数 collection 会根据对每个元素调用 .toString() 生成漂亮的输出。

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

// Prints [Alice, Bob]

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

Java 中的每个 class 默认情况下都有 toString() 方法,如果您将那个 class 的某个对象传递给 [=13=,则会调用该方法].默认情况下,此调用 returns 该对象的 className@hashcode。

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

您可以覆盖 class 的 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);
    }
}

我认为 apache 提供了一个更好的 util class 它提供了一个函数来获取字符串

ReflectionToStringBuilder.toString(object)

在 Eclipse 中, 转到您的class, 右击->源->生成toString();

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

如果您直接打印 Person 的任何对象,它将 ClassName@HashCode 写入代码。

在您的案例中 com.foo.Person@2f92e0f4 正在打印。其中Person是对象所属的class,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());
  }
}

在 intellij 中,您可以通过按 alt+inset 然后选择 toString() 来自动生成 toString 方法,这里是用于测试的输出 class:

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 +
            '}';
 }
}

如您所见,它通过连接 class 的几个属性生成一个字符串,对于基元,它将打印它们的值,对于引用类型,它将使用它们的 class 类型(在本例为 Test2 的字符串方法)。

如果您查看对象 class(Java 中所有 class 的父对象 class),则 toString() 方法实现是

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

每当您打印 Java 中的任何对象时,都会调用 toString()。现在由您决定是否覆盖 toString() 然后您的方法将调用其他对象 class 方法调用。

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

如果您想要更多有意义的信息,那么您需要覆盖 class 中的 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); 打印人物对象时,它将打印人物的姓名,而不是 class 姓名和哈希码。

在第二种情况下,当您尝试打印数组时,它会打印 [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);

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

我更喜欢使用实用函数,它使用 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);
    }
}

我设法在 Spring 中使用 Jackson 完成了这项工作 5. 根据对象的不同,它可能并非在所有情况下都有效。

import com.fasterxml.jackson.databind.ObjectMapper;
    
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(yourObject));

输出看起来像

{
  "id" : 1,
  "fieldOne" : "string"
}

Here 是使用 Jackson

的更多示例

如果你使用 GSON 它可能看起来像

Gson gson = new Gson();
System.out.println(gson.toJson(yourObject));

对于“深度”toString(),有一个替代基于 JSON 的答案(Jackson、GSON 等):ReflectionToStringBuilder from the Apache Commons Lang 3 library, with RecursiveToStringStyle or MultilineRecursiveToStringStyle。代码示例:

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
  ]
]

如果您正在使用项目 Lombok,您可以使用 @ToString 注释并生成标准 toString() 方法,而无需添加样板文件。

import lombok.ToString;

@ToString
public class LoginDto {
  private String user;
  private String pass;
}
...
System.out.println(loginDto.toString());
// LoginDto(user=x@xxx.x, pass=xxxxx)

在 class 上使用 Lombok @Data 注释将提供 getter、setter、toString 和哈希码。使用 Lombok 更好,因为它可以处理样板代码。