本文是我们名为“ 高级Java ”的学院课程的一部分。
 本课程旨在帮助您最有效地使用Java。 它讨论了高级主题,包括对象创建,并发,序列化,反射等。 它将指导您完成Java掌握的过程! 在这里查看 ! 
目录
-  1.简介 2.方法等于和hashCode 3.方法toString 4.方法克隆 5.方法等于和==运算符 6.有用的助手类 7.下载源代码 8.接下来 
1.简介
 从本教程的第1部分“ 如何创建和销毁对象”中 ,我们已经知道Java是一种面向对象的语言(但是,不是纯粹的面向对象的语言)。 在Java类层次结构的顶部是Object类,Java中的每个类都隐式地继承自它。 因此,所有类都继承Object类中声明的方法集,最重要的是以下方法: 
| 方法 | 描述 | 
| protected Object clone() | 创建并返回此对象的副本。 | 
| protected void finalize() | 当垃圾回收确定不再有对该对象的引用时,由垃圾回收器在对象上调用。 我们在本教程的第1部分“ 如何创建和销毁对象”中讨论了终结器。 | 
| boolean equals(Object obj) | 指示其他某个对象是否与此对象“相等”。 | 
| int hashCode() | 返回对象的哈希码值。 | 
| String toString() | 返回对象的字符串表示形式。 | 
| void notify() | 唤醒正在此对象的监视器上等待的单个线程。 我们将在本教程的第9部分 并发最佳实践中讨论此方法。 | 
| void notifyAll() | 唤醒正在此对象的监视器上等待的所有线程。 我们将在本教程的第9部分 并发最佳实践中讨论此方法。 | 
| void wait()    | 使当前线程等待,直到另一个线程为此对象调用 notify()方法或notifyAll()方法。 我们将在本教程的第9部分 并发最佳实践中讨论这些方法。 | 
表格1
 在本教程的这一部分中,我们将研究equals , hashCode , toString和clone方法,它们的用法以及需要牢记的重要约束。 
2.方法等于和hashCode
 默认情况下,Java中的任何两个对象引用(或类实例引用)仅在它们引用相同的内存位置(引用相等)时才相等。 但是Java允许类通过重写Object类的equals()方法来定义自己的相等性规则。 听起来像一个强大的概念,但是正确的equals()方法实现应符合一组规则并满足以下约束: 
- 反身的 。 对象x必须等于自身,并且equals(x)必须返回true 。
- 对称的 。 如果equals(y)返回true,则y.equals(x)也必须返回true 。
- 传递的 。 如果equals(y)返回true,而y.equals(z)返回true ,则x.equals(z)也必须返回true 。
- 一致 。 除非修改用于相等比较的任何属性,否则多次调用equals()方法必须得到相同的值。
- 等于Null 。 equals(null)的结果必须始终为false 。
 不幸的是,Java编译器无法在编译过程中强制执行这些约束。 但是,不遵循这些规则可能会导致非常怪异且难以解决问题。 一般建议是这样的:如果您要编写自己的equals()方法实现,请在实际需要时三思而后行。 现在,有了所有这些规则,让我们为Person类编写一个equals()方法的简单实现。 
package com.javacodegeeks.advanced.objects;public class Person {private final String firstName;private final String lastName;private final String email;public Person( final String firstName, final String lastName, final String email ) {this.firstName = firstName;this.lastName = lastName;this.email = email;}public String getEmail() {return email;}public String getFirstName() {return firstName;}public String getLastName() {return lastName;}// Step 0: Please add the @Override annotation, it will ensure that your// intention is to change the default implementation.@Overridepublic boolean equals( Object obj ) {// Step 1: Check if the 'obj' is nullif ( obj == null ) {return false;}// Step 2: Check if the 'obj' is pointing to the this instanceif ( this == obj ) {return true;}// Step 3: Check classes equality. Note of caution here: please do not use the // 'instanceof' operator unless class is declared as final. It may cause // an issues within class hierarchies.if ( getClass() != obj.getClass() ) {return false;}// Step 4: Check individual fields equalityfinal Person other = (Person) obj;if ( email == null ) {if ( other.email != null ) {return false;} } else if( !email.equals( other.email ) ) {return false;}if ( firstName == null ) {if ( other.firstName != null ) {return false;} } else if ( !firstName.equals( other.firstName ) ) {return false;}if ( lastName == null ) {if ( other.lastName != null ) {return false;}} else if ( !lastName.equals( other.lastName ) ) {return false;}return true;}        
} 并非偶然的是,本节的标题中还包含hashCode()方法。 最后要记住的规则是:每当您重写equals()方法时,也始终要重写hashCode()方法。 如果对于任何两个对象, equals()方法返回true ,则这两个对象中的每个对象的hashCode()方法必须返回相同的整数值(但是相反的语句不那么严格:如果对于任何两个对象, equals()方法返回false ,则这两个对象中的每个对象的hashCode()方法都可能会或可能不会返回相同的整数值)。 让我们看一下Person类的hashCode()方法。 
// Please add the @Override annotation, it will ensure that your
// intention is to change the default implementation.
@Override
public int hashCode() {final int prime = 31;int result = 1;result = prime * result + ( ( email == null ) ? 0 : email.hashCode() );result = prime * result + ( ( firstName == null ) ? 0 : firstName.hashCode() );result = prime * result + ( ( lastName == null ) ? 0 : lastName.hashCode() );return result;
} 为了避免意外,请尽可能在实现equals()和hashCode()同时尝试使用final字段。 这将确保这些方法的行为不会受到字段更改的影响(但是,在实际项目中,并非总是可能的)。 
 最后,始终确保在equals()和hashCode()方法的实现中使用相同的字段。 如果有任何更改影响所讨论的字段,它将保证两种方法的行为一致。 
3.方法toString
 toString()可能是其他方法中最有趣的方法,并且被更频繁地覆盖。 它的目的是提供对象(类实例)的字符串表示形式。 正确编写的toString()方法可以大大简化实际系统中问题的调试和故障排除。 
 默认的toString()实现在大多数情况下不是很有用,只是返回完整的类名和对象哈希码,以@ ,fe分隔: 
com.javacodegeeks.advanced.objects.Person@6104e2ee 让我们尝试改善实现,并为我们的Person类示例重写toString()方法。 这是使toString()更有用的一种方法。 
// Please add the @Override annotation, it will ensure that your
// intention is to change the default implementation.
@Override
public String toString() {return String.format( "%s[email=%s, first name=%s, last name=%s]", getClass().getSimpleName(), email, firstName, lastName );
} 现在, toString()方法提供了Person类实例的字符串版本,包括其所有字段。 例如,在执行以下代码片段时: 
final Person person = new Person( "John", "Smith", "john.smith@domain.com" );
System.out.println( person.toString() );以下输出将在控制台中打印出来:
Person[email=john.smith@domain.com, first name=John, last name=Smith] 不幸的是,标准Java库对简化toString()方法实现的支持有限,尤其是,最有用的方法是Objects.toString() , Arrays.toString() / Arrays.deepToString() 。 让我们看一下Office类及其可能的toString()实现。 
package com.javacodegeeks.advanced.objects;import java.util.Arrays;public class Office {private Person[] persons;public Office( Person ... persons ) {this.persons = Arrays.copyOf( persons, persons.length );}@Overridepublic String toString() {return String.format( "%s{persons=%s}", getClass().getSimpleName(), Arrays.toString( persons ) );}public Person[] getPersons() {return persons;}
} 以下输出将在控制台中打印出来(如我们所见, Person类实例也已正确转换为字符串): 
Office{persons=[Person[email=john.smith@domain.com, first name=John, last name=Smith]]} Java社区已经开发了两个非常全面的库,这些库在很大程度上toString()实现的工作。 其中包括Google Guava's Objects.toStringHelper和Apache Commons Lang ToStringBuilder 。 
4.方法克隆
 如果Java中有一种声誉不佳的方法,则肯定是clone() 。 它的目的很明确–返回正在调用的类实例的确切副本,但是有很多原因使它不像听起来那么容易。 
 首先,如果您决定实现自己的clone()方法,则可以遵循Java文档中规定的许多约定。 其次,该方法在Object类中声明为protected ,因此为了使其可见,应使用重写类本身的返回类型将其重写为public 。 第三,重写的类应实现Cloneable接口(这只是一个没有定义方法的标记或mixin接口),否则将引发CloneNotSupportedException异常。 最后,实现应首先调用super.clone() ,然后在需要时执行其他操作。 让我们看看如何为我们的示例Person类实现它。 
public class Person implements Cloneable {// Please add the @Override annotation, it will ensure that your// intention is to change the default implementation.@Overridepublic Person clone() throws CloneNotSupportedException {return ( Person )super.clone();}
} 该实现看起来非常简单明了,那么这里可能出什么毛病? 几件事情,实际上。 在执行类实例的克隆时,不会调用任何类构造函数。 这种行为的结果是可能会出现意外的数据共享。 让我们考虑上一节介绍的Office类的以下示例: 
package com.javacodegeeks.advanced.objects;import java.util.Arrays;public class Office implements Cloneable {private Person[] persons;public Office( Person ... persons ) {this.persons = Arrays.copyOf( persons, persons.length );}@Overridepublic Office clone() throws CloneNotSupportedException {return ( Office )super.clone();}public Person[] getPersons() {return persons;}
} 在此实现中, Office类实例的所有克隆都将共享同一个人数组,这不太可能实现所需的行为。 为了使clone()实现能够做正确的事情,应该做一些工作。 
@Override
public Office clone() throws CloneNotSupportedException {final Office clone = ( Office )super.clone();clone.persons = persons.clone();return clone;
} 现在看起来更好,但是即使此实现也非常脆弱,因为将人员字段定为final将导致相同的数据共享问题(因为不能重新分配final )。 
 总的来说,如果您想精确复制类,最好避免使用clone() / Cloneable并使用更简单的替代方法(例如,复制构造函数,具有C ++背景的开发人员熟悉的概念或工厂)方法,这是我们在本教程的第1部分“ 如何创建和销毁对象”中讨论的一种有用的构造模式。 
5.方法等于和==运算符
 Java ==运算符和equals()方法之间存在有趣的关系,这会引起很多问题和混乱。 在大多数情况下(比较原始类型除外), ==运算符执行引用相等:如果两个引用都指向同一个对象,则返回true,否则返回false 。 让我们看一个说明差异的简单示例: 
final String str1 = new String( "bbb" );
System.out.println( "Using == operator: " + ( str1 == "bbb" ) );
System.out.println( "Using equals() method: " + str1.equals( "bbb" ) );从人类的角度来看,str1 ==“ bbb”和str1.equals(“ bbb”)之间没有区别:在两种情况下,结果都应该相同,因为str1只是对“ bbb”字符串的引用。 但是在Java中并非如此:
Using == operator: false
Using equals() method: true 即使两个字符串看起来完全相同,在此特定示例中,它们也作为两个不同的字符串实例存在。 根据经验,如果您处理对象引用,请始终使用equals()或Objects.equals() (请参阅下一部分有用的帮助程序类以获取更多详细信息)进行相等性比较,除非您确实有比较的意图如果对象引用指向同一实例。 
6.有用的助手类
 自Java 7发行以来,标准Java库附带了几个非常有用的帮助程序类。 其中之一是Objects类。 特别是,以下三种方法可以大大简化您自己的equals()和hashCode()方法的实现。 
| 方法 | 描述 | 
| static boolean equals(Object a, Object b) | 如果参数彼此相等,则返回true;否则返回false 。 | 
| static int hash(Object... values) | 为一系列输入值生成哈希码。 | 
| static int hashCode(Object o) | 返回非空参数的哈希码,对于空参数返回0。 | 
表2
 如果我们使用这些帮助器方法为Person的类示例重写equals()和hashCode()方法,则代码量将大大减少,并且代码的可读性也将大大提高。 
@Override
public boolean equals( Object obj ) {if ( obj == null ) {return false;}if ( this == obj ) {return true;}if ( getClass() != obj.getClass() ) {return false;}final PersonObjects other = (PersonObjects) obj;if( !Objects.equals( email, other.email ) ) {return false;} else if( !Objects.equals( firstName, other.firstName ) ) {return false;            } else if( !Objects.equals( lastName, other.lastName ) ) {return false;            }return true;
}@Override
public int hashCode() {return Objects.hash( email, firstName, lastName );
}7.下载源代码
- 您可以在此处下载源代码: advanced-java-part-2
8.接下来
 在本节中,我们介绍了Object类,它是Java中面向对象编程的基础。 我们已经看到了每个类如何覆盖从Object类继承的方法并强加其自己的相等性规则。 在下一节中,我们将切换编码方式,并讨论如何正确设计类和接口。 
翻译自: https://www.javacodegeeks.com/2015/09/using-methods-common-to-all-objects.html