使用所有对象共有的方法
本文是我們名為“ 高級Java ”的學院課程的一部分。
本課程旨在幫助您最有效地使用Java。 它討論了高級主題,包括對象創建,并發,序列化,反射等。 它將指導您完成Java掌握的旅程! 在這里查看 !
目錄
1.簡介 2.方法equals和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() void wait(long timeout) void wait(long timeout, int nanos) | 使當前線程等待,直到另一個線程為此對象調用notify()方法或notifyAll()方法。 我們將在本教程的第9部分 并發最佳實踐中討論這些方法。 |
表格1
在本教程的這一部分中,我們將介紹equals , hashCode , toString和clone方法,它們的用法以及需要牢記的重要約束。
2.方法equals和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
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的使用所有对象共有的方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 抽象类和接口设计_如何设计类和接口
- 下一篇: 安卓微信助手插件(安卓微信助手)