成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Java進(jìn)階2 —— 使用Object的通用方法

jzman / 3434人閱讀

摘要:判斷另外一個對象是否與當(dāng)前對象相等返回當(dāng)前對象的哈希值返回一個表示當(dāng)前對象的字符串喚醒一個等待當(dāng)前對象的鎖監(jiān)視器的線程。

原文鏈接:http://www.javacodegeeks.com/2015/09/using-methods-common-to-all-objects.html

本文是Java進(jìn)階課程的第二篇。

本課程的目標(biāo)是幫你更有效的使用Java。其中討論了一些高級主題,包括對象的創(chuàng)建、并發(fā)、序列化、反射以及其他高級特性。本課程將為你的精通Java的旅程提供幫助。

內(nèi)容提綱

引言

equals和hashCode方法

toString方法

clone方法

equals方法與"=="操作符

有用的幫助類

源碼下載

下章概要

1. 引言

從前面一篇對象的創(chuàng)建與銷毀中,我們知道Java是一種面向?qū)ο缶幊陶Z言(盡管不是純粹的面向?qū)ο?。Java類層次結(jié)構(gòu)的頂層是Object類,所有的其他類都隱式的繼承于它。因此,所有的類也都從Object中繼承了方法,其中最重要的幾個方法如下表:

方法 描述
protected Object clone() 創(chuàng)建并返回當(dāng)前對象的一份拷貝
protected void finalize() 當(dāng)垃圾回收器判斷出該對象不再被引用時,就會調(diào)用finalize()方法。在對象的創(chuàng)建與銷毀中有對finalizers的介紹。
boolean equals(Object obj) 判斷另外一個對象是否與當(dāng)前對象相等
int hasCode() 返回當(dāng)前對象的哈希值
String toString() 返回一個表示當(dāng)前對象的字符串
void notify() 喚醒一個等待當(dāng)前對象的鎖監(jiān)視器的線程。我們將會在第9篇文章并發(fā)最佳實(shí)踐中詳細(xì)介紹此方法
void notifyAll() 喚醒所有等待當(dāng)前對象的鎖監(jiān)視器的線程。我們將會在第9篇文章并發(fā)最佳實(shí)踐中詳細(xì)介紹此方法
void wait()
void wait(long timeout)
void wait(long timeout, int nanos)
使當(dāng)前線程進(jìn)入等待狀態(tài)直到其他線程調(diào)用了當(dāng)前對象的notify()notifyAll()方法。我們將會在第9篇文章并發(fā)最佳實(shí)踐中詳細(xì)介紹此方法

表1

在本篇文章中我們將重點(diǎn)介紹equalshashCode、toStringclone方法。通過本章節(jié)的學(xué)習(xí),需要對這幾個方法的用法及重要的使用限制了然于胸。

2. equlas和hashCode方法

默認(rèn)情況下,Java 中任何兩個對象引用(或類實(shí)例引用)只有指向相同的內(nèi)存地址時才認(rèn)為是相等的(引用相等)。但是Java允許通過重載Objectequals()方法給類自定義判等規(guī)則。聽起來這是個很強(qiáng)大的概念,然而在適當(dāng)?shù)?b>equals()方法實(shí)現(xiàn)需要滿足以下幾個規(guī)則限制:

自反性:對象x必須與其自身相等,equals(x)返回true

對稱性:如果equals(y)true,則y.equals(x)也要返回true

傳遞性:如果equals(y)true,并且y.equals(z)也為true,則x.equals(z)也要為true

一致性:多次調(diào)用equals()方法應(yīng)該返回相同值,除非對用于判等的任何一個屬性進(jìn)行了修改

與null判等equals(null)總是要返回false

不幸的是Java編譯器并不會在編譯時對以上規(guī)則進(jìn)行檢查。然而,不遵守上述規(guī)則時可能會引入非常怪異并難以解決的問題。通用的建議是:如果需要重寫equals()方法,請至少思考兩次重寫的必要性。遵循以上規(guī)則,我們?yōu)?b>Person類重寫一個簡單的equals()實(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.
    @Override
    public boolean equals( Object obj ) {
        // Step 1: Check if the "obj" is null
        if ( obj == null ) {
            return false;
        }
        
        // Step 2: Check if the "obj" is pointing to the this instance
        if ( 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 equality
        final 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()方法并不是偶然的,至少要記住下面這條規(guī)則:任何時候重載equals()方法時,需要一并重載hashCode()方法。如果兩個對象通過equals()方法判等時返回true,則每個對象的hashCode()方法需要返回相同的整數(shù)值(反過來并沒有限制:如果兩個對象通過equals()方法返回false,則hashCode()方法可以返回相同或不同的整數(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;
}      

為了避免得到不可預(yù)期的結(jié)果,盡可能在實(shí)現(xiàn)equals()hashCode()方法時使用final字段,從而保證方法的結(jié)果不會受到字段變化的影響(盡管真實(shí)場景中未必發(fā)生)。

最后,要確保在實(shí)現(xiàn)equals()hashCode()方法是使用相同的字段,以確保在不可預(yù)期的字段調(diào)整時保證這兩個方法行為的一致性。

3. toString方法

toString()是最讓人感興趣的方法,并且被重載的頻率也更高。此方法的目的是提供對象(類實(shí)例)的字符串表現(xiàn)。如果對toString()方法重載恰當(dāng),能極大的簡化debug難度和分析解決問題的過程。

默認(rèn)情況下,toString()的結(jié)果僅僅返回以@符分隔的全類名與對象哈希值串,然而這個結(jié)果在大多場景下并沒什么用途。如下:

com.javacodegeeks.advanced.objects.Person@6104e2ee

我們來通過重寫PersontoString()方法以使其輸出更有用,下面是其中一種實(shí)例:

// 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的所有字段,然后執(zhí)行下面的代碼片段:

final Person person = new Person( "John", "Smith", "[email protected]" );
System.out.println( person.toString() );

控制臺中將輸出以下結(jié)果:

Person[[email protected], first name=John, last name=Smith]

遺憾的是在Java標(biāo)準(zhǔ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 );
    }
    
    @Override
    public String toString() {
        return String.format( "%s{persons=%s}", 
            getClass().getSimpleName(), Arrays.toString( persons ) );
    }
    
    public Person[] getPersons() {
        return persons;
    }
}

相應(yīng)的控制臺輸出如下(同時也有Person實(shí)例的字符串值):

Office{persons=[Person[[email protected], first name=John, last name=Smith]]}

Java社區(qū)實(shí)例了大量有用的類庫以簡化toString()的實(shí)現(xiàn)。其中廣泛使用的有Google Guava的Objects.toStringHelper和Apache Commons Lang的ToStringBuilder

4. clone方法

如果舉出Java中最聲名狼藉的方法,當(dāng)屬clone()無疑。clone()方法的目的很簡單——返回對象實(shí)例的拷貝,然而有一堆理由可證明其使用并不像聽起來那么輕而易舉。

首先,實(shí)現(xiàn)自定義的clone()方法時需要遵守Java文檔)中列出的一系列約定。其次,在Object類中clone()方法被聲明為protected,所以為了提高方法的可見性,在重載時需要聲明為public并把返回值類型調(diào)整為重載類自身類型。再次,重載類需要實(shí)現(xiàn)Cloneable接口(盡管該接口作為一種聲明,并未提供任何方法定義),否則將會拋出CloneNotSupportedException異常。最后,在實(shí)現(xiàn)clone()方法時要先調(diào)用super.clone()然后再執(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.
    @Override
    public Person clone() throws CloneNotSupportedException {
        return ( Person )super.clone();
    }
}

上面的實(shí)現(xiàn)看起來簡單直接,然而卻隱藏著錯誤。當(dāng)類實(shí)例的clone動作被執(zhí)行時,未調(diào)用任何構(gòu)造方法,后果將導(dǎo)致預(yù)料外的數(shù)據(jù)泄露。下面再看下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 );
    }

    @Override
    public Office clone() throws CloneNotSupportedException {
        return ( Office )super.clone();
    }
    
    public Person[] getPersons() {
        return persons;
    }
}

在這個實(shí)現(xiàn)中,Office實(shí)例克隆出來的所有對象都將共享相同的person數(shù)組,然而這并不是我們預(yù)期的行為。為了讓clone()實(shí)現(xiàn)正確的行為,我們還要做一些額外的工作:

@Override
public Office clone() throws CloneNotSupportedException {
    final Office clone = ( Office )super.clone();
    clone.persons = persons.clone();
    return clone;
}

看起來是正確了,但如果對persons字段聲明為final就將破壞這種正確性,因此final字段不能被重新賦值,從而導(dǎo)致數(shù)據(jù)再次被共享。

總之,當(dāng)需要類實(shí)例的拷貝時,盡可能避免使用clone() / Cloneable,相反可以選擇其他更簡單的替代方案(例如:C++程序員熟悉的復(fù)制構(gòu)造方法,或者工廠方法——在對象的創(chuàng)建與銷毀中討論過的一種有用的構(gòu)造模式)。

5. equals方法與"=="操作符

在Java中,==操作符與equals()方法有種奇怪的關(guān)系,卻會引入大量的問題與困惑。大多數(shù)情況下(除比較基本數(shù)據(jù)類型),==操作符執(zhí)行的是引用相等:只要兩個引用指向同一個對象時為true,否則返回false。下面舉例說明二者的區(qū)別:

final String str1 = new String( "bbb" );
System.out.println( "Using == operator: " + ( str1 == "bbb" ) );
System.out.println( "Using equals() method: " + str1.equals( "bbb" ) );

從我們?nèi)祟惖囊暯莵砜矗瑂tr1 == "bbb" 和 str1.equals("bbb")并無區(qū)別:str1僅僅是"bbb"的一個引用,所以結(jié)果應(yīng)該是相同的;但對于Java來說卻不盡然:

Using == operator: false
Using equals() method: true

盡管兩個字符串看起來完全一樣,但事實(shí)上卻是兩個不同的String實(shí)例。作為建議,在處理對象引用時要使用equals()Objects.equals()進(jìn)行判等,除非你真的是要判斷兩個引用是否指向同一個實(shí)例。

6. 有用的幫助類

從Java 7發(fā)布以來,一批有用的幫助類加入到了標(biāo)準(zhǔn)Java庫中,Objects便是其中之一。具體來說,以下三個方法可以簡化你的equals()hashCode()方法實(shí)現(xiàn)。

方法 描述
static boolean equals(Object a, Object b) 當(dāng)參數(shù)中的兩個對象相等時返回true,否則返回false
static int hash(Object...values) 為參數(shù)列表生成哈希值
static int hashCode(Object o) 為非null參數(shù)生成哈希值,如果參數(shù)為null返回0

如果使用上面的方法來重寫Personequals()hashCode()實(shí)現(xiàn),代碼量將會大大縮減,同時代碼的可讀性也將大大增強(qiáng)。

@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. 下章概要

在本章中,我們學(xué)習(xí)了作為Java面向?qū)ο蠡A(chǔ)的Object類,以及自定義的類如何通過自己的判等規(guī)則重載Object的相關(guān)方法。下一章中,我們將會把視線暫時從代碼實(shí)現(xiàn)上收起,轉(zhuǎn)向去討論如何設(shè)計(jì)合適的類和接口。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/65466.html

相關(guān)文章

  • PHP 進(jìn)階之路 - 后端多元化之快速切入 Java 開發(fā)

    摘要:以實(shí)現(xiàn)自己熟悉的東西為導(dǎo)向比如我們做后端開發(fā),首先是常用的循環(huán)迭代條件判斷增刪改成。它是由實(shí)現(xiàn)的,不保證元素的順序,也就是說所說元素插入的順序與輸出的順序不一致。 下面是我直播的文字版,直播地址:https://segmentfault.com/l/15...代碼:https://github.com/zhoumengka...整個項(xiàng)目我們我又細(xì)分了6個版本來演進(jìn),希望更加便于大家對比...

    Cristic 評論0 收藏0
  • PHP 進(jìn)階之路 - 后端多元化之快速切入 Java 開發(fā)

    摘要:以實(shí)現(xiàn)自己熟悉的東西為導(dǎo)向比如我們做后端開發(fā),首先是常用的循環(huán)迭代條件判斷增刪改成。它是由實(shí)現(xiàn)的,不保證元素的順序,也就是說所說元素插入的順序與輸出的順序不一致。 下面是我直播的文字版,直播地址:https://segmentfault.com/l/15...代碼:https://github.com/zhoumengka...整個項(xiàng)目我們我又細(xì)分了6個版本來演進(jìn),希望更加便于大家對比...

    xi4oh4o 評論0 收藏0
  • Java進(jìn)階1 —— 對象創(chuàng)建與銷毀

    摘要:構(gòu)造方法是在對象實(shí)例初始化過程中具有舉足輕重的地位,并且提供了多種方式來定義構(gòu)造方法。在中創(chuàng)建對象的開銷是相當(dāng)?shù)偷模⑶宜俣群芸?。對象終結(jié)器前面我們講述的都是構(gòu)造方法和對象初始化相關(guān)的主題,但還未提及他們的反面對象銷毀。 原文鏈接:http://www.javacodegeeks.com/2015/09/how-to-create-and-destroy-objects.html 本文...

    nemo 評論0 收藏0
  • Java進(jìn)階3 —— 類和接口設(shè)計(jì)原則

    摘要:首當(dāng)其沖的便是接口中的每個聲明必須是即便不指定也是,并且不能設(shè)置為非,詳細(xì)規(guī)則可參考可見性部分介紹。函數(shù)式接口有著不同的場景,并被認(rèn)為是對編程語言的一種強(qiáng)大的擴(kuò)展。抽象類與中的接口有些類似,與中支持默認(rèn)方法的接口更為相像。 原文鏈接:http://www.javacodegeeks.com/2015/09/how-to-design-classes-and-interfaces.htm...

    lauren_liuling 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<