Как напечатать мой Java-объект, не получая "SomeType@2f92e0f4"?

У меня есть класс, определенный следующим образом:

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

Что означает этот вывод? Как мне изменить этот вывод, чтобы он содержал имя моего человека? И как мне распечатать коллекции моих предметов?

Примечание: это подразумевается как канонический вопрос и ответ на этот вопрос.

15 ответов

Решение

Фон

Все объекты 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 - название класса, то есть класс MyType в упаковке com.foo,
  • @ - соединяет строку
  • 2f92e0f4 хеш-код объекта.

Названия классов массивов выглядят немного иначе, что хорошо объясняется в Javadocs для Class.getName(), Например, [Ljava.lang.String средства:

  • [ - одномерный массив (в отличие от [[ или же [[[ так далее.)
  • L - массив содержит класс или интерфейс
  • java.lang.String - тип объектов в массиве

Настройка вывода

Чтобы напечатать что-то другое, когда вы звоните System.out.println(myObject), вы должны переопределить toString() метод в вашем собственном классе. Вот простой пример:

public class Person {

  private String name;

  // constructors and other methods omitted

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

Теперь, если мы напечатаем Person мы видим их имя, а не com.foo.Person@12345678,

Имейте в виду, что toString() это всего лишь один из способов преобразования объекта в строку. Обычно этот вывод должен полностью описывать ваш объект в четкой и сжатой форме. Лучше toString() за наших Person класс может быть:

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

Который напечатал бы, например, Person[name=Henry], Это действительно полезная часть данных для отладки / тестирования.

Если вы хотите сосредоточиться только на одном аспекте вашего объекта или включить много джазового форматирования, вам может быть лучше вместо этого определить отдельный метод, например String toElegantReport() {...},


Автогенерация вывода

Многие IDE предлагают поддержку для автоматического создания toString() метод, основанный на полях в классе. См., Например, документы по Eclipse и IntelliJ.

Несколько популярных библиотек Java также предлагают эту функцию. Вот некоторые примеры:


Печать групп объектов

Итак, вы создали хороший toString() для вашего класса. Что произойдет, если этот класс будет помещен в массив или коллекцию?

Массивы

Если у вас есть массив объектов, вы можете вызвать Arrays.toString() произвести простое представление содержимого массива. Например, рассмотрим этот массив Person объекты:

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

// Prints: [Fred, Mike]

Примечание: это вызов статического метода с именем toString() в классе Arrays, который отличается от того, что мы обсуждали выше.

Если у вас есть многомерный массив, вы можете использовать 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() как обсуждалось выше.

Я думаю, что Apache предоставляет лучший класс утилит, который предоставляет функцию для получения строки

ReflectionToStringBuilder.toString(object)

Каждый класс в 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);
    }
}

В Eclipse перейдите в свой класс, щелкните правой кнопкой мыши ->source->Generate. toString();

Это переопределит toString() метод и напечатает объект этого класса.

Я предпочитаю использовать служебную функцию, которая использует 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);
    }
}

По умолчанию каждый объект в 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); он напечатает имя человека вместо имени класса и хэш-кода.

Во втором случае, когда вы пытаетесь напечатать массив, он печатает [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);

Могут быть и другие способы. Надеюсь это поможет.:)

В intellij вы можете автоматически сгенерировать метод toString, нажав alt+inset, а затем выбрав 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 +
            '}';
 }
}

Как вы можете видеть, он генерирует String путем объединения нескольких атрибутов класса, для примитивов он будет печатать их значения, а для ссылочных типов он будет использовать их тип класса (в данном случае для строкового метода Test2).

Если вы напрямую напечатать какой-либо объект лица, он будет 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());
  }
}

Для «глубокого» toString()есть альтернатива ответам на основе JSON (Jackson, GSON и т. д.): ReflectionToStringBuilder из библиотеки Apache Commons Lang 3 с RecursiveToStringStyle или 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
  ]
]

Мне удалось это сделать, используя Jackson весной 5. В зависимости от объекта Джексон может работать не во всех случаях.

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

ObjectMapper mapper = new ObjectMapper();
Staff staff = createStaff();
// pretty print
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(staff);
System.out.println("-------------------------------------------------------------------")
System.out.println(json);
System.out.println("-------------------------------------------------------------------")

вывод будет примерно таким

-------------------------------------------------------------------
{
  "id" : 1,
  "internalStaffId" : "1",
  "staffCms" : 1,
  "createdAt" : "1",
  "updatedAt" : "1",
  "staffTypeChange" : null,
  "staffOccupationStatus" : null,
  "staffNote" : null
}
-------------------------------------------------------------------

Больше примеров с использованием Jackson Вот

Ты можешь попробовать GSONтакже. Должно получиться примерно так:

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

Использование аннотации Lombok @Data для класса предоставит геттер, сеттер, toString и хэш-код. Использование Lombok лучше, так как оно обрабатывает шаблонный код.

Если вы посмотрите на класс Object (родительский класс всех классов в Java), реализация метода toString()

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

всякий раз, когда вы печатаете какой-либо объект в Java, вызывается toString(). Теперь вам решать, если вы переопределите toString(), тогда ваш метод вызовет вызов метода другого класса Object.

Если вы используете проект 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)

Если вы не хотите переопределятьtoString()метод, который вы можете использовать,

          public static void printJson(Object o) {
        ObjectMapper mapper = new ObjectMapper();
        try {
            String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(o);
            System.out.println(json);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Нам также необходимо импортировать,import com.fasterxml.jackson.databind.ObjectMapper;

Мы можем создать метод Utils, и это будет очень удобно.

Arrays.deepToString(arrayOfObject)

Над функцией вывести массив объектов разных примитивов.

[[AAAAA, BBBBB], [6, 12], [2003-04-01 00:00:00.0, 2003-10-01 00:00:00.0], [2003-09-30 00:00:00.0, 2004-03-31 00:00:00.0], [Interim, Interim], [2003-09-30, 2004-03-31]];
Другие вопросы по тегам