Я уже писал в блоге на удобном Apache Commons ToStringBuilder, и меня недавно спросили, что представляет собой загадочный текст, который появляется в сгенерированном выводе String. Коллега, задававший вопрос, правильно предположил, что он рассматривал хеш-код, но он не соответствовал хеш-коду его экземпляра. Я объяснил, что ToStringBuilder добавляет к выводу хеш-код идентичности в шестнадцатеричном формате. В этой статье я более подробно рассмотрю использование ToStringBuilder хеш-кода идентификатора, представленного в шестнадцатеричном формате. Даже те, кто не использует ToStringBuilder, могут найти эту информацию полезной в качестве стандартного Java Object.toString ()также использует шестнадцатеричное представление того, что фактически является его хэш-кодом идентичности .
Я начну с очень простого примера Java с использованием ToStringBuilder. В этом примере используются три Java-класса (Person.java, Employee.java и Main.java), которые показаны далее.
Person.java
package dustin.examples; import org.apache.commons.lang.builder.ToStringBuilder; /** * A simple representation of a Person intended only to demonstrate Apache * Commons ToStringBuilder. * * @author Dustin */ public class Person { /** Person's last name (surname). */ protected final String lastName; /** Person's first name. */ protected final String firstName; /** * Parameterized constructor for obtaining an instance of Person. * * @param newLastName Last name of new Person instance. * @param newFirstName First name of new Person instance. */ public Person(final String newLastName, final String newFirstName) { this.lastName = newLastName; this.firstName = newFirstName; } /** * Provide String representation of this Person instance. * @return My String representation. */ @Override public String toString() { final ToStringBuilder builder = new ToStringBuilder(this); builder.append("First Name", this.firstName); builder.append("Last Name", this.lastName); return builder.toString(); } }
Employee.java
package dustin.examples; import java.util.Objects; import org.apache.commons.lang.builder.ToStringBuilder; /** * Simple class intended to demonstrate ToStringBuilder. * * @author Dustin */ public class Employee extends Person { /** Employee ID. */ private final String employeeId; /** * Parameterized constructor for obtaining an instance of Employee. * * @param newLastName Last name of the employee. * @param newFirstName First name of the employee. * @param newId Employee's employee ID. */ public Employee( final String newLastName, final String newFirstName, final String newId) { super(newLastName, newFirstName); this.employeeId = newId; } /** * Provide String representation of me. * * @return My String representation. */ @Override public String toString() { final ToStringBuilder builder = new ToStringBuilder(this); builder.appendSuper(super.toString()); builder.append("Employee ID", this.employeeId); return builder.toString(); } /** * Simple object equality comparison method. * * @param obj Object to be compared to me for equality. * @return {@code true} if the provided object and I are considered equal. */ @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Employee other = (Employee) obj; if (!Objects.equals(this.employeeId, other.employeeId)) { return false; } return true; } /** * Hash code for this instance. * * @return My hash code. */ @Override public int hashCode() { int hash = 3; hash = 19 * hash + Objects.hashCode(this.employeeId); return hash; } }
Main.java (Версия 1)
package dustin.examples; import static java.lang.System.out; /** * Simple class enabling demonstration of ToStringBuilder. * * @author Dustin */ public class Main { /** * Main function for running Java examples with ToStringBuilder. * * @param args the command line arguments */ public static void main(String[] args) { final Person person = new Person("Washington", "Willow"); out.println(person); final Employee employee = new Employee("Lazentroph", "Frank", "56"); out.println(employee); } }
Приведенный выше пример прост и его вывод показан ниже:
Вывод, изображенный выше, показывает строку String, напечатанную для вывода обоих экземпляров, сгенерированного ToStringBuilder. Представление String экземпляра класса Person включает строку «1f5d386», а представление String экземпляра класса Employee содержит строку «1c9b9ca». Эти строки являются шестнадцатеричным представлением хеш-кода каждого объекта.
Строки «1f5d386» и «1c9b9ca» не похожи на целочисленные хэш-коды, которые многие из нас привыкли видеть из-за их шестнадцатеричного представления. В Integer.toHexString (INT) метода [доступная , так как JDK 1.0.2] это удобный метод для печати целого числа в шестнадцатеричном формате и может быть использован для преобразования «нормальной» хэш — коды , чтобы увидеть , если они совпадают, генерируемое ToStringBuilder. Я добавил вызовы к этому методу в хэш-кодах экземпляров в новой версии класса Main.
Main.java (версия 2)
package dustin.examples; import static java.lang.System.out; /** * Simple class enabling demonstration of ToStringBuilder. * * @author Dustin */ public class Main { /** * Main function for running Java examples with ToStringBuilder. * * @param args the command line arguments */ public static void main(String[] args) { final Person person = new Person("Washington", "Willow"); out.println(person); out.println("\tHash Code (ten): " + person.hashCode()); out.println("\tHash Code (hex): " + Integer.toHexString(person.hashCode())); final Employee employee = new Employee("Lazentroph", "Frank", "56"); out.println(employee); out.println("\tHash Code (ten): " + employee.hashCode()); out.println("\tHash Code (hex): " + Integer.toHexString(employee.hashCode())); } }
Выполнение вышеизложенного приводит к следующему выводу:
Как показывают выходные данные, шестнадцатеричное представление хеш-кода для экземпляра Person действительно совпадает с показанным в строке, сгенерированной ToStringBuilder для этого экземпляра. Однако этого нельзя сказать о экземпляре Employee. Разница в том, что класс Person не переопределяет метод hashCode () и поэтому использует хеш-код идентификатора по умолчанию, в то время как класс Employee переопределяет свой собственный hashCode () (и, следовательно, отличается от хэш-кода идентификатора).
Третья версия Main выводит хэш-код идентификатора с использованием System.identityHashCode (Object) [более подробно обсуждается в моем блоге Java System.identityHashCode ].
Main.java (Версия 3)
package dustin.examples; import static java.lang.System.out; /** * Simple class enabling demonstration of ToStringBuilder. * * @author Dustin */ public class Main { /** * Main function for running Java examples with ToStringBuilder. * * @param args the command line arguments */ public static void main(String[] args) { final Person person = new Person("Washington", "Willow"); out.println(person); out.println("\tHash Code (ten): " + person.hashCode()); out.println("\tHash Code (hex): " + Integer.toHexString(person.hashCode())); out.println("\t\tIdentity Hash (ten): " + System.identityHashCode(person)); out.println("\t\tIdentity Hash (hex): " + Integer.toHexString(System.identityHashCode(person))); final Employee employee = new Employee("Lazentroph", "Frank", "56"); out.println(employee); out.println("\tHash Code (ten): " + employee.hashCode()); out.println("\tHash Code (hex): " + Integer.toHexString(employee.hashCode())); out.println("\t\tIdentity Hash (ten): " + System.identityHashCode(employee)); out.println("\t\tIdentity Hash (hex): " + Integer.toHexString(System.identityHashCode(employee))); }
Теперь мы можем сравнить хэш-код идентификатора со строкой, сгенерированной ToStringBuilder.
Последний пример окончательно демонстрирует, что ToStringBuilder включает шестнадцатеричное представление хеш-кода идентификатора системы в свой сгенерированный вывод. Если кто-то хочет использовать шестнадцатеричное представление переопределенного хеш-кода, а не хеш-кода идентификатора, можно использовать экземпляр ToStringStyle (обычно это экземпляр StandardToStringStyle ), а метод setUseIdentityHashCode (boolean) можно вызвать с параметром false. Этот экземпляр ToStringStyle затем может быть передан методу ToStringBuilder.setDefaultStyle (ToStringStyle) .
Примечательно, что методы equals (Object) и hashCode () в классе Employee, показанном выше, были автоматически созданы NetBeans 7.1 . Я был рад видеть, что с моей исходной версией Java для этого проекта, определенной как JDK 1.7 , это автоматическое создание этих двух методов использовало преимущества класса Objects .
В этом посте я использовал вывод, сгенерированный ToStringBuilder, чтобы облегчить обсуждение шестнадцатеричных представлений идентификационных хеш-кодов, но я мог бы просто использовать собственную встроенную реализацию JDK «default» Object.toString () для той же цели. На самом деле, Javadoc даже рекламирует это:
Метод toString для класса Object возвращает строку, состоящую из имени класса, экземпляром которого является объект, символа знака «@» и шестнадцатеричного представления без знака хеш-кода объекта. Другими словами, этот метод возвращает строку, равную значению:
getClass (). getName () + ‘@’ + Integer.toHexString (hashCode ())
Единственная причина, по которой я не использовал этот пример для начала, заключается в том, что я почти всегда переопределяю метод toString () в своих классах и не получаю эту реализацию «по умолчанию». Однако, когда я использую ToStringBuilder для реализации своих переопределенных методов toString (), я вижу эти шестнадцатеричные представления. Я могу уменьшить использование ToStringBuilder по мере увеличения использования Objects.toString () .
Многие из нас не думают о шестнадцатеричных представлениях или хэш-кодах идентичности в нашей повседневной работе на Java. В этом сообщении в блоге я использовал вывод ToStringBuilder в качестве предлога для того, чтобы взглянуть немного ближе на эти две концепции. Попутно я также кратко рассмотрел метод Integer.toHexString (Object), который полезен для печати чисел в их шестнадцатеричном представлении. Знание о поддержке Java шестнадцатеричного представления важно, потому что оно отображается в выводе toString () , при маркировке цветов , адресов памяти и в других местах.