java.lang包—对象基类Object
原文作者:Boblim
原文地址:Java:Object類詳解
目錄
一、上帝類
二、Object的類方法
三、常見面試題
Java的一些特性會讓初學者感到困惑,但在有經驗的開發者眼中,卻是合情合理的。例如,新手可能不會理解Object類。這篇文章分成三個部分講跟Object類及其方法有關的問題。
一、上帝類
什么是Object類?
Object類存儲在java.lang包中,是所有java類(Object類除外)的終極父類。當然,數組也繼承了Object類。然而,接口是不繼承Object類的,原因在這里指出:Section 9.6.3.4 of the Java Language Specification
?Object類中聲明了以下函數,java的任何類都繼承了這些函數,并且可以覆蓋不被final修飾的函數。例如,沒有final修飾的toString()函數可以被覆蓋,但是final wait()函數就不行。我會在下文中詳細說明這些函數。
protected Object clone() boolean equals(Object obj) protected void finalize() Class< > getClass() int hashCode() String toString() void wait() void wait(long timeout) void wait(long timeout, int nanos) void notify() void notifyAll()可以聲明要“繼承Object類”嗎?
可以。Object 類可以顯示繼承,也可以隱式繼承;在代碼中明確地寫出繼承Object類沒有語法錯誤。即以下兩種寫法都正確:
importjava.lang.Object; //明確的繼承Object類 public class Employee extends Object {public static void main(String[] args) {} } //默認繼承Object類 public class Employee{private String name;publicstaticvoidmain(String[] args){} }二、Object的類方法
1、關于克隆函數clone()
Java基礎—復制之深拷貝與淺拷貝
2、關于wait()/notify()/?notifyAll()
Java并發編程—線程間協作方式wait()/notify()/notifyAll()原理分析
3、關于比較函數equals(Object obj)
什么時候需要重寫equals()?我們知道每一個java類都繼承自Object類,equals()是Object類中提供的方法之一。那么,讓我們先來看看Object#equals()在Java中的原代碼:
public boolean equals(Object obj) {return (this == obj); }可以看出,只有當一個實例等于它本身的時候,equals()才會返回true值。通俗地說,此時比較的是兩個引用是否指向內存中的同一個對象,也可以稱做是否實例相等。而我們在使用equals()來比較兩個指向值對象的引用的時候,往往希望知道它們邏輯上是否相等,而不是它們是否指向同一個對象。在這樣的情況下, 如果超類也沒有重寫equals()以實現期望的行為,這時我們就需要重寫equals方法。而且這樣做也使得這個類的實例可以被用做映射表(map)的鍵,或者集合(set)的元素,并使映射表或者集合表現出預期的行為。Object類提供的equals方法只是一個很簡單的,不能適應應用程序有特殊要求的情況。比如網絡對象,帶有volatile屬性的對象,或是帶有多層子對象的復合對象,等等,是不能像String一類的對象進行簡單比較的,所以提供了這樣一個機制,就像serializable接口一樣,既有默認的序列化方法,也提供了程序自己定制,覆蓋默認方式的可能性。Object僅僅提供了一個對引用的比較,如果兩個引用不是同一個那就返回false,這是無法滿足大多數對象比較的需要的,所以要覆蓋。
怎樣重寫equals()方法?重寫equals()方法看起來非常簡單,但是有許多改寫的方式會導致錯誤,并且后果非常嚴重。要想正確改寫equals()方法,你必須要遵守它的通用約定。下面是約定的內容,來自java.lang.Object的規范:
equals方法實現了等價關系(equivalence relation):
- 1. 自反性:對于任意的引用值x,x.equals(x)一定為true。
- 2. 對稱性:對于任意的引用值x 和 y,當x.equals(y)返回true時,y.equals(x)也一定返回true。
- 3. 傳遞性:對于任意的引用值x、y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也一定返回true。
- 4. 一致性:對于任意的引用值x 和 y,如果用于equals比較的對象信息沒有被修改,多次調用x.equals(y)要么一致地返回true,要么一致地返回false。
- 5. 非空性:對于任意的非空引用值x,x.equals(null)一定返回false。
接下來我們通過實例來理解上面的約定。我們首先以一個簡單的非可變的二維點類作為開始:
public class Point{private final int x;private final int y;public Point(int x, int y){this.x = x;this.y = y;}public boolean equals(Object o){if(!(o instanceof Point))return false;Point p = (Point)o;return p.x == x && p.y == y;}}假設你想要擴展這個類,為一個點增加顏色信息:
public class ColorPoint extends Point{private Color color;public ColorPoint(int x, int y, Color color){super(x, y);this.color = color;}//override equasl()public boolean equals(Object o){if(!(o instanceof ColorPoint))return false;ColorPoint cp = (ColorPoint)o;return super.equals(o) && cp.color==color;} }我們重寫了equals方法,只有當實參是另一個有色點,并且具有同樣的位置和顏色的時候,它才返回true。可這個方法的問題在于,你在比較一個普通點和一個有色點,以及反過來的情形的時候,可能會得到不同的結果:
public static void main(String[] args){Point p = new Point(1, 2);ColorPoint cp = new ColorPoint(1, 2, Color.RED);System.out.println(p.equals(cp));System.out.println(cp.eqauls(p)); }運行結果:
true??
false
這樣的結果顯然違反了對稱性,你可以做這樣的嘗試來修正這個問題:讓ColorPoint.equals在進行“混合比較”的時候忽略顏色信息:
public boolean equals(Object o){if(!(o instanceof Point))return false;//如果o是一個普通點,就忽略顏色信息if(!(o instanceof ColorPoint))return o.equals(this);//如果o是一個有色點,就做完整的比較ColorPoint cp = (ColorPoint)o;return super.equals(o) && cp.color==color; }這種方法的結果會怎樣呢?讓我們先來測試一下:
public static void main(String[] args){ColorPoint p1 = new ColorPoint(1, 2, Color.RED);Point p2 = new Point(1, 2);ColorPoint p3 = new ColorPoint(1, 2, Color.BLUE);System.out.println(p1.equals(p2));System.out.println(p2.equals(p1));System.out.println(p2.equals(p3));System.out.println(p1.eqauls(p3)); }運行結果:
true
true
true
false
這種方法確實提供了對稱性,但是卻犧牲了傳遞性(按照約定,p1.equals(p2)和p2.eqauals(p3)都返回true,p1.equals(p3)也應返回true)。要怎么解決呢?事實上,這是面向對象語言中關于等價關系的一個基本問題。要想在擴展一個可實例化的類的同時,既要增加新的特征,同時還要保留equals約定,沒有一個簡單的辦法可以做到這一點。新的解決辦法就是不再讓ColorPoint擴展Point,而是在ColorPoint中加入一個私有的Point域,以及一個公有的視圖(view)方法:
public class ColorPoint{private Point point;private Color color;public ColorPoint(int x, int y, Color color){point = new Point(x, y);this.color = color;}//返回一個與該有色點在同一位置上的普通Point對象public Point asPoint(){return point;}public boolean equals(Object o){if(o == this)return true;if(!(o instanceof ColorPoint))return false;ColorPoint cp = (ColorPoint)o;return cp.point.equals(point)&&cp.color.equals(color);} }還有另外一個解決的辦法就是把Point設計成一個抽象的類(abstract class),這樣你就可以在該抽象類的子類中增加新的特征,而不會違反equals約定。因為抽象類無法創建類的實例,那么前面所述的種種問題都不會發生。
重寫equals方法的要點:
- 1. 使用==操作符檢查“實參是否為指向對象的一個引用”。
- 2. 使用instanceof操作符檢查“實參是否為正確的類型”。
- 3. 把實參轉換到正確的類型。
- 4. 對于該類中每一個“關鍵”域,檢查實參中的域與當前對象中對應的域值是否匹配。對于既不是float也不是double類型的基本類型的域,可以使用==操作符進行比較;對于對象引用類型的域,可以遞歸地調用所引用的對象的equals方法;對于float類型的域,先使用Float.floatToIntBits轉換成int類型的值,然后使用==操作符比較int類型的值;對于double類型的域,先使用Double.doubleToLongBits轉換成long類型的值,然后使用==操作符比較long類型的值。
- 5. 當你編寫完成了equals方法之后,應該問自己三個問題:它是否是對稱的、傳遞的、一致的?(其他兩個特性通常會自行滿足)如果答案是否定的,那么請找到這些特性未能滿足的原因,再修改equals方法的代碼。
三、常見面試題
1、寫出java.lang.Object類的六個常用方法?Object類中的各個方法有什么作用?
這是一個非常常見的問題,用來確定你對基礎知識的熟悉程度。
2、
?
?
?
?
?
總結
以上是生活随笔為你收集整理的java.lang包—对象基类Object的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java.lang包—类加载器Class
- 下一篇: java.lang包—基本类型的封装类