使用所有对象共有的方法
本文是我們名為“ 高級(jí)Java ”的學(xué)院課程的一部分。
本課程旨在幫助您最有效地使用Java。 它討論了高級(jí)主題,包括對(duì)象創(chuàng)建,并發(fā),序列化,反射等。 它將指導(dǎo)您完成Java掌握的旅程! 在這里查看 !
目錄
1.簡(jiǎn)介 2.方法equals和hashCode 3.方法toString 4.方法克隆 5.方法等于和==運(yùn)算符 6.有用的助手類 7.下載源代碼 8.接下來(lái)1.簡(jiǎn)介
從本教程的第1部分“ 如何創(chuàng)建和銷毀對(duì)象”中 ,我們已經(jīng)知道Java是一種面向?qū)ο蟮恼Z(yǔ)言(但是,不是純粹的面向?qū)ο蟮恼Z(yǔ)言)。 在Java類層次結(jié)構(gòu)的頂部是Object類,Java中的每個(gè)類都隱式地繼承自它。 因此,所有類都繼承Object類中聲明的方法集,最重要的是以下方法:
| 方法 | 描述 |
| protected Object clone() | 創(chuàng)建并返回此對(duì)象的副本。 |
| protected void finalize() | 當(dāng)垃圾回收確定不再有對(duì)該對(duì)象的引用時(shí),由垃圾回收器在對(duì)象上調(diào)用。 我們已經(jīng)在本教程的第1部分“ 如何創(chuàng)建和銷毀對(duì)象”中討論了終結(jié)器。 |
| boolean equals(Object obj) | 指示其他某個(gè)對(duì)象是否與此對(duì)象“相等”。 |
| int hashCode() | 返回對(duì)象的哈希碼值。 |
| String toString() | 返回對(duì)象的字符串表示形式。 |
| void notify() | 喚醒正在此對(duì)象的監(jiān)視器上等待的單個(gè)線程。 我們將在本教程的第9部分 并發(fā)最佳實(shí)踐中討論此方法。 |
| void notifyAll() | 喚醒正在此對(duì)象的監(jiān)視器上等待的所有線程。 我們將在本教程的第9部分 并發(fā)最佳實(shí)踐中討論此方法。 |
| void wait() void wait(long timeout) void wait(long timeout, int nanos) | 使當(dāng)前線程等待,直到另一個(gè)線程為此對(duì)象調(diào)用notify()方法或notifyAll()方法。 我們將在本教程的第9部分 并發(fā)最佳實(shí)踐中討論這些方法。 |
表格1
在本教程的這一部分中,我們將介紹equals , hashCode , toString和clone方法,它們的用法以及需要牢記的重要約束。
2.方法equals和hashCode
默認(rèn)情況下,Java中的任何兩個(gè)對(duì)象引用(或類實(shí)例引用)僅在它們引用相同的內(nèi)存位置時(shí)才相等(引用相等)。 但是Java允許類通過(guò)重寫(xiě)Object類的equals()方法來(lái)定義自己的相等性規(guī)則。 聽(tīng)起來(lái)像一個(gè)強(qiáng)大的概念,但是正確的equals()方法實(shí)現(xiàn)應(yīng)符合一組規(guī)則并滿足以下約束:
- 反身的 。 對(duì)象x必須等于自身,并且equals(x)必須返回true 。
- 對(duì)稱的 。 如果equals(y)返回true,則y.equals(x)也必須返回true 。
- 傳遞的 。 如果equals(y)返回true,而y.equals(z)返回true ,則x.equals(z)也必須返回true 。
- 一致 。 除非修改用于相等比較的任何屬性,否則多次調(diào)用equals()方法必須得到相同的值。
- 等于Null 。 equals(null)的結(jié)果必須始終為false 。
不幸的是,Java編譯器無(wú)法在編譯過(guò)程中強(qiáng)制執(zhí)行這些約束。 但是,不遵循這些規(guī)則可能會(huì)導(dǎo)致非常怪異且難以解決問(wèn)題。 一般建議是這樣的:如果您打算編寫(xiě)自己的equals()方法實(shí)現(xiàn),請(qǐng)?jiān)趯?shí)際需要時(shí)三思而后行。 現(xiàn)在,有了所有這些規(guī)則,讓我們?yōu)镻erson類編寫(xiě)一個(gè)equals()方法的簡(jiǎn)單實(shí)現(xiàn)。
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;} }并非偶然的是,本節(jié)的標(biāo)題中還包含hashCode()方法。 最后要記住的規(guī)則是:每當(dāng)您重寫(xiě)equals()方法時(shí),也始終要重寫(xiě)hashCode()方法。 如果對(duì)于任何兩個(gè)對(duì)象, equals()方法返回true ,則這兩個(gè)對(duì)象中的每個(gè)對(duì)象的hashCode()方法必須返回相同的整數(shù)值(但是相反的語(yǔ)句不那么嚴(yán)格:如果對(duì)于任何兩個(gè)對(duì)象, equals()方法返回false ,則這兩個(gè)對(duì)象中的每個(gè)對(duì)象的hashCode()方法都可能會(huì)或可能不會(huì)返回相同的整數(shù)值)。 讓我們看一下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; }為了避免意外,請(qǐng)盡可能在實(shí)現(xiàn)equals()和hashCode()同時(shí)嘗試使用final字段。 這將確保這些方法的行為不會(huì)受到字段更改的影響(但是,在實(shí)際項(xiàng)目中,并非總是可能的)。
最后,始終確保在equals()和hashCode()方法的實(shí)現(xiàn)中使用相同的字段。 如果有任何變化影響所討論的字段,它將保證兩種方法的行為一致。
3.方法toString
toString()可以說(shuō)是其他方法中最有趣的方法,并且被更頻繁地覆蓋。 它的目的是提供對(duì)象(類實(shí)例)的字符串表示形式。 正確編寫(xiě)的toString()方法可以大大簡(jiǎn)化實(shí)際系統(tǒng)中問(wèn)題的調(diào)試和故障排除。
默認(rèn)的toString()實(shí)現(xiàn)在大多數(shù)情況下不是很有用,只是返回完整的類名和對(duì)象哈希碼,以@ ,fe分隔:
com.javacodegeeks.advanced.objects.Person@6104e2ee讓我們嘗試改善實(shí)現(xiàn),并為我們的Person類示例重寫(xiě)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 ); }現(xiàn)在, toString()方法提供了Person類實(shí)例的字符串版本,包括其所有字段。 例如,在執(zhí)行以下代碼片段時(shí):
final Person person = new Person( "John", "Smith", "john.smith@domain.com" ); System.out.println( person.toString() );以下輸出將在控制臺(tái)中打印出:
Person[email=john.smith@domain.com, first name=John, last name=Smith]不幸的是,標(biāo)準(zhǔn)Java庫(kù)對(duì)簡(jiǎn)化toString()方法實(shí)現(xiàn)的支持有限,特別是,最有用的方法是Objects.toString() , Arrays.toString() / Arrays.deepToString() 。 讓我們看一下Office類及其可能的toString()實(shí)現(xiàn)。
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;} }以下輸出將在控制臺(tái)中打印出來(lái)(如我們所見(jiàn), Person類實(shí)例也已正確轉(zhuǎn)換為字符串):
Office{persons=[Person[email=john.smith@domain.com, first name=John, last name=Smith]]}Java社區(qū)已經(jīng)開(kāi)發(fā)了幾個(gè)非常全面的庫(kù),這些庫(kù)在很大程度上toString()實(shí)現(xiàn)的工作。 其中包括Google Guava's Objects.toStringHelper和Apache Commons Lang ToStringBuilder 。
4.方法克隆
如果Java中有一種聲譽(yù)不佳的方法,則肯定是clone() 。 它的目的非常明確–返回正在調(diào)用的類實(shí)例的確切副本,但是有很多原因使它不像聽(tīng)起來(lái)那樣簡(jiǎn)單。
首先,如果您決定實(shí)現(xiàn)自己的clone()方法,則可以遵循Java文檔中規(guī)定的許多約定。 其次,該方法在Object類中聲明為protected ,因此為了使其可見(jiàn),應(yīng)使用重寫(xiě)類本身的返回類型將其重寫(xiě)為public 。 第三,覆蓋的類應(yīng)實(shí)現(xiàn)Cloneable接口(這只是一個(gè)未定義方法的標(biāo)記或mixin接口),否則將引發(fā)CloneNotSupportedException異常。 最后,實(shí)現(xiàn)應(yīng)首先調(diào)用super.clone() ,然后在需要時(shí)執(zhí)行其他操作。 讓我們看看如何為示例Person類實(shí)現(xiàn)它。
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();} }該實(shí)現(xiàn)看起來(lái)非常簡(jiǎn)單明了,那么這里可能出什么毛病? 實(shí)際上,有兩件事。 在執(zhí)行類實(shí)例的克隆時(shí),不會(huì)調(diào)用任何類構(gòu)造函數(shù)。 這種行為的結(jié)果是可能會(huì)出現(xiàn)意外的數(shù)據(jù)共享。 讓我們考慮上一節(jié)介紹的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;} }在此實(shí)現(xiàn)中, Office類實(shí)例的所有克隆都將共享同一個(gè)人數(shù)組,這不太可能實(shí)現(xiàn)所需的行為。 為了使clone()實(shí)現(xiàn)能夠做正確的事情,應(yīng)該做一些工作。
@Override public Office clone() throws CloneNotSupportedException {final Office clone = ( Office )super.clone();clone.persons = persons.clone();return clone; }現(xiàn)在看起來(lái)更好,但是即使此實(shí)現(xiàn)也非常脆弱,因?yàn)閷⑷藛T字段定為final將導(dǎo)致相同的數(shù)據(jù)共享問(wèn)題(因?yàn)椴荒苤匦路峙鋐inal )。
總的來(lái)說(shuō),如果您想精確復(fù)制類,最好避免使用clone() / Cloneable并使用更簡(jiǎn)單的替代方法(例如,復(fù)制構(gòu)造函數(shù),具有C ++背景的開(kāi)發(fā)人員熟悉的概念或工廠)方法,這是我們?cè)诒窘坛痰?strong>第1部分“ 如何創(chuàng)建和銷毀對(duì)象”中討論的一種有用的構(gòu)造模式。
5.方法等于和==運(yùn)算符
Java ==運(yùn)算符和equals()方法之間存在有趣的關(guān)系,這會(huì)引起很多問(wèn)題和混亂。 在大多數(shù)情況下(比較原始類型除外), ==運(yùn)算符執(zhí)行引用相等:如果兩個(gè)引用都指向同一個(gè)對(duì)象,則返回true,否則返回false 。 讓我們看一個(gè)說(shuō)明差異的簡(jiǎn)單示例:
final String str1 = new String( "bbb" ); System.out.println( "Using == operator: " + ( str1 == "bbb" ) ); System.out.println( "Using equals() method: " + str1.equals( "bbb" ) );從人類的角度來(lái)看,str1 ==“ bbb”和str1.equals(“ bbb”)之間沒(méi)有區(qū)別:在兩種情況下,結(jié)果都應(yīng)該相同,因?yàn)閟tr1只是對(duì)“ bbb”字符串的引用。 但是在Java中并非如此:
Using == operator: false Using equals() method: true即使兩個(gè)字符串看起來(lái)完全相同,在此特定示例中,它們也作為兩個(gè)不同的字符串實(shí)例存在。 根據(jù)經(jīng)驗(yàn),如果您處理對(duì)象引用,請(qǐng)始終使用equals()或Objects.equals() (請(qǐng)參閱下一部分有用的幫助程序類以獲取更多詳細(xì)信息)進(jìn)行相等性比較,除非您確實(shí)有比較的意圖如果對(duì)象引用指向同一實(shí)例。
6.有用的助手類
自Java 7發(fā)行以來(lái),標(biāo)準(zhǔn)Java庫(kù)附帶了幾個(gè)非常有用的幫助程序類。 其中之一是Objects類。 特別地,以下三種方法可以大大簡(jiǎn)化您自己的equals()和hashCode()方法的實(shí)現(xiàn)。
| 方法 | 描述 |
| static boolean equals(Object a, Object b) | 如果參數(shù)彼此相等,則返回true,否則返回false 。 |
| static int hash(Object... values) | 為一系列輸入值生成哈希碼。 |
| static int hashCode(Object o) | 返回非空參數(shù)的哈希碼,對(duì)于空參數(shù)返回0。 |
表2
如果我們使用這些輔助方法為Person的類示例重寫(xiě)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.接下來(lái)
在本節(jié)中,我們介紹了Object類,它是Java中面向?qū)ο缶幊痰幕A(chǔ)。 我們已經(jīng)看到了每個(gè)類如何重寫(xiě)從Object類繼承的方法并強(qiáng)加其自己的相等性規(guī)則。 在下一節(jié)中,我們將轉(zhuǎn)換編碼方式,并討論如何正確設(shè)計(jì)類和接口。
翻譯自: https://www.javacodegeeks.com/2015/09/using-methods-common-to-all-objects.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的使用所有对象共有的方法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 抽象类和接口设计_如何设计类和接口
- 下一篇: ejb能调用另一个ejb吗_异步EJB只