Object类

Object

利用继承和多态性可以解决子类对象与父类对象的自动转型操作,但是如果想要统一参数中的开发类型,就必须有一种类可以称为所有类的父类,这个类就是Object。任何一个类都隐含继承了Object类,所以使用Object的好处是可以接收所有类的对象。

//这两种写法等价:
//1. class A{}
//2. class A extends Object{}

对于任何一个类,理论上应该覆写Object中的三个方法:

public String toString();//取得对象信息
public boolean equals(Object obj);//对象比较
public native int hashCode();//取得对象的哈希码

public final native Class<?> getClass()

protected native Object clone() throws CloneNotSupportedException//创建并返回对象的副本。此方法避免了引用传递的缺点:所有的变量指向同一堆内存。

public String toString()

public final native void notify()

public final native void notifyAll()

public final native void wait(long timeout) throws InterruptedException

public final void wait(long timeout, int nanos) throws InterruptedException

public final void wait() throws InterruptedException

protected void finalize() throws Throwable {}

toString()

toString()方法默认输出对象的编码,但是String类中覆写了这个方法,所以String类输出的是字符串。

class Book extends Object{
}
public class Demo{
    public static void main(String[] args){
        Object obja = new Book();
        Object objb = "Hello";
        System.out.println(obja);
        System.out.println(obja.toString());
        System.out.println(objb);
    }
}
/*程序执行结果为:
Book@7ef20235
Book@7ef20235
Hello
*/

下面来覆写toString()方法,使其输出字符串:

class Book{
    private String title;
    public Book(String title){
        this.title = title;
    }
    public String toString(){
        return "title: "+this.title;
    }
}

public class ObjcetTest {
    public static void main(String[] args){
        Object obja = new Book("Learn Java");
        System.out.println(obja);	//直接输出对象,默认调用toString()
    }
}
/*程序执行结果:
title: Learn Java
*/

equals()

在Object类中,equals()方法默认比较两个对象的内存地址,不符合真正对象比较的需要,也需要覆写。

实现步骤:

  1. 检查是否为同一个对象的引用,如果是直接返回 true;
  2. 检查是否是同一个类型,如果不是,直接返回 false;
  3. 将 Object 对象进行转型;
  4. 判断每个关键域是否相等。
//覆写equals方法
class Book{
    private String title;
    public Book(String title) {
        this.title = title;
    }

    public boolean equals(Object obj){
        if(this == obj){
            return true; //是否是同一个对象的引用
        }
        if(this == null){   //对象内容是否为null
            return false;
        }
        if(!(obj instanceof Book)){ //是否是同一个类型
            return false;
        }
        Book book = (Book)obj;//向下转型
        if(this.title.equals(book.title)) { //比较关键域
            return true;
        }
        return false;
    }
}
public class ObjectTest{
    public static void main(String[] args) {
        Book ba = new Book("Learn Java");
        Book bb = new Book("Learn Java");
        System.out.println(ba.equals(bb));
    }
}
/*执行结果为:
true
*/

Object对象除了可以接收一切类对象,也可以接受数组和接口,因为数组和接口都是引用数据类型。

hashCode()

hashCode() 返回散列值,而 equals() 是用来判断两个对象是否等价。等价的两个对象散列值一定相同,但是散列值相同的两个对象不一定等价。在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证等价的两个对象散列值也相等。

下面的代码中,新建了两个等价的对象,并将它们添加到 HashSet 中。我们希望将这两个对象当成一样的,只在集合中添加一个对象,但是因为 EqualExample 没有实现 hasCode() 方法,因此这两个对象的散列值是不同的,最终导致集合添加了两个等价的对象。

EqualExample e1 = new EqualExample(1, 1, 1);
EqualExample e2 = new EqualExample(1, 1, 1);
System.out.println(e1.equals(e2)); // true
HashSet<EqualExample> set = new HashSet<>();
set.add(e1);
set.add(e2);
System.out.println(set.size());   // 2

理想的散列函数应当具有均匀性,即不相等的对象应当均匀分布到所有可能的散列值上。这就要求了散列函数要把所有域的值都考虑进来,可以将每个域都当成 R 进制的某一位,然后组成一个 R 进制的整数。R 一般取 31,因为它是一个奇素数,如果是偶数的话,当出现乘法溢出,信息就会丢失,因为与 2 相乘相当于向左移一位。

一个数与 31 相乘可以转换成移位和减法: 31*x == (x<<5)-x,编译器会自动进行这个优化。

@Override
public int hashCode() {
    int result = 17;
    result = 31 * result + x;
    result = 31 * result + y;
    result = 31 * result + z;
    return result;
}
posted @ 2021-08-29 11:01  黄了的韭菜  阅读(45)  评论(0编辑  收藏  举报