跳轉到內容

Java 程式設計/API/java.lang.Object

來自 Wikibooks,開放世界中的開放書籍

java.lang.Object

[編輯 | 編輯原始碼]

Object 類是所有 Java 類的超類。所有 Java 類都繼承自此類。這使得我們可以擁有所有 Java 類中都可用的方法成為可能。與 C++ 不同,這簡化了事情。

Object 類方法 描述
boolean equals( Object o ); 提供比較物件的一般方法
Class getClass(); Class 類為我們提供了更多關於物件的資訊
int hashCode(); 返回一個雜湊值,用於在集合中搜索物件
void notify(); 用於執行緒同步
void notifyAll(); 用於執行緒同步
String toString(); 可用於將物件轉換為字串
void wait(); 用於執行緒同步
protected Object clone() throws CloneNotSupportedException ; 返回一個與當前物件完全相同的新物件
protected void finalize() throws Throwable; 此方法在垃圾回收物件之前呼叫

equals() 方法

[編輯 | 編輯原始碼]
  • boolean equals( Object o ) 方法提供了一種通用的方式來比較物件的相等性。您需要在您的類中重寫它。然後你可以寫
public boolean isCustomerExist( Customer newCustomer )
{
   boolean isRet = false;
   Iterator iter = _collAllCustomer.iterator();
   while ( iter.hasNext() )
   {
      if ( newCustomer.equals( (Customer) iter.next() )
      {
         // -- Customer was found ---
         isRet = true;
      }
   }
  return isRet;
}

請記住,當您重寫 equals() 時,始終需要同時重寫 hashCode() 以使這兩個方法保持一致。如果兩個物件相等,則它們必須具有相同的雜湊碼。

有關更多資訊,另請參閱 Java 程式設計/比較物件

getClass() 方法

[編輯 | 編輯原始碼]

程式中的每個類都有一個 Class 物件。每個陣列也屬於一個類,該類反映為一個 Class 物件,該物件由所有具有相同元素型別和維數的陣列共享。Java 基本型別booleanbytecharshortintlongfloatdouble)以及關鍵字 void 也表示為 Class 物件。Class 沒有公共建構函式。相反,當類載入時,Java 虛擬機器會自動構造 Class 物件

有關更多資訊,請參閱 Class

Class 最常用的用途是在執行時找出物件的類名。

import com.yourCompany.Customer;
...
Object obj = new Customer();
...
System.out.println( "Name:" + obj.getClass().getName() );

輸出

Name: com.yourCompany.Customer

hashCode() 方法

[編輯 | 編輯原始碼]

在大多數情況下,您不應該重寫此方法,因為此方法的預設實現會為物件返回一個唯一編號。當物件放入集合時,將使用該編號。如果逐一順序比較物件,則在大型集合中查詢物件可能需要一段時間。為了加快搜索速度,可以將物件放置在樹形結構中,並根據整數雜湊碼加權。在遍歷樹時比較雜湊碼,可以減少物件比較的次數。

   _______ A _____
   |              |  
__ B__          __C__
|     |        |     |
D     E        F     G
...  ...      ...   ...

為了讓您大致瞭解其工作原理,請參見上圖。假設我們正在搜尋物件 G。如果在樹的每個“節點”上我們都可以決定走哪條路,那麼透過3 步我們就可以到達物件 G。

相比之下,線上性搜尋中

A --- B  ----- C  ---- C  ---- D  ---- E ---- F ---- G

我們需要8 步才能到達物件 G。

因此,使用樹形結構搜尋速度會更快。但是,新增新物件會更慢,因為需要維護樹形結構。首先必須找到新物件在樹中的位置。

toString() 方法

[編輯 | 編輯原始碼]

此方法可用於將物件轉換為String。它在許多地方自動用於將物件轉換為字串;例如:在 PrintStream、StringBuffer 中,以及在對物件使用字串連線運算子時。

預設實現返回一個帶有類名和雜湊碼的奇怪字串。

例如

String str = "This customer is " + objCust;

toString() 方法在 objCust 物件上呼叫。

toString() 方法也可用於除錯

public class Customer
{
   private String _name;
   private String _address;
   private String _age;
...
   public String toString()
   {
       StringBuffer buf = new StringBuffer();
       buf.append( "Name   = " );  buf.append( _name );     buf.append( "\n" );
       buf.append( "Address= " );  buf.append( _address );  buf.append( "\n" );
       buf.append( "Age    = " );  buf.append( _age );      buf.append( "\n" );
    ...
       return buf.toString();
   }
...
}

之後,無論何時在您的程式碼中,您想檢視客戶物件是什麼,只需呼叫

System.out.println( objCustomer );

執行緒同步方法

[編輯 | 編輯原始碼]

在多執行緒環境中,當多個執行緒可以訪問和修改資源時,結果可能是不可預測的。例如,讓我們有一個計數器變數,它由多個執行緒遞增。

注意!同步是一個模稜兩可的術語。它不包括使所有執行緒同時執行相同的程式碼段。恰恰相反。它可以防止任何兩個執行緒同時執行相同的程式碼段。它使一個處理的結束與第二個處理的開始同步。

Example 程式碼段 1.1:計數器實現
int counter = 0;
...
counter += 1;

以上程式碼由以下子操作構成

  • 讀取;讀取變數 counter
  • 新增;將 1 加到值上
  • 儲存;將新值儲存到變數 counter

假設兩個執行緒需要執行該程式碼,如果 counter 變數的初始值為零,我們期望操作後該值變為 2。

執行緒 1   執行緒 2
         
讀取 0   讀取 0
         
新增 1   新增 1
         
儲存 1   儲存 1
         

在上述情況下,執行緒 1 的操作丟失了,因為執行緒 2 覆蓋了它的值。我們希望執行緒 2 等待執行緒 1 完成操作。請參見下文

執行緒 1   執行緒 2
         
讀取 0   阻塞
         
新增 1   阻塞
         
儲存 1   解除阻塞
         
  讀取 1
     
  新增 1
     
  儲存 2
     
臨界區
在上面的例子中,程式碼 counter+=1 必須在任何給定時間由一個且僅一個執行緒執行。這稱為臨界區。在程式設計過程中,在多執行緒環境中,我們必須識別屬於臨界區的所有這些程式碼段,並確保在任何給定時間只有一個執行緒可以執行這些程式碼。這稱為同步。

執行緒同步
執行緒訪問臨界區程式碼必須在各個執行緒之間進行同步,也就是說要確保在任何給定時間只有一個執行緒可以執行它。
物件監視器
每個物件都有一個物件監視器。基本上它是一個訊號量,指示臨界區程式碼是否正在被某個執行緒執行。在執行臨界區程式碼之前,執行緒必須獲得物件監視器。一次只有一個執行緒可以擁有物件的監視器。
執行緒可以透過三種方式之一成為物件監視器的擁有者
  • 透過執行該物件的同步例項方法。參見synchronized關鍵字。
  • 透過執行同步語句的主體,該語句對物件進行同步。參見synchronized關鍵字。
  • 對於Class型別的物件,透過執行該類的同步靜態方法。
物件監視器負責同步,那麼為什麼我們還需要“wait() 和 notify() 方法”呢?
對於同步,我們並不真正需要它們,但是對於某些情況來說,使用它們會很好。一個友好且體貼的執行緒會使用它們。在執行臨界區程式碼期間,執行緒可能會卡住,無法繼續執行。這可能是因為它正在等待IO和其他資源。無論如何,執行緒可能需要等待相對較長的時間。如果執行緒一直持有物件監視器並阻止其他執行緒工作,那將是自私的。因此,執行緒透過呼叫物件上的wait()方法進入“等待”狀態。它必須是執行緒從中獲取物件監視器的同一個物件。
另一方面,執行緒只有在至少存在另一個執行緒將在資源可用時呼叫notify()方法的情況下,才應該呼叫wait()方法,否則執行緒將永遠等待,除非將時間間隔指定為引數。
讓我們做一個類比。你去商店購買一些物品。你在櫃檯前排隊,你獲得了售貨員的關注 - 你獲得了她的“物件監視器”。你詢問你想要的物品。其中一件物品需要從倉庫中取來。這將花費超過五分鐘的時間,因此你釋放售貨員(將她的“物件監視器”還給她),以便她可以為其他顧客服務。你進入等待狀態。假設還有五位顧客已經在等待。另一位售貨員從倉庫中取來物品。當她這樣做時,她會引起第一位售貨員的注意,獲得她的物件監視器並通知一位或所有等待的顧客,以便等待的顧客醒來並再次排隊以獲得第一位售貨員的關注。
請注意等待的顧客和取來物品的售貨員之間的同步。這是一種生產者-消費者同步。
還要注意,只有一個物件監視器,屬於第一位售貨員。在發生等待通知之前,必須首先獲得該物件監視器/售貨員的關注。


final void wait() 方法
當前執行緒必須擁有該物件的監視器。執行緒釋放對該監視器的所有權,並等待另一個執行緒透過呼叫notify方法或notifyAll方法通知正在等待該物件監視器的執行緒喚醒。然後,執行緒等待直到它能夠重新獲得監視器的所有權並恢復執行。
final void wait(long time)
與wait相同,但執行緒在指定持續時間過去後喚醒,無論是否收到通知。
final void notify()
此方法只能由擁有該物件監視器的執行緒呼叫。喚醒一個正在等待該物件監視器的執行緒。如果多個執行緒正在等待該物件監視器,則選擇其中一個喚醒。選擇是任意的,並且由實現決定。執行緒透過呼叫其中一個wait方法來等待物件監視器。
被喚醒的執行緒在當前執行緒釋放對該物件的鎖之前無法繼續執行。被喚醒的執行緒將以通常的方式與任何其他可能正在積極競爭同步該物件的執行緒競爭;例如,被喚醒的執行緒在成為下一個鎖定該物件的執行緒方面沒有任何可靠的特權或劣勢。
final void notifyAll()
notify()相同,但它喚醒所有正在等待該物件監視器的執行緒。


sleep() 和 wait() 方法之間有什麼區別?
Thread.sleep(millis)
這是Thread類的靜態方法。導致當前正在執行的執行緒休眠(暫時停止執行)指定的毫秒數。執行緒不會丟失任何監視器的所有權。這意味著如果執行緒擁有物件監視器,則所有其他需要該監視器的執行緒都將被阻塞。無論執行緒是否擁有任何監視器,都可以呼叫此方法。
wait()
此方法繼承自Object類。執行緒必須首先獲得該物件的監視器,然後才能呼叫wait()方法。物件監視器由wait()方法釋放,因此它不會阻塞其他想要此物件監視器的等待執行緒。


另請參閱

[編輯 | 編輯原始碼]
華夏公益教科書