Java object

介绍Java中的object类。

RegisterNatives

RegisterNatives用于寻找本地函数。

StackOverflow

getClass

getClass用于返回对象的运行时类型。

StackOverflow

hashCode

hashCode用于标记对象的唯一值,可用于确定对象在HashTable中的位置,默认值对象内存地址编号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public int hashCode()
{
final int PRIME = 31;
return new HashCodeBuilder(getId()%2==0?getId()+1:getId(), PRIME).toHashCode();
}
@Override
public boolean equals(Object o) {
if (o == null)
return false;
if (o == this)
return true;
if (o.getClass() != getClass())
return false;
return new EqualsBuilder().append(getId(), ((Demo)o).getId()).isEquals();
}

equals

equals用于比较两个对象是否相等。

注意:如果重写equals方法必须重写hashCode方法。

“==”与equals的不同

对于基本类型来说,==比较的是值的大小

对于对象(引用)来说,==比较的是对象地址

equals无法用于基本类型。

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

clone

clone用于对象的复制,具体分为两步:

  • 申请同样大小的内存空间;
  • 再新的内存空间创建一个相同内容的对象。

new与clone的区别

new与clone的区别在于内存空间填充的方式

new的核心是分配内存,大体步骤如下:

  • 检查被创建对象的类型;
  • 申请指定类型的内存空间;
  • 调用构造函数,填充对象内容;
  • 返回对象的引用(地址)。

clone的核心是内存复制,大体步骤如下:

  • (同new)检查被创建对象的类型;
  • (同new)申请指定类型的内存空间;
  • 拷贝源对象的内存到新的内存空间;
  • (同new)返回对象的引用(地址)。

注意:clone是浅拷贝,也就是只拷贝基本类型的值以及对象的引用。

深拷贝

为了实现深拷贝,需要实现Clonable接口、覆盖并实现clone方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static class Body implements Cloneable{  
public Head head;
public Body() {}
public Body(Head head) {this.head = head;}

@Override
protected Object clone() throws CloneNotSupportedException {
// 不仅复制当前对象,而且复制当前对象的引用属性
Body newBody = (Body) super.clone();
newBody.head = (Head) head.clone();
return newBody;
}

}
static class Head implements Cloneable{
public Face face;

public Head() {}
public Head(Face face){this.face = face;}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

嵌套对象的深拷贝是需要把每一层嵌套的对象、属性均拷贝才能实现。

此外,利用Json序列化与反序列化也可以实现相同的功能,不过性能较差。

toString

默认情况下,返回格式如下,

1
getClass().getName() + '@' + Integer.toHexString(hashCode())

一般建议每一个类都重写该方法,便于开发和调试。

Object lock

所有对象都拥有一把对象锁

JVM负责跟踪对象被加锁的次数。如果一个对象被解锁,其计数变为0。在任务(线程)第一次给对象加锁的时候,计数变为1。每当这个相同的任务(线程)在此对象上获得锁时,计数会递增。

只有首先获得锁的任务(线程)才能继续获取该对象上的多个锁。

每当任务离开一个synchronized方法,计数递减,当计数为0的时候,锁被完全释放,此时别的任务就可以使用此资源。

waitnotifynotifyAllJava语言提供的实现线程间阻塞(Blocking)和控制进程内调度(inter-process communication)的底层机制。

wait

1
Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.

wait方法可用来改变当前线程到Wating or TimedWating状态。

wait方法必须在拿到对象锁才能执行,也就是所谓的在synchronized内使用。

在调用wait方法之前,线程必须要获得该对象的对象锁,即只能在同步方法或同步块中调用wait方法。

如果调用wait方法时,没有持有适当的锁,则抛出IllegalMonitorStateException

notify & notifyAll

1
Wakes up a single thread that is waiting on this object's monitor.

notify方法是为了唤醒wait挂起的线程而存在的。

notify方法也必须在拿到对象锁才能执行,也就是所谓的在synchronized内使用。

注意:Waiting状态的线程等待的是notify的通知(而不是对象锁)。

notify只是唤醒线程,也就是改变线程状态为Runable状态。

notifyAll

1
Wakes up all threads that are waiting on this object's monitor.

notifyAll方法用于唤醒全部等待的线程,这些线程同时会去竞争对象锁,先拿到先执行,直到所有线程执行完成。

与notify的区别在于:唤醒线程的个数。

finalize

finalizeGC在回收对象之前最先调用的方法。

C++中的析构函数调用的时机是确定的(对象离开作用域或delete掉),

Java中的finalize的调用具有不确定性,只有当JVM发生GC时才会回收这部分对象的内存。