【问题标题】:How do I print my Java object without getting "SomeType@2f92e0f4"?如何在不获取“SomeType@2f92e0f4”的情况下打印我的 Java 对象?
【发布时间】:2015-05-22 07:40:58
【问题描述】:

我有一个类定义如下:

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

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

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

【问题讨论】:

标签: java string object tostring


【解决方案1】:

背景

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

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

此方法在Object 类(所有Java 对象的超类)中定义。 Object.toString() 方法返回一个看起来相当难看的字符串,由类名、@ 符号和对象的十六进制 hashcode 组成。代码如下:

// 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) 时打印不同的内容,您必须在自己的类中使用 override toString() 方法。这是一个简单的例子:

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(),如上所述。

【讨论】:

    【解决方案2】:

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

    ReflectionToStringBuilder.toString(object)
    

    【讨论】:

    • 这样做的好处是不需要编辑类,这有时是不可能的。但是,我怎样才能递归地打印嵌套对象呢?
    • 完美!这正是我们所需要的。谢谢!
    • @lukas84 ReflectionToStringBuilder.toString(input, new RecursiveToStringStyle());这也将打印嵌套对象
    【解决方案3】:

    默认情况下,Java 中的每个类都有toString() 方法,如果您将该类的某个对象传递给System.out.println(),就会调用该方法。默认情况下,此调用返回该对象的 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);
        }
    }
    

    【讨论】:

    • 这是一个恰当而简短的答案,但要澄清为什么 OP 将 [Lcom.foo.Person;@28a418fc 作为输出:这也是 toString() 方法的输出,但在在运行时为 Person[] 类型而不是 Person 生成的类(请参阅 stackoverflow.com/a/8546532/1542343)。
    【解决方案4】:

    在 Eclipse 中, 去你的课, 右键->源码->生成toString()

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

        在 intellij 中,您可以通过按 alt+inset 然后选择 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 的字符串方法)。

        【讨论】:

          【解决方案7】:

          默认情况下,Java 中的每个 Object 都有 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); 打印人员对象时,它将打印人员的姓名而不是类名和哈希码。

          在第二种情况下,当您尝试打印数组时,它会打印[Lcom.foo.Person;@28a418fc Array 类型及其哈希码。


          如果要打印人名,有很多方法。

          您可以编写自己的函数来迭代每个人并打印

          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);
          

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

          【讨论】:

            【解决方案8】:

            如果您直接打印 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());
              }
            }
            

            【讨论】:

              【解决方案9】:

              如果你查看 Object 类(Java 中所有类的父类),那么 toString() 方法的实现是

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

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

              【讨论】:

                【解决方案10】:

                对于“深度”toString(),可以使用基于 JSON 的答案(Jackson、GSON 等)的替代方法:ReflectionToStringBuilder 来自Apache Commons Lang 3 库,带有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
                  ]
                ]
                

                【讨论】:

                  【解决方案11】:

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

                  【讨论】:

                    【解决方案12】:

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

                    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());
                    

                    【讨论】:

                      【解决方案13】:

                      如果您正在使用项目 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)
                      

                      【讨论】:

                        猜你喜欢
                        相关资源
                        最近更新 更多