Java:Object類詳解
Java 問答:終極父類
Java的一些特性會讓初學者感到困惑,但在有經驗的開發者眼中,卻是合情合理的。例如,新手可能不會理解Object
類。這篇文章分成三個部分講跟Object
類及其方法有關的問題。
上帝類
問:什麼是Object
類?
答:Object
類儲存在java.lang包中,是所有java類(Object
類除外)的終極父類。當然,陣列也繼承了Object
類。然而,介面是不繼承Object
類的,原因在這裡指出:Section
9.6.3.4 of the Java Language Specification:“Object
類不作為介面的父類”。
Object
類中聲明瞭以下函式,我會在下文中作詳細說明。
- protected Object clone()
- boolean equals(Object obj)
- protected void finalize()
- Class< > getClass()
- int hashCode()
- void notify()
- void notifyAll()
- String toString()
- void wait()
- void wait(long timeout)
- void wait(long timeout, int nanos)
java的任何類都繼承了這些函式,並且可以覆蓋不被final
修飾的函式。例如,沒有final
修飾的toString()
函式可以被覆蓋,但是final
wait()
問:可以宣告要“繼承Object
類”嗎?
答:可以。在程式碼中明確地寫出繼承Object
類沒有語法錯誤。參考程式碼清單1。
程式碼清單1:明確的繼承Object
類
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import java.lang.Object;
public class Employee extends Object
{
private String
name;
public Employee(String
name) {
this .name
= name;
}
public String
getName() {
return name;
}
public static void main(String[]
args) {
Employee
emp = new Employee( "John
Doe" );
System.out.println(emp.getName());
}
}
|
你可以試著編譯程式碼1(javac Employee.java
),然後執行Employee.class(java
Employee
),可以看到John Doe 成功的輸出了。
因為編譯器會自動引入java.lang
包中的型別,即import
java.lang.Object
; 沒必要宣告出來。Java也沒有強制宣告“繼承Object
類”。如果這樣的話,就不能繼承除Object
類之外別的類了,因為java不支援多繼承。然而,即使不宣告出來,也會預設繼承了Object
類,參考程式碼清單2。
程式碼清單2:預設繼承Object
類
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public class Employee
{
private String
name;
public Employee(String
name)
{
this .name
= name;
}
public String
getName()
{
return name;
}
public static void main(String[]
args)
{
Employee
emp = new Employee( "John
Doe" );
System.out.println(emp.getName());
}
}
|
就像程式碼清單1一樣,這裡的Employee
類繼承了Object
,所以可以使用它的函式。
克隆Object
類
問:clone()
函式是用來做什麼的?
答:clone()
可以產生一個相同的類並且返回給呼叫者。
問:clone()
是如何工作的?
答:Object
將clone()
作為一個本地方法來實現,這意味著它的程式碼存放在本地的庫中。當代碼執行的時候,將會檢查呼叫物件的類(或者父類)是否實現了java.lang.Cloneable
介面(Object
類不實現Cloneable
)。如果沒有實現這個介面,clone()
將會丟擲一個檢查異常()——java.lang.CloneNotSupportedException
,如果實現了這個介面,clone()
會建立一個新的物件,並將原來物件的內容複製到新物件,最後返回這個新物件的引用。
問:怎樣呼叫clone()
來克隆一個物件?
答:用想要克隆的物件來呼叫clone()
,將返回的物件從Object
類轉換到克隆的物件所屬的類,賦給物件的引用。這裡用程式碼清單3作一個示例。
程式碼清單3:克隆一個物件
1 2 3 4 5 6 7 8 9 10 11 |
public class CloneDemo implements Cloneable
{
int x;
public static void main(String[]
args) throws CloneNotSupportedException
{
CloneDemo
cd = new CloneDemo();
cd.x
= 5 ;
System.out.printf( "cd.x
= %d%n" , cd.x);
CloneDemo
cd2 = (CloneDemo) cd.clone();
System.out.printf( "cd2.x
= %d%n" , cd2.x);
}
}
|
程式碼清單3聲明瞭一個繼承Cloneable
介面的CloneDemo
類。這個介面必須實現,否則,呼叫Object的clone()時將會導致丟擲異常CloneNotSupportedException
。
CloneDemo
聲明瞭一個int
型變數x
和主函式main()
來演示這個類。其中,main()
宣告可能會向外丟擲CloneNotSupportedException
異常。
Main()
先例項化CloneDemo
並將x
的值初始化為5。然後輸出x
的值,緊接著呼叫clone()
,將克隆的物件傳回CloneDemo
。最後,輸出了克隆的x
的值。
編譯程式碼清單3(javac CloneDemo.java
)然後執行(java
CloneDemo
)。你可以看到以下執行結果:
1 2 |
cd .x
= 5
cd2.x = 5
|
問:什麼情況下需要覆蓋clone()
方法呢?
答:上面的例子中,呼叫clone()
的程式碼是位於被克隆的類(即CloneDemo
類)裡面的,所以就不需要覆蓋clone()
了。但是,如果呼叫別的類中的clone()
,就需要覆蓋clone()
了。否則,將會看到“clone
在Object
中是被保護的”提示,因為clone()
在Object
中的許可權是protected
。(譯者注:protected
許可權的成員在不同的包中,只有子類物件可以訪問。程式碼清單3的CloneDemo
類和程式碼清單4的Data
類是Object
類的子類,所以可以呼叫clone()
,但是程式碼清單4中的CloneDemo
類就不能直接呼叫Data
父類的clone()
)。程式碼清單4在程式碼清單3上稍作修改來演示覆蓋clone()
。
程式碼清單4:從別的類中克隆物件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class Data implements Cloneable
{
int x;
@Override
public Object
clone() throws CloneNotSupportedException
{
return super .clone();
}
}
public class CloneDemo
{
public static void main(String[]
args) throws CloneNotSupportedException
{
Data
data = new Data();
data.x
= 5 ;
System.out.printf( "data.x
= %d%n" , data.x);
|