类注释
package java.lang;
/**
* Class {@code Object} is the root of the class hierarchy.
* Every class has {@code Object} as a superclass. All objects,
* including arrays, implement the methods of this class.
*
* @author unascribed
* @see java.lang.Class
* @since JDK1.0
*/
Object类是类结构的根,所有的类都将继承(显式或者隐式)这个类。所有的对象及数组都会实现这个类的方法。
方法及代码块
registerNatives()
private static native void registerNatives();
static {
registerNatives();
}
这段代码并没有注释。可以看到registerNatives()这个方法被native修饰,这意味着这个方法并不是使用java实现的,它是一个本地方法,是由C/C++之类的其他语言编译而来的。在Java中调用其它语言编写的程序需要将其加载到动态链接库,然后通过定位的方式找到需要调用的程序。registerNatives()的功能就是定位,它将需要的本地方法的链接保存下来,通过这些链接便可以访问具体的本地方法。
getClass()
/**
* Returns the runtime class of this {@code Object}. The returned
* {@code Class} object is the object that is locked by {@code
* static synchronized} methods of the represented class.
*
* <p><b>The actual result type is {@code Class<? extends |X|>}
* where {@code |X|} is the erasure of the static type of the
* expression on which {@code getClass} is called.</b> For
* example, no cast is required in this code fragment:</p>
*
* <p>
* {@code Number n = 0; }<br>
* {@code Class<? extends Number> c = n.getClass(); }
* </p>
*
* @return The {@code Class} object that represents the runtime
* class of this object.
* @jls 15.8.2 Class Literals
*/
public final native Class<?> getClass();
返回这个对象运行时的类.这个方法需要注意这一句:
The actual result type is {@code Class<? extends |X|>} where {@code
|X|} is the erasure of the static type of the expression on which
{@code getClass} is called.”
调用getClass()时真正返回的运行时的类。假设有这么两个类:Person、Boy。Boy基础了Person。Person zs= new Boy();
那么调用zs.getClass()
返回的将是Boy类。
hashCode()
/**
* Returns a hash code value for the object. This method is
* supported for the benefit of hash tables such as those provided by
* {@link java.util.HashMap}.**/
public native int hashCode();
注释很长,截取其中一部分。返回这个对象的哈希码,这个方法的支持得益于哈希表。在Java应用程序多次调用同一个对象的hashCode()方法时必须返回相同的整数(哈希码).这也就意味着,在Java应用程序的生命周期内,哈希码可以作为它的唯一标识,用于和其它对象比较。如equals()方法中一般会默认调用此方法。
class Boy{
String name;
public Boy(String name){
this.name=name;
}
}
public class Test {
public static void main(String[] args){
Boy boy1=new Boy("张三");
Boy boy2=new Boy("张三");
System.out.println(boy1.equals(boy2));
}
}
输出:false
解释:因为boy1和boy2并不是同一个对象,虽然他们内部的属性值一样,
但是哈希码并不相同,在调用equals方法时,默认对比其哈希码是否相同,
若不同则返回false。
equals(Object obj)
这个方法主要用于对比两个对象是否相同,可以覆写此方法根据程序员的意愿来判断两个对象是否相同。在之前的代码中做出一点更改,假设身份证号码相同我们就认为他们是同一个人:
class Boy{
String name;
String id;
public Boy(String name,String id){
this.name=name;
this.id=id;
}
@Override
public boolean equals(Object obj) {
Boy boy=(Boy) obj;
if (boy.id==this.id)
return true;
else return false;
}
}
public class Test {
public static void main(String[] args) throws Throwable {
Boy boy1=new Boy("张三","123456789");
Boy boy2=new Boy("李四","123456789");
System.out.println(boy1.equals(boy2));
}
}
输出:true
解释:我们覆写了equals方法,在这个方法中只比较了身份证号,
身份证相同就返回ture;
clone()
/*
* @return a clone of this instance.
* @throws CloneNotSupportedException if the object's class does not
* support the {@code Cloneable} interface. Subclasses
* that override the {@code clone} method can also
* throw this exception to indicate that an instance cannot
* be cloned.
* @see java.lang.Cloneable
*/
protected native Object clone() throws CloneNotSupportedException;
这个方法返回实例的一个副本,但是克隆分为浅克隆和深克隆。对象实例都存储在Java堆中,clone()则是在Java堆中申请一个相同大小的堆内存,并且把整个实例的内容都复制进去。但是Boy实例boy1中只保存了指向girl的一个引用,并不是将整个girl都放进了boy1中,所以克隆后girlFriend指向的是同一个girl。
- 浅克隆
class Boy implements Cloneable {
String name;
Girl girlFriend;
public Boy(String name, String id,Girl girlFriend) {
this.name = name;
this.girlFriend=girlFriend;
}
@Override
protected Boy clone() throws CloneNotSupportedException {
Boy boy=(Boy)super.clone();
return boy;
}
}
class Girl {
String name;
public Girl(String name) {
this.name = name;
}
}
public class Test {
public static void main(String[] args) throws Throwable {
Girl girl=new Girl("小红");
Boy boy1 = new Boy("小明", "123456789",girl);
Boy boy2 = boy1.clone();
System.out.println(boy1.hashCode()==boy2.hashCode());
System.out.println(boy1.girlFriend.hashCode()==boy2.girlFriend.hashCode());
}
}
输出:false
true
解释:可以看到boy2确实与boy1不再是同一个对象了,
但是由于浅克隆只是复制了引用,所以它们的girlFriend指向的对象仍然是同一个对象。
- 深克隆
//boy的克隆方法
protected Boy clone() throws CloneNotSupportedException {
Boy boy = (Boy) super.clone();
boy.girlFriend=(Girl)boy.girlFriend.clone();
return boy;
}
//gile的克隆方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
//其它代码与之前相同
运行结果:false
false
深克隆与浅克隆的区别就在于深克隆将实例中的引用指向的实例也克隆一份。
toString()
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
这个方法是Object中少有的非本地方法,当然,里面也调用了其它的本地方法。它默认返回类名及调用者的哈希码。推荐所有的子类都覆写此方法,覆写时尽量返回一个简单但信息丰富的提示,并且易读性高。在需要将对象转换成字符串时默认调用此方法。
public static void main(String[] args) throws Throwable {
Girl girl = new Girl("小红");
Boy boy1 = new Boy("小明", "123456789", girl);
String boyString="boyString:"+boy1;
System.out.println(boyString);
}
输出:boyString:Boy@4554617c
notify()
此方法也是一个本地方法。这个方法唤醒一个在此对象监视器上等待的线程,若是有多个线程等待,则由虚拟机自行选择其中一个唤醒。此方法只能由作为此对象监视器所有者的线程调用,如果不符合条件就会抛出IllegalMonitorStateException
,那么怎么成为它的监视器所有者的线程呢?
- By executing a synchronized instance method of that object.
- By executing the body of a {@code synchronized} statement that synchronizes on the object.
- For objects of type {@code Class,} by executing a synchronized static method of that class.
这是源码上的注释,大意就是可以通过三种方式获取这个对象的监视器
1.执行这个对象的同步实例方法
2.执行这个对象中的同步方法块
3.执行该类的静态同步方法
notifyAll()
notify()唤醒单个线程,而这个方法则是唤醒所有在这个对象监视器上等待的线程。
wait()
public final native void wait(long timeout) throws InterruptedException;
使当前线程等待,可以通过一下几个方式唤醒它:
- 另外一个线程调用notify()或者notifyAll()方法
- 设置的timeout时间已过
- 另外一个线程调用interrupt()来中断此线程
如果设置的timeout为0,则不考虑时间,只等待唤醒。此方法会导致当前线程将自身置于此对象的等待集中,然后放弃对此对象的所有同步声明。还有一种特殊的唤醒方式叫做spurious wakeup
虚假唤醒,这种情况非常少见,但是也必须考虑到这种情况,以免发生错误。源码注释中给出样例:
synchronized (obj) {
while (condition does not hold)
obj.wait(timeout);
... // Perform action appropriate to condition
}
当条件不满足时应该继续等待。
wait()方法还有另一个:
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);
}
它仍然调用了 wait(long timeout)方法,但是它提供了一种更加精确的控制方法,nanos代表的是纳秒。真正等待的时间为1000000*timeout+nanos
纳秒。
finalize()
源码注释中是这样写的:
Called by the garbage collector on an object when garbage collection determines that there are no more references to the object.
当没有任何的引用指向此对象时,垃圾收集器回收此对象时会调用这个方法。那么我们可以在这个方法中做点手脚用来“逃脱”垃圾收集器的回收,比如让其它引用指向这个对象。但是并不建议这么做,一般在此方法中只做一些资源释放的操作。Java编程语言不保证哪个线程将为任何给定对象调用finalize()方法,但是当有线程调用这个方法时,它将不会持有任何用户可见的同步锁。如果调用这个方法时引发以了一些不被捕获的异常,那么将会忽略这个异常,并且终止对这个对象的终结操作。
这个方法调用后,在JVM再次确认没有任何未死亡的线程可以访问到此对象之前不会采取进一步的操作。