对Object类中方法的深入理解
看一下API中关于Object的介绍:
类 Object 是类层次结构的根类。每个类都使用 Object 作为超类。
所有对象(包括数组)都实现这个类的方法。
那么Object中到底有哪些方法,各自有什么应用呢?
这个问题也经常出现在面试中,如果平时没有关注,可能很难回答好,这里简单整理一下。
首先看一下java.lang.Object的源码:
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 { }
}
源码的注释非常详细,现在针对这几个方法做分析。
>>hashCode()和equals(Object obj)方法
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()方法:
public boolean equals(Object obj) {
return (this == obj);
}
java中==运算符作用在基本数据类型和引用数据类型是不同的,
基本数据类型 byte, short, char, int, long, float, double, boolean
他们之间的比较,应用双等号(==),比较的是他们的值。
引用数据类型用(==)进行比较的时候,
比较的是在内存中的存放地址,所以,除非是实例化的同一个对象比较后的结果才为true,否则比较后结果为false。
注意一下,String,Integer,Date这些类重写了equals()方法,不再是比较类在堆内存中的存放地址了。
以String为例:
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()方法:
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 方法最开始的声明,即相等对象必须具有相等的哈希码。
1.notify()/notifyAll()和wait()
wait()的作用是让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁。
而notify()和notifyAll()的作用,则是唤醒当前对象上的等待线程;notify()是唤醒单个线程,而notifyAll()是唤醒所有的线程。
wait(),notify() 和 notifyAll() 可以让线程协调完成一项任务。例如,一个线程生产,另一线程消费。生产线程不能在前一产品被消费之前运行,而应该等待前一个被生产出来的产品被消费之后才被唤醒,进行生产。同理,消费线程也不能在生产线程之前运行,即不能消费不存在的产品,所以应该等待生产线程执行一个之后才执行。利用这些方法,就可以实现这些线程之间的协调。
来看一个例子:
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();
}
}
}
主线程的执行方法:
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的,是调用的系统底层方法实现。
2.不同的wait()方法有什么区别
查看源码可以发现这里有三个不同wait方法:
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) 方法中,
当等待时间结束或者被唤醒(无论哪一个先发生)时将会结束等待。
3.finalize()方法
/**
* 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()方法,它跟析构函数不一样。
4.getClass()方法
/**
* 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方法就是复制对象。所谓的复制对象,就是要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象。
这里涉及到浅拷贝和深拷贝的关系:
Person p = new Person(23, "zhang");
Person p1 = p;
这段代码中,P和P1其实是引用,指向同一个对象。
Person p = new Person(23, "zhang");
Person p1 = (Person) p.clone();
但是这段代码是创建了两个相同的对象,P和P1的引用分别指向不同的对象,
但是Clone执行的方法是浅拷贝,需要注意这个,P和P1里的name字段指向的是同一块堆内存。

具体的可以查看这篇文章:
详解Java中的clone方法
6.toString()方法
返回对象表示的字符串,实际上我们应用的都是对这个类的重写。
对Object类中方法的深入理解的更多相关文章
- Object类中方法详解
目录 概述 hashCode方法 getClass方法 toString方法 equals方法 clone方法 finalize方法 概述 Object 是类层次结构的根类.每个类都使用 Object ...
- Object类clone方法的自我理解
网上搜帖: clone()是java.lang.Object类的protected方法,实现clone方法: 1)类自身需要实现Cloneable接口 2)需重写clone()方法,最好设置修饰符mo ...
- Java:面向对象(继承,方法的重写(overide),super,object类及object类中方法的重写,父子类代码块执行顺序)
继承: 1.继承是对某一匹类的抽象,从而实现对现实世界更好的建模. 2.提高代码的复用性. 3.extends(扩展),子类是父类的扩展. 4.子类继承父类可以得到父类的全部属性和方法.(除了父类的构 ...
- java Object类中方法介绍
- 【Java基础之Object类(一)】Java中Object类中的所有方法(toString、equals、hashCode、clone、finalize、wait和notify等)详解(转载)
java中的hashcode.equals和toString方法都是基类Object的方法. 首先说说toString方法,简单的总结了下API说明就是:返回该对象的字符串表示,信息应该是简明但易于读 ...
- Object类中wait代餐方法和notifyAll方法和线程间通信
Object类中wait代餐方法和notifyAll方法 package com.yang.Test.ThreadStudy; import lombok.SneakyThrows; /** * 进入 ...
- 重写Object类中的equals方法
Object是所有类的父亲,这个类有很多方法,我们都可以直接调用,但有些方法并不适合,例如下面的student类 public class Student { //姓名.学号.年纪 private S ...
- Java基础(43):Java中的Object类与其方法(转)
Object类 java.lang.Object java.lang包在使用的时候无需显示导入,编译时由编译器自动导入. Object类是类层次结构的根,Java中所有的类从根本上都继承自这个类. O ...
- -1-5 java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait(),notify(),notifyAll()等方法都定义在Object类中
本文关键词: java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait( ...
随机推荐
- $state.go页面不刷新数据
http://blog.csdn.net/WenJimmy/article/details/51027952 假如进入market/beian/add添加数据,保存提交后回退market/beian列 ...
- Linux 之 shell 比较运算符
运算符 描述 示例 文件比较运算符 -e filename 如果 filename 存在,则为真 [ -e /var/log/syslog ] -d filename 如果 filename 为目录, ...
- cvLoadImage
编辑 本词条缺少名片图,补充相关内容使词条更完整,还能快速升级,赶紧来编辑吧! 函数原型:IplImage* cvLoadImage( const char* filename, int flags= ...
- POJ 1509 Glass Beads
Description 求字符串的最小循环表示. Sol SAM. 把原串复制一遍,建出SAM,然后每次选最小的一个跑 \(len\) 次,这就是最小循环表示的最后一个节点,然后 \(x-len+1\ ...
- uploadify插件的功能应用
一.相关key值介绍 uploader:uploadify.swf文件的相对路径,该swf文件是一个带有文字BROWSE的按钮,点击后淡出打开文件对话框,默认值:uploadify.swf. scri ...
- 腾讯云环境配置之PHP5.6.3 + redis扩展 稳定版
腾讯云环境配置之PHP5.6.3 + redis扩展 稳定版 时间:2015-01-18 01:41来源:linux.it.net.cn 作者:IT #由于上文装过yum groupinstall ...
- CSS——display和float
1.display 属性规定元素应该生成的框的类型. 值 描述 none 此元素不会被显示. block 此元素将显示为块级元素,此元素前后会带有换行符. inline 默认.此元素会被显示为内联元素 ...
- ACM/ICPC 之 DFS求解欧拉回路+打表(POJ1392)
本题可以通过全部n位二进制数作点,而后可按照某点A的末位数与某点B的首位数相等来建立A->B有向边,以此构图,改有向图则是一个有向欧拉回路,以下我利用DFS暴力求解该欧拉回路得到的字典序最小的路 ...
- nyoj1000_快速幂_费马小定理
又见斐波那契数列 时间限制:1000 ms | 内存限制:65535 KB 难度:4 描述 斐波那契数列大家应该很熟悉了吧.下面给大家引入一种新的斐波那契数列:M斐波那契数列. M斐波那契数列 ...
- 跨浏览器的事件对象-------EventUtil 中的方法及用法
什么是EventUti----封装好的事件对象 在JavaScript中,DOM0级.DOM2级与旧版本IE(8-)为对象添加事件的方法不同 为了以跨浏览器的方式处理事件,需要编写一段“通用代码”,即 ...