Object类:
<strong>public class Object</strong>
类 Object
是类层次结构的根类。每个类都使用 Object
作为超类。所有对象(包括数组)都实现这个类的方法。
构造方法摘要 | |
---|---|
Object() |
方法摘要 | |
---|---|
protected Object | clone() 创建并返回此对象的一个副本。 |
boolean | equals(Object obj) 指示其他某个对象是否与此对象“相等”。 |
protected void | finalize() 当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。 |
Class<?> | getClass() 返回此 Object 的运行时类。 |
int | hashCode() 返回该对象的哈希码值。 |
void | notify() 唤醒在此对象监视器上等待的单个线程。 |
void | notifyAll() 唤醒在此对象监视器上等待的所有线程。 |
String | toString() 返回该对象的字符串表示。 |
void | wait() 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。 |
void | wait(long timeout) 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。 |
void | wait(long timeout, int nanos) 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。 |
由于Object类当中使用的native方法比较多,源码代码确实没有看到太多,下面参考Object.class来看下实现的方法:
首先我们定义一个CObject继承Object:
public class CObject extends Object
创建一个空的构造方法:
/**
* 构造方法
*/
public CObject(){
}
下面是参照Object.class的源码来实现的:
clone方法:
/**
* 创建并返回此对象的一个副本
* Object,212行
*/
@Override
protected native Object clone() throws CloneNotSupportedException;
由于是native的方法,没有看到太多的实现方法。
equals方法:
/**
* 指示其他某个对象是否与此对象“相等”
* Object,148行
*/
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
return (this == obj);
}
finalize方法:
/**
* 当垃圾回收器确定不存在该对象的更多引用时,由对象的垃圾回收器调用此方法
* Object,555行
*/
@Override
protected void finalize() throws Throwable { }
getClass方法:
/**
* 返回此Object的运行时类
* 由于是final类型类,不能实现复写,由于是native的类,并不能看到源码,方法定义如下
* Object,63行
*/
//public final native Class<?> getClass();
hashCode方法:
/**
* 返回该对象的哈希码值,也是natvie方法
* Object,100行
*/
public native int hashCode();
notify和notifyAll方法:
/**
* 唤醒在此对象监视器上等待的单个线程
* 由于是native final方法,无法复写
* Object,271行
*/
//public final native void notify();
/**
* 唤醒在此对象监视器上等待的所有线程
* 由于是native final方法,无法复写
* Object,295行
*/
//public final native void notifyAll();
toString方法:
/**
* 返回该对象的字符串表示
* Object,235行
*/
@Override
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
wait方法:
/**
* 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
* Object,501行
*/
// public final void wait() throws InterruptedException {
// wait(0);
// }
/**
* 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。
* Object,382行
*/
// public final native void wait(long timeout) throws InterruptedException;
/**
* 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。
* Object,446行
*/
// 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 > 0) {
// timeout++;
// }
//
// wait(timeout);
// }
这边所有的方法都没有什么好说的,实现的代码不多,有机会的话把native里面的实现也看一下,下面测试代码:
为了区别不同的对象是不同的,我们在CObject当中再添加两个方法:
public String name = "cObject";
/**
* 自定义方法,获取一个名字区别不同的对象
* @return
*/
public String getName(){
return name;
}
/**
* 自定义方法,设置一个名字区别不同的对象
* @param name
*/
public void setName(String name){
this.name = name;
}
测试clone和equals方法:
clone由于是protect的,并不能直接实现,后面放在cloneable接口或segment类当中来进行详细解释。
由于要使用到clone方法,必须要实现cloneable接口,否则会报出CloneNotSupportedException的错误:
public class CObject extends Object implements Cloneable{
下面是测试代码:
/**
* 测试equals
* @param cObject
*/
public static void testEquals(CObject cObject){
CObject newObject = cObject;
//判断两个Object是否相等
isEqual(cObject, newObject);
Object o = null;
try {
//创建cObject的一个副本
o = cObject.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
//将副本的类型转换成CObject
newObject = (CObject)o;
//判断两个Object是否相等
isEqual(cObject,newObject);
System.out.println("newObect里面的名字为:" +newObject.name);
}
结果为:
等于true的时候是我们直接把cObject的值直接赋值给的newObject,所以相等。
等于false时,我们是用clone方法复制了一个cObject的副本,然后赋值给newObject,所以不相等;我们最初给cObject里面赋值了一个name为cObject,所以,在newObject里面的name也为cObject,证明clone方法是复制了一个cObject的副本的。
测试finalize方法:
测试结果是使用和没有使用finalize方法都没有任何的结果,对象依然存在,资源并没有被释放掉,原因是Object里面的方法是这样定义的:
/**
* 当垃圾回收器确定不存在该对象的更多引用时,由对象的垃圾回收器调用此方法
* Object,555行
*/
@Override
protected void finalize() throws Throwable { }
finalize
方法,以配置系统资源或执行其他清除。
finalize 的常规协定是:当 JavaTM 虚拟机已确定尚未终止的任何线程无法再通过任何方法访问此对象时,将调用此方法,除非由于准备终止的其他某个对象或类的终结操作执行了某个操作。finalize 方法可以采取任何操作,其中包括再次使此对象对其他线程可用;不过,finalize 的主要目的是在不可撤消地丢弃对象之前执行清除操作。例如,表示输入/输出连接的对象的 finalize 方法可执行显式 I/O 事务,以便在永久丢弃对象之前中断连接。
Object 类的 finalize 方法执行非特殊性操作;它仅执行一些常规返回。Object 的子类可以重写此定义。
Java 编程语言不保证哪个线程将调用某个给定对象的 finalize 方法。但可以保证在调用 finalize 时,调用 finalize 的线程将不会持有任何用户可见的同步锁定。如果 finalize 方法抛出未捕获的异常,那么该异常将被忽略,并且该对象的终结操作将终止。
在启用某个对象的 finalize 方法后,将不会执行进一步操作,直到 Java 虚拟机再次确定尚未终止的任何线程无法再通过任何方法访问此对象,其中包括由准备终止的其他对象或类执行的可能操作,在执行该操作时,对象可能被丢弃。
对于任何给定对象,Java 虚拟机最多只调用一次 finalize 方法。
finalize
方法抛出的任何异常都会导致此对象的终结操作停止,但可以通过其他方法忽略它。
也就是说,finalize当中应该是需要自己实现具体回收的内容,这个方法只是JVM在GC前会调用这个方法而已。
测试hashCode、toString和getClass方法:
/**
* 测试hashCode和toString方法
* @param cObject
*/
private static void testHashCode(CObject cObject){
System.out.println(String.format("hashCode为%d,toString为:%s", cObject.hashCode(),cObject.toString()));
}
结果为:
因为toString方法是这样实现的:
return getClass().getName() + "@" + Integer.toHexString(hashCode());
所以getClass获取的路径就是oathchen.lang.cls.CObject,toHexString方法放在Integer类里面来说。
测试wait和notify方法:
/**
* 测试wait和notify方法
* @param cObject
*/
private void testWaitAndNotify(CObject cObject){
final CObject synObj = cObject;
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized(synObj) {
System.out.println("T1获取synObj的对象监视器,开始执行同步块");
try {
System.out.println("T1在 wait()时挂起了");
synObj.wait();
System.out.println("T1被T2唤醒后并重新获得synObj的对象监视器,继续执行");
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("T1获取synObj的对象监视器,结束同步块");
}
};
});
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("T2启动,但是因为T1占用了synObj的对象监视器,则等待T1执行synObj.wait来释放它");
synchronized(synObj) {
try {
System.out.println("在T1执行synObj.wait后,T2获取synObj的对象监视器,进入同步块");
synObj.notify();
System.out.println("T2执行synObj.notify(),T1被唤醒,但T2还在同步块中,没有释放synObj的对象监视器,T1等待synObj的对象监视器");
// TimeUnit.MINUTES.sleep(1);
System.out.println("T2结束同步块,释放synObj的对象监视器,T1获取到synObj的对象监视器,并执行wait后面的操作");
}catch(Exception e) {
e.printStackTrace();
}
}
};
});
t2.start();
}
结果为:
这段代码是借鉴http://xiemingmei.iteye.com/blog/1073940上面的内容,我自己也试着写了一个,发现效果不是很好,就用了这个,解释的比较清楚。我跟上面代码写的不一样的地方是我是直接extends Thread而且是通过构造方法传递的CObect参数进入Thread里面的,出了一些问题:
//自定义一个线程
class MyThread extends Thread{
//一个线程当中有一个CObject数据
CObject cObject = new CObject();//此处必须要new一个初始化,否则会报出空指针,虽然该变量已在main里面初始化过了
//计数
int count;
//获取当前的计数
public int getCount(){
return count;
}
//初始化方法
public MyThread(CObject cObject){
cObject = this.cObject;
}
//复写run方法
@Override
public void run() {
synchronized(cObject){
for(int i=0;i<10;i++){
// 先睡眠一秒
try {
sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//使用hashCode判断不同的线程
System.out.println(this.hashCode() +":"+ count++);
if(count == 5){
try {
System.out.println("i am waiting");
cObject.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
相当于在这个Thread里面我定义了一个局部变量cObject,并没有初始化,而我同步的是局部变量的cObject,编译器应该是这样判断出空指针异常的,虽然我通过构造方法传递进来的值是初始化过的。