概述
Object类是类层次结构的根类,可以作为各种的通用持有者。它是每个java类的基类,如果没有明确指出基类,Object就被认为是当前定义的类的基类。包括arrays在内的所有对象,都实现Object类的方法。Object类属于java.lang包,在这个类中有很多native(本地)方法。其类图如下(JDK8):
构造方法
Object类没有显示的构造方法,只有编译器默认提供的构造方法。
字段
Object没有字段。
方法
Object只包含12个方法,但这些方法都十分重要。
按访问限制级别可分为:- public:
equals(Object)
,hashCode()
,notify()
,notifyAll()
,toString()
,wait()
,wait(long)
,wait(long, int)
- protected:
clone()
,finalize()
- private:
registerNatives()
按是否为final可分为:
- final:
getClass()
,notify()
,notifyAll()
,wait()
,wait(long)
,wait(long, int)
,registerNatives()
(类私有方法自动成为final)。这些方法不能被子类重写。 - 非final:
hashCode()
,equals(Object)
,clone()
,toString()
,finalize()
。这些方法可以被子类重写,且必须满足通用的约定,否则其他依赖于这些约定的类就无法与该类正常工作。
按是否为native可分为:
- native:
registerNatives()
,getClass()
,hashCode()
,clone()
,notify()
,notifyAll()
,wait(long)
。这些方法是用C/C++在动态库中实现的,然后通过JNI(java Native Inteface)调用。Java语言本身不能对操作系统底层进行访问与操作,但可以通过JNI接口来调用其他语言来实现对底层的访问,JNI已加入Java标准。 - 非native:
equals(Object)
,toString()
,wait()
,wait(long, int)
,finalize()
。
registerNatives()
方法
其主要作用是将C/C++中的方法映射到Java中的native方法,实现方法命名的解耦。在类初始化时调用static块,执行registerNatives方法。
private static native void registerNatives();static { registerNatives();}复制代码
getClass()
方法
返回包含对象信息的类对象(Class类型的实例),并且返回的类对象是被此类的静态同步(static synchronized)方法锁定的**(啥意思?)**。类型类Class表示一个类型的类,因为一切皆对象,类型也不例外,因此所有的类型类都是Class类的实例。
public final native Class getClass();复制代码
与线程有关的方法
包括wait
,notify
和notifyAll
,暂不分析,先占个坑,之后补上。
finalize()
方法
类似C++的析构函数,当GC(Garbage Collector)确定不存在对该对象的更多引用时,由对象的GC调用此方法,用于释放资源。一般不建议使用此方法来释放非内存资源。它不同于C++的析构函数,析构函数在对象作用域调用,执行时间点确定。而此方法,是在内存不足,GC发生时进行调用,由于GC是不确定随机的,所以无法确定此方法的执行时间。
protected void finalize() throws Throwable {}复制代码
equals(Object)
方法
equals方法用于两个对象是否相等。Object的equals方法很简单,只用两个对象的引用是否相等来判断是否是同一个对象。Java规范要求equals方法具备以下特性:
- 自反性:对于任何非空引用,x.equals(x)应该返回true;
- 对称性:对于任何引用x和y,当且仅当y.equals(x)返回true,x.equals(y)也应该返回true;
- 传递性:对于任何引用x、y和z,如果x.equals(y)返回true,y.equals(z)返回true,x.equals(z)也应该返回true;
- 一致性:如果x和y引用的对象没有发生变化,反复调用x.equals(y)应该返回同样的结果;
- 对于任意非空引用x,x.equals(null)应该返回false。
Object类的equals实现为:
public boolean equals(Object obj) { return (this == obj); // 大佬都不嫌麻烦用括号扩起,习惯使然!}复制代码
然而在大多数情况下,只将引用作为判断对象的唯一标准,太严格且没有实际意义。例如对于大多数实体类,如Book类,我们对比两个Book类实例时,关注的是这两个实例的状态(书名、价格、作者等)是否相等。如果两者的状态都相等,即使不是同一个引用,我们也判定这两个Book实例相等。下面实现一个判断两个Book实例是否相等的equals方法:
public class Book { private String name; // 书名 private int price; // 价格 private String writer; // 作者 @Override // 1. 带注解,防止入参类型错误 public boolean equals(Object otherObject){ // 2. 检测两个实例引用是否相同,优化需要。 if (this == otherObject){ return true; } // 3. 检测被比较对象是否为空,必须 if (otherObject == null){ return false; } // 4. 比较this和入参是否同属于一个类 if (getClass() != otherObject.getClass()){ return false; } // 5. 将otherObject转换为相应的类类型变量,为后续比较具体域状态做准备 Book other = (Book) otherObject; // 6. 比较所有的域。有对象域可能都为null,因此不能使用name.equals(other.name)这种方法 return Objects.equals(name, other.name) && price == other.price && Objects.equals(writer, other.writer); }}复制代码
在代码的第4步中,使用getClass()
来判断两个对象是否同属于一个类,其使用场景为:equals的定义在每个子类中有所改变。如果所有的子类都拥有统一的语义,如所有的子类都使用父类的equals方法,则使用instanceof
代替getClass()
:
if (!(otherObject instanceof Book)) { return false;}复制代码
equals在使用时,要格外注意当父类实现了equals方法,子类之间equals方法的实现方式要满足对称性的要求。
另外值得注意的是,如果重写equals方法,则必须同时重新定义hashCode()方法,以便用户可以将对象插入到散列表中。hashCode()
方法
Object类中的hashCode方法是native方法,返回一个整型数值(也可以是负数),无具体的实现方式。由于该方法是在Object中定义的,因此java中每个对象都会有一个默认的散列码(hash code),其值可能是对象的存储地址。
public native int hashCode();复制代码
hashCode方法具体分析考虑之后结合集合再整理,先占个坑。TODO...
toString()
方法
Object的该方法返回了类的名称加上'@',再加上此类哈希码的16进制表示,如:I[@1a46e30,其中I[表示一个整型数组。
public String toString(){ return getClass().getName() + "@" + Integer.toHexString(hashCode());}复制代码
一个好的习惯,应该是为每个类,特别是模型类重写一个toString()方法,以便用户能够通过这个方法获得对象状态的必要信息。一个反面例子就是数组没有重写toString(),而是直接继承了Object的toString()方法,因此数组对象调用toString()方法,打印的字符串都是类似I[@1a46e30这种形式的,鸡肋!所以无奈只能使用静态方法Arrays.toString(arr)
或者Arrays.deepToString()
方法。
绝对多数的toString方法都遵循这样的格式:类的名字,随后是一对方括号括起来的阈值。
下面是Person类中toString方法的实现:
public String toString() { return getClass().getName() + "[name=" + name + ", age=" + age + "]";}复制代码
toString在项目被频繁使用,特别是只要对象与一个字符串通过操作符"+"连接起来,或作为System.out.println()
的入参,则编译器都会默认调用对象的toString方法。