对Object类中方法的深入理解

简介: 看一下API中关于Object的介绍: 类 Object 是类层次结构的根类。每个类都使用 Object 作为超类。 所有对象(包括数组)都实现这个类的方法。 那么Object中到底有哪些方法,各自有什么应用呢? 这个问题也经常出现在面试中,如果平时没有关注,可能很难回答好,这里简单整理一下。

看一下API中关于Object的介绍:

类 Object 是类层次结构的根类。每个类都使用 Object 作为超类。
所有对象(包括数组)都实现这个类的方法。

那么Object中到底有哪些方法,各自有什么应用呢?
这个问题也经常出现在面试中,如果平时没有关注,可能很难回答好,这里简单整理一下。

首先看一下java.lang.Object的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public  class  Object {
     private  static  native  void  registerNatives();
     static  {
         registerNatives();
     }
     /**
      * 创建并返回此对象的副本。
      * “复制”的准确含义可能根据对象的不同有变化。
      */
     public  final  native  Class<?> getClass();
     
     /**
      * 1.在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,
      * 前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。 
      * 2.如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。 
      * 3.如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode方法不 要求一定生成不同的整数结果
      * 但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
      */
     public  native  int  hashCode();
     public  boolean  equals(Object obj) {
         return  ( this  == obj);
     }
 
     protected  native  Object clone()  throws  CloneNotSupportedException;
     
     /**
      * 返回该对象的字符串表示
      */
     public  String toString() {
         return  getClass().getName() +  "@"  + Integer.toHexString(hashCode());
     }
 
     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 {
         if  (timeout <  0 ) {
             throw  new  IllegalArgumentException( "timeout value is negative" );
         }
         if  (nanos <  0  || nanos >  999999 ) {
             throw  new  IllegalArgumentException(
                                 "nanosecond timeout value out of range" );
         }
         if  (nanos >=  500000  || (nanos !=  0  && timeout ==  0 )) {
             timeout++;
         }
         wait(timeout);
     }
     public  final  void  wait()  throws  InterruptedException {
         wait( 0 );
     }
     /**
      * 当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法
      */
     protected  void  finalize()  throws  Throwable { }
     }
    

源码的注释非常详细,现在针对这几个方法做分析。

 

1.hashCode()和equals(Object obj)方法

1
public  native  int  hashCode();

native关键字表示该方法不是用java实现的,JDK源码中并不包含,对于不同的平台,这部分的实现也是不同的。

Java要实现对底层的控制,就需要其他语言的帮助,JVM将控制调用本地方法的所有细节。

hashCode方法的说明是:

Whenever it is invoked on the same object more than once during an execution of a Java application,the hashCode method must consistently return the same integer.

If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.

the programmer should be aware that producing distinct integer results
for unequal objects may improve the performance of hash tables.
开发人员应该知道,为不相等的对象产生不同的整数结果可能会提高哈希表的性能。
(这句没理解,再看)

This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the Java programming language.
hashcode一般是通过将该对象的内部地址转换成一个整数来实现的,不需要使用Java实现。

 

再来看equals()方法:

1
2
3
public  boolean  equals(Object obj) {
return  ( this  == obj);
}

java中==运算符作用在基本数据类型和引用数据类型是不同的,
基本数据类型 byte, short, char, int, long, float, double, boolean
他们之间的比较,应用双等号(==),比较的是他们的值。

引用数据类型用(==)进行比较的时候,
比较的是在内存中的存放地址,所以,除非是实例化的同一个对象比较后的结果才为true,否则比较后结果为false。
注意一下,String,Integer,Date这些类重写了equals()方法,不再是比较类在堆内存中的存放地址了。

以String为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public  boolean  equals(Object anObject) {
         if  ( this  == anObject) {
             return  true ;
         }
         if  (anObject  instanceof  String) {
             String anotherString = (String) anObject;
             int  n = value.length;
             if  (n == anotherString.value.length) {
                 char  v1[] = value;
                 char  v2[] = anotherString.value;
                 int  i =  0 ;
                 while  (n-- !=  0 ) {
                     if  (v1[i] != v2[i])
                             return  false ;
                     i++;
                 }
                 return  true ;
             }
         }
         return  false ;
     }

可以看到String对内部的字符数组进行了逐个字符的判断是否相等,

《Effective JAVA》中认为,99%的情况下,当你覆盖了equals方法后,请务必覆盖hashCode方法。

重写hashcode()方法:

1
2
3
4
5
6
7
8
9
10
11
12
public  int  hashCode() {
         int  h = hash;
         if  (h ==  0  && value.length >  0 ) {
             char  val[] = value;
 
             for  ( int  i =  0 ; i < value.length; i++) {
                 h =  31  * h + val[i];
             }
             hash = h;
         }
         return  h;
     }

当equals()方法被重写时,通常需要重写 hashCode 方法,以维护在hashCode 方法最开始的声明,即相等对象必须具有相等的哈希码

 

2.notify()/notifyAll()和wait()

wait()的作用是让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁。
而notify()和notifyAll()的作用,则是唤醒当前对象上的等待线程;notify()是唤醒单个线程,而notifyAll()是唤醒所有的线程。

wait(),notify() 和 notifyAll() 可以让线程协调完成一项任务。例如,一个线程生产,另一线程消费。生产线程不能在前一产品被消费之前运行,而应该等待前一个被生产出来的产品被消费之后才被唤醒,进行生产。同理,消费线程也不能在生产线程之前运行,即不能消费不存在的产品,所以应该等待生产线程执行一个之后才执行。利用这些方法,就可以实现这些线程之间的协调。

来看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public  class  WTThread  extends  Thread{
     public  WTThread(String name) {
         super (name);
     }
 
     public  void  run() {
         //同步锁
         synchronized  ( this ) {
             System.out.println(Thread.currentThread().getName()+ " call notify()" );
             /**
              * 这个线程执行的过程中会去唤醒当前对象的wait线程
              */
             notify();
         }
     }
}

 主线程的执行方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public  class  UseWaitNotify {
 
     public  static  void  main(String[] args)  throws  InterruptedException{
         WTThread t1= new  WTThread( "t1" );
         //同步锁
          synchronized (t1) {
             //启动线程t1
             System.out.println(Thread.currentThread().getName()+ " start t1" );
             t1.start();
             
            /**
             * 主线程进入等待状态
             * 同时wait()释放主线程持有的同步锁
             */
             System.out.println(Thread.currentThread().getName()+ " wait()" );
             t1.wait();
             /**
              * 主线程等待后,释放了锁,t1线程就可以执行了
              * 于是会操作 下面的执行体
              * System.out.println(Thread.currentThread().getName()+" call notify()");
              * notify();
              */
             /**
              * t1执行完毕后,唤醒主线程,主线程持有锁,继续执行
              * 打印查看目前正在执行的线程
              */
             System.out.println(Thread.currentThread().getName()+ " continue" );
          }
     }
}

  控制台输出:

main start t1
main wait()
t1 call notify()
main continue

这段代码来自这篇文章,Java多线程系列--“基础篇”05之 线程等待与唤醒

我还不是特别理解,先放在这里,这几个方法也都是native的,是调用的系统底层方法实现。

 

3.不同的wait()方法有什么区别

查看源码可以发现这里有三个不同wait方法:

1
2
3
4
5
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 {
         wait( 0 );
}

没有参数的 wait() 方法被调用之后,线程就会一直处于等待状态,直到本对象(就是 wait() 被调用的那个对象)调用 notify() 或 notifyAll() 方法。
相应的,wait(long timeout) 和wait(long timeout, int nanos) 方法中,
当等待时间结束或者被唤醒(无论哪一个先发生)时将会结束等待。

4.finalize()方法

1
2
3
4
5
/**
* Called by the garbage collector on an object when garbage collection
* determines that there are no more references to the object.
*/
protected  void  finalize()  throws  Throwable { }

可以看到这个方法是protected的。

特殊情况下,需要程序员实现finalize,当对象被回收的时候释放一些资源,比如:一个socket链接,在对象初始化时创建,整个生命周期内有效,那么就需要实现finalize,关闭这个链接。
使用finalize还需要注意一个事,调用super.finalize();
一个对象的finalize()方法只会被调用一次,而且finalize()被调用不意味着gc会立即回收该对象,所以有可能调用finalize()后,该对象又不需要被回收了,然后到了真正要被回收的时候,因为前面调用过一次,所以不会调用finalize(),产生问题。
一般来说,不推荐使用finalize()方法,它跟析构函数不一样。

>>getClass()方法

1
2
3
4
5
6
/**
* Returns the runtime class of this Object.
* The returned Class object is the object that is locked by
* "static""synchronized" methods of the represented class.
*/
public  final  native  Class<?> getClass();


返回这个Object对象的运行时类。

5.clone()方法

clone方法就是复制对象。所谓的复制对象,就是要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象。
这里涉及到浅拷贝和深拷贝的关系:

1
2
Person p =  new  Person( 23 "zhang" );
Person p1 = p;

这段代码中,P和P1其实是引用,指向同一个对象。

1
2
Person p =  new  Person( 23 "zhang" );
Person p1 = (Person) p.clone();

但是这段代码是创建了两个相同的对象,P和P1的引用分别指向不同的对象。

具体的可以查看这篇文章:
详解Java中的clone方法 

6.toString()方法

返回对象表示的字符串,实际上我们应用的都是对这个类的重写。

 


目录
相关文章
|
7月前
|
Java
Object类
Object类
33 1
|
8月前
|
Java
Java面向对象中 Object类的详解和其中的equals()和toString()方法的详解
Java面向对象中 Object类的详解和其中的equals()和toString()方法的详解
39 0
|
设计模式 Java
8. 说说 Object 类下面有几种方法呢?
8. 说说 Object 类下面有几种方法呢?
50 0
8. 说说 Object 类下面有几种方法呢?
|
算法 Java 索引
Object类的常用方法
1)==既可以判断基本类型,又可以判断引用类型 ①如果判断的是基本类型,判断的是值是否相等 ②如果判断引用类型,判断的是地址是否相等,即判断是不是同一个对象 2)equals是Object类中的方法,只能判断引用类型。默认判断的是地址是否相等,子类一般都重写该方法。
|
Java
详解Object类和抽象类上
详解Object类和抽象类上
64 0
详解Object类和抽象类上
|
存储 Java 程序员
详解Object类和抽象类下
详解Object类和抽象类下
109 0
详解Object类和抽象类下
重写toString()这个来自Object类的方法的意义
重写toString()这个来自Object类的方法的意义
84 0
|
程序员
Object类中的方法
Object类中的方法
|
IDE Java 开发工具
|
JavaScript 前端开发
31、Object 对象的相关方法
Object.getPrototypeOf方法返回参数对象的原型。这是获取原型对象的标准方法。
103 0