JDK8 Object源码
[TOC]
JDK8 Object
1.简述
Java对象被设计成单继承,所有的对象都直接或者间接的继承Object类,拥有Object的一些属性和方法。所以Object也可以理解为所有类的父类。
2.方法汇总
Modifier and Type | Method | Description |
---|---|---|
protected Object | clone() | Creates and returns a copy of this object. |
boolean | equals(Object obj) | Indicates whether some other object is “equal to” this one. |
protected void | finalize() | Called by the garbage collector on an object when garbage collection determines that there are no more references to the object. |
Class<?> | getClass() | Returns the runtime class of this Object. |
int | hashCode() | Returns a hash code value for the object. |
String | toString() | Returns a string representation of the object. |
void | notify() | Wakes up a single thread that is waiting on this object’s monitor. |
void | notifyAll() | Wakes up all threads that are waiting on this object’s monitor. |
void | wait() | Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. |
void | wait(long timeout) | Causes the current thread to wait until either another thread invokes the notify() method or the notifyAll() method for this object, or a specified amount of time has elapsed. |
void | wait(long timeout, int nanos) | Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object, or some other thread interrupts the current thread, or a certain amount of real time has elapsed. |
3.方法详细信息
Clone()
//创建并返回此对象的副本。
protected Object clone() throws CloneNotSupportedException如何实现拷贝,到底是浅拷贝?还是深拷贝?
- 要想重写clone方法,需要实现Cloneable接口
- 通过super.clone()获取Objec.clone()方法返回的拷贝对象
- 如果你想要实现深拷贝,你需要在对象返回前进行修改
情况一:创建一个Person类,实现Cloneable接口,重写clone方法
package com.joey.object;
public class Person implements Cloneable {
int age;
String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//不重写toString()方法,为了看哈希值是否一致
public String show() {
return "年龄:" + this.age + ",姓名:" + this.name;
}
}
class ObjectTest{
public static void main(String[] args) throws CloneNotSupportedException {
Person person = new Person(18,"joey");
//深拷贝一个对象
Person person1 = (Person)person.clone();
//输出对象的哈希地址值
System.out.println(person);
System.out.println(person1);
//输出对象的内容
System.out.println(person.show());
System.out.println(person1.show());
//修改复制的对象
person1.setAge(24);
person1.setName("方陈勇");
//输出判断是否会修改person的值,其实地址值不一样就已经很清楚了
System.out.println(person.show());
System.out.println(person1.show());
}
}
/*输出结果:
com.joey.object.Person@1b6d3586
com.joey.object.Person@4554617c
年龄:18,姓名:joey
年龄:18,姓名:joey
年龄:18,姓名:joey
年龄:24,姓名:方陈勇
*/结论一:单纯看地址值能够判断两个是不同的对象,属于深拷贝,并且修改person1的值不会影响person的值。
情况二:创建一个Address类,重写toString()方法
package com.joey.object;
public class Address{
String location;
public Address(String location) {
this.location = location;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public String toString() {
return "Address{" +
"location='" + location + '\'' +
'}';
}
}Person类做一下修改
package com.joey.object;
public class Person implements Cloneable {
private int age;
private String name;
private Address address;
public Person(int age, String name,Address address) {
this.age = age;
this.name = name;
this.address= address;
}
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
//不重写toString()方法,为了看地址值是否一致
public String show() {
return "年龄:" + this.age + ",姓名:" + this.name +",地址:" +this.address;
}
}
class ObjectTest{
public static void main(String[] args) throws CloneNotSupportedException {
Person person = new Person(18,"joey",new Address("杭州西湖"));
//拷贝一个对象
Person person1 = (Person)person.clone();
//输出对象的哈希地址值
System.out.println(person);
System.out.println(person1);
//输出对象的内容
System.out.println("**********************复制后************************");
System.out.println(person.show());
System.out.println(person1.show());
//修改复制的对象
System.out.println("*****************修改person1中的age和name*************");
person1.setAge(24);
person1.setName("方陈勇");
//输出判断
System.out.println(person.show());
System.out.println(person1.show());
//修改复制的对象
System.out.println("*****************修改person1中的age和name*************");
Address address = person1.getAddress();
address.setLocation("杭州滨江");
//输出判断
System.out.println(person.show());
System.out.println(person1.show());
}
}
/*输出结果:
com.joey.object.Person@1b6d3586
com.joey.object.Person@4554617c
**********************复制后************************
年龄:18,姓名:joey,地址:Address{location='杭州西湖'}
年龄:18,姓名:joey,地址:Address{location='杭州西湖'}
*****************修改person1中的age和name*************
年龄:18,姓名:joey,地址:Address{location='杭州西湖'}
年龄:24,姓名:方陈勇,地址:Address{location='杭州西湖'}
*****************修改person1中的age和name*************
年龄:18,姓名:joey,地址:Address{location='杭州滨江'}
年龄:24,姓名:方陈勇,地址:Address{location='杭州滨江'}
*/结论二:person1只修改age和name时发现不会影响person的值,但是修改address对象的值时就会有影响。是因为Address类没有重写Clone()方法,person和person1指向的都是同一个Address对象
情况三:那么如何实现深拷贝。修改Address类实现Cloneable接口,重写clone()方法,在Person类中重写Clone()方法的时候调用Address重写后的clone()方法,即同时克隆Person类和Address类。
修改Address类
>package com.joey.object;
>public class Address implements Cloneable{
String location;
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Address(String location) {
this.location = location;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public String toString() {
return "Address{" +
"location='" + location + '\'' +
'}';
}
}修改Person类中重写的Clone()方法
>
protected Object clone() throws CloneNotSupportedException {
//深拷贝
Person person = (Person)super.clone();
person.address = (Address) this.address.clone();
return person;
//return super.clone();
}
/*输出结果:
com.joey.object.Person@1b6d3586
com.joey.object.Person@4554617c
**********************复制后************************
年龄:18,姓名:joey,地址:Address{location='杭州西湖'}
年龄:18,姓名:joey,地址:Address{location='杭州西湖'}
*****************修改person1中的age和name*************
年龄:18,姓名:joey,地址:Address{location='杭州西湖'}
年龄:24,姓名:方陈勇,地址:Address{location='杭州西湖'}
*****************修改person1中的age和name*************
年龄:18,姓名:joey,地址:Address{location='杭州西湖'}
年龄:24,姓名:方陈勇,地址:Address{location='杭州滨江'}
*/结论三:可以看到最后person的address内容也没有改变。也就是说,当一个类里有很多引用类型时,如果想实现深拷贝那就得将每个引用类型都实现Cloneable接口重写一下clone()方法,比较麻烦的。可能好奇为什么String为什么没有影响,因为String是不可变的,相当于重新new了一个String。
所以,对于很多引用类型,可以使用序列化对象的方式进行深拷贝。
equals(Object obj)
//直接判断的是引用,若想判断内容是否相同,需要重写此方法。
public boolean equals(Object obj)常见问题:equals()和==的比较
- equals()用于引用类型的比较,如果引用类型没有重写equals()方法,调用的就是Object类中的equals(),比较的是地址值。
- ==用于在基本数据类型和引用数据类型的比较。如果是基本数据类型,比较的是内容,如果是引用数据类型,那比较的就是地址值。
哪些类重写了equals()方法
- String
- Date
- File
- 包装类
- …
如何重写equals方法
equals()方法里面提到了四个原则:
- 自反性reflexive,a.equals(a) == true
- 对称性symmetric,a.equals(b) == b.equals(a)
- 传递性transitive,当a.equals(b) == true且b.equals(c) == true时,a.equals(c) == true
- 一致性consistent,当equals实现不变时,a.equals(b)返回值永远不变
例如String中的equals(),重写判断的是内容相等,hashCode()方法也重写了
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;
}注意:重写equals()方法,需要重写hashCode()
finalize()
//该方法用于释放资源。因为无法确定该方法什么时候被调用,很少使用。
protected void finalize() throws Throwable {}finalize的原理
(1)对象在初始化的过程中会判断是否重写了finalize,方法是判断两个字段标志has_finalizer_flag和RegisterFinalizersAtInit。
(2)如果重写了finalize,那就把当前对象注册到FinalizerThread的ReferenceQueue队列中。注册之后的对象就叫做Finalizer。方法是调用register_finalizer函数。此时java虚拟机一看当前有这个对象的引用,于是就不进行垃圾回收了。
(3)对象开始被调用,FinalizerThread线程负责从ReferenceQueue队列中获取Finalizer对象。开始执行finalize方法,在执行之前,这个对象一直在堆中。
(4)对象执行完毕之后,将这个Finalizer对象从队列中移除,java虚拟机一看对象没有引用了,就进行垃圾回收了。
这就是整个过程。不过在这里我们主要看的是finalize方法对垃圾回收的影响,其实就是在第三步,也就是这个对象含有finalize,进入了队列但一直没有被调用的这段时间,会一直占用内存。
注意:应该避免使用finalize()方法,可能会导致内存溢出。
getClass()
//返回此Object运行时类类型,final修饰不可重写,一般和getName()联合使用,如getName().getClass().
public final native Class<?> getClass();getClass()返回调用对象的包名+类名,jvm可以根据这个唯一定位一个类
hashCode()
//返回对象的哈希码值。
public native int hashCode();这里存在一种情况,重写了equals()方法没有重写,两个对象的内容确实相等了。但是,地址值还是不一样。
比如String类中,String a = “a”;String b = new String(“a”); a在字符串常量池中,b在堆中,地址值不一样。比如在使用集合Set或者其他一些需要唯一性判断的地方,内容相等其实就是相等,但是有两个地址值,可能会造成重复。
所以说重写equals()方法同时也要注意重写hashCode()方法
toString()
//返回了Object对象的全限定类名+@+16进制表示的物理地址,它建议所有的子类都重写该方法,以致于可以打印出关键信息
public String toString()notify()
notifyAll()
wait()
wait(long timeout)
wait(long timeout, int nanos)
到线程再看这几个……