Object类以及Object类中的方法详解

object类

基本概念:

java.lang.Object类是所有类层次结构的根类,任何类都是java.lang.Object类的直接子类或者间接子类

  • 助记:万物皆对象(也就可以看做是所有类层次结构都是对象(Object)的子类或者间接子类)

常用方法(重点):

Object(构造方法):

  • Object()方法时Object类的构造方法

当一个类没有继承别的类(也就是没有其他父类时),这个类就是Object类的直接子类

  • 当一个没有继承其他类(也就是这个类是Object类的直接子类时,那么这个类中调用无参构造方法时在第一句加上super(),也就表示调用父类的构造方法,也就是调用了Object类的无参空构造方法(也就是调用了Object(); )

eg:

public class Aa{
    public Aa(){
        /*
        这里其实就是调用了Object类的无参空构造方法
        */
        super();
    }
}

成员方法():

第一个:

public boolean equals(Object obj)

  • equals方法会返回一个boolean类型的返回值,所以我们就需要定义一个boolean类型的变量去接收equals()方法的返回值

  • equals()方法是有参的,需要传递实参

用于判断调用的对象与传递的参数的对象的地址是否相同

equals(Object obj)中的Object类是所有类结构的直接或者间接父类,那么这里传递的实参就可以是任何一个类的实例化对象(也就是构成了多态)

  • equals方法在非空对象引用上实现判断相等

(不能是空引用调用,空引用调用任何类中的方法都会产生空指针异常,这里其实也就是调用java.lang包中的Object类中的equals方法)

调用方式

/*
这里可以通过任何一个对象去调用这个equals方法,因为所有的类都是Object类的直接子类或者间接子类,也就是所有的类都会继承Object类中被public修饰的方法,所以正在调用这个方法的对象的模板类也一定继承了equals方法
*/
对象名.equals(Object obj);
  • 该方法默认比较的是两个对象的地址,与==(逻辑运算符)运算符的效果相同

    eg:x.equals(A a) 这里的x只有与a是同一个存储空间,也就是只有x与a是同一个对象时x.equals(A a)的返回值才为true

    eg:

    //这里我们假设Student类中没有重写继承下来的equals方法
    Student s1=new Student();
    Student s2=new Student();
    System.out.println(s1.equals(s2));
    

    上面由于Student类中没有重写从Object类中继承下来的equals方法,那么也就是调用的Object类中的equals方法,那么业就是比较的两个对象地址是否相同(显然这里的s1和s2是两个不同的对象,那么也就是不同的地址空间存储的这两个对象),所以equals方法的返回值为false,所以打印的结果也就是false

  • 但是有的类中重写了equals方法(重写的方法不再是比较两个对象的地址是否相同,而是比较两个对象的内容是否相同),如String类,Date类,File类中都重写了Object类中的equals方法

​ eg:

   ```java

/*
这里调用的是String创建的对象,而我们前面讲过,String类中重写了equals方法,那么这个时候字符串类的对象调用这个equals方法时判断的将不再是两个对象的地址值,而是比较这两个对象的内容是否相同
*/
String str1=new String(“张三”);
String str2=new String(“李四”);
System.out.println(str1.equals(str2));
```

上面由于String类中重写了equals方法,那么这个时候equals方法不再比较两个对象的地址值是否相同,而是比较的这两个对象的内容是否相等(显然这里的str1和str2虽然是两个不同的对象,但是他们的实际存储内容都是张三),所以这里的equals方法返回值为true,那么这里打印结果也就是true

equals方法的特性(了解):

  1. 自反性
  2. 对称性
  3. 传递性
  4. 一致性
  5. 对于任何的非空引用值x,x.equals(null)的返回值都为false

equals方法默认比较的是地址值,想要让equals比较实际内容,那么就需要重写equals方法

注意:调用equals()方法的对象不能为空,但是equals()方法的参数可以为空,也就是可以为null,但是当equals()方法的参数为空时,equals()方法的返回值就一定为false

equals方法重写的思路(理解):

1.当调用对象和参数的地址相同时,那么内容也一定相同(也就是返回true)

2.当调用对象不为空而参数为空时,那么这个时候一定会返回false

3.当参数对象和调用equals()方法的对向的类型相同时才需要比较它们的内容是否相同

  • 在上面我们一般通过instanceof关键字判断调用equals()方法的对象和参数对象是否相同

4.若类型调用对象和参数对象的类型都不相同,那么也就不用比较它们的内容了(对象类型不相同,一般它们的内容也不会相同)

eg(重写代码(示例))(标准版):

public boolean equals(Object obj){
    if(this=obj){
        return true;
    }
    if(obj==null)
    {
        return false;
    }
    if(this instanceof student){
        if(this.getId()==obj.getId()){
            return turn;
        }else{
            return false;
        }
    }
    return false;
}

equals方法重写代码优化

public boolean equals(Object obj){
    if(this==obj)return true;
    if(obj==null)return false;
    if(this instanceof obj){
        return true;
    }
    return false;
}

第二个:

public int hashcode()

hashcode()方法是无参的,不需要传递实参

hashcode()方法会返回一个int类型的变量,会返回调用对象的哈希码值(也就是内存地址的编号)

  • 注意hashcode()方法返回的是调用对象的内存地址的编号(哈希码值),但不是内存地址

java官方关于Hashcode()方法的常规协定

第一: 若调用equals()方法返回的结果为true,那么这个调用equals()方法的对象和这个equals()方法中的参数对象分别调用hashcode()方法返回的结果相同

第二:若调用equals()方法返回的结果为false时,那么这个调用equals()方法的对象和这个equals()方法中的参数对象分别调用hashcode()方法的返回结果应该是要不相同(但是也有可能会出现返回的哈希码值相同的情况,因为这个毕竟是哈希码值,我们数据结构中讲过,有可能会出现输入不同的值,但是返回的哈希值却相同的情况下也是会有的,但是我们由于要顺应java官方的协定,也就是最好让输入不同值时的返回值也不同,这样可以提升哈希表的性能)

  • 哈希表的性能(出错越少,越能避免输入两个不同值,输出却是同一个值的情况(或者是输入同一个值却返回了两个不同的值的情况),那么这个哈希表的性能也就越高)

注意:重写了equals()方法之后,我们应该同样去重写hashcode()方法来维护hashcode()方法的常规协定(也就是equals()方法的输出结果与hashcode()方法的输出结果相对应)

hashcode方法的重写

eg1:(耍流氓式的写法):

public int hashcode(){
    /*
    这里就是返回了这个对象的一个属性,这个属性叫做id,就是这个对象的工号
    */
    return getId();
} 

eg2:(间接耍流氓的方式):

public int hashcode(){
    int temp=12;
    /*
    这里就类似于时一个哈希函数,通过哈希函数计算出具体的哈希值
    */
    return temp*31+getId();
}
  • 当我们在类中重写了hashcode方法后,则调用重写的版本不再代表内存编号了,现在hashcode方法的返回值是什么就取决于你在这里具体写了什么

  • java官方要求hashcode()方法与equals()方法保持一致(也就是重写equals方法那么也就要重写hashcode方法)

  • 在java的继承结构中java.lang.Object类位于继承顶端(也就是Object类是所有类的根类)

第三个:

public String toString()

  • 该方法的返回字符串类型的值
  • 这个这个方法没有参数,所以就不需要传递参数

toString()方法的功能:

toString()方法返回的是对象的字符串表示(返回的字符串为:包名.类名@哈希码值的十六进制形式)

  • 为了返回一些更有意义的数据我们就需要去重写toSting()方法

eg:

public String toString(){
    return "student[Id="+getId+",name="+getName+"]"
}

  • 我们以后就可以使用toSting()方法来代替show()方法来打印信息

其实也就是通过在类中重写toSting()方法来代替了show()方法

关于toString()方法在使用中的两个特例:

第一个:当System.out.println()中使用了字符串内容与引用相连接时,这个引用会自动调用toString()方法

eg:

System.out.println("s1="+s1);//这里的s1是一个对象,也就是一个引用(这个引用指向了一个实例化对象)
//那么这里会自动通过s1对象调用toString()方法来得到结果
//如果没有进行重写那么这里就会输出 这个包名.类名@这个对象(也就是s1)的哈希码值
/*
哈希码值:就是这个对象储存地址的编码,而不是这个对象的存储地址
*/

第二个:当调用println()方法或者print()方法打印引用时,这个引用会自动调用toString()方法

eg:

System.out.println(s1);//这里就会输出s1的字符串表示,也就是所在包名.类名@s1对象的哈希码值

  • 总结:这两个特征可以归结为一句话:

java在使用System.out.println()打印对象时,或者打印对象连接字符串时,都会默认调用toString()方法,如果没有重写toString()方法,那么就会输出对象的字符串表示(也就是包名.类名@对象哈希码值(哈希码值也就是对象的内存地址编码,但不是内存地址))

第四个(finalize()方法不重要,非必要不使用):

protected void finalize() thows Throwable

finalize方法在jdk1.9就已经过时了

标记finalize()方法已经过时的标记:

@Deprecated(since=“9”)

finalize()方法的需要注意的事项

  1. finalize()方法通过protected修饰符进行了修饰,这个方法在Object类中的源代码中只有一个方法体但没有内容,内容需要程序员进行重写时再具体书写
  2. 这个方法不需要程序员手动调用,java的垃圾回收器(GC)会自动调用这个方法
  3. finalize()方法的执行时机:当一个对象即将被垃圾回收器回收的时候垃圾回收器会自动调用这个方法
  4. finalize()方法其实就是sun公司为程序员提供的一个时机(垃圾回收时机)
  • 如果需要在对象回收之前执行一段代码的话,这段代码就可以写到重写的finalize()方法中
  • 这个finalize()方法有点类似于静态代码块,静态代码块就是提供了一个类加载的时机(还有实例代码块,实例化代码块提供了一个对象创建的时机)

什么时候使用finalize()方法(现在一般不会使用了,因为在jdk1.9之后finalize()方法就已经过时了):

当一个业务要求让每一个对象销毁之前打印出对象销毁的时间(这里的时间精确到毫秒)的话,我们就需要重写finalize()方法并调用

  • finalize()方法是java官方为程序员提供的一个对象销毁时机,在java的垃圾回收器需要回收一个对象的时候java

垃圾回收器就会自动调用这个finalize()方法,这个finalize()方法如果你不重写的话就是什么都没有,什么也看不到,当你需要在对象被回收时机做点什么时就需要你去重写这个finalize()方法

上一篇:java中equals和hashCode方法为什么总是需要重写?


下一篇:为什么重写equals方法,还必须要重写hashcode方法(面试题)