一、本节目的:为了了解Object类源码实现。
二、源码展示
import jdk.internal.HotSpotIntrinsicCandidate;
/**
* Object 类是java所有类的父类。
*/
public class Object {
private static native void registerNatives();
static {
registerNatives();
}
/**
* JDK的源码中,被@HotSpotIntrinsicCandidate标注的方法,在
* HotSpot中都有一套高效的实现,该高效实现基于CPU指令,运行时,
* HotSpot维护的高效实现会替代JDK的源码实现,从而获得更高的效
* 率。
*/
@HotSpotIntrinsicCandidate
public Object() {}
/**
* 返回当前对象的运行时Class对象。
*/
@HotSpotIntrinsicCandidate
public final native Class<?> getClass();
/**
* 返回当前对象的hash值。
* 在一次Java应用程序执行相同的obj对象时,不管执行多少次该对象的hashCode()方法,程序总会返回一个不同的hash值。
* 注意:
当两个对象的equals进行比较,返回true时,那么这两个对象同时调用hashCode()方法,那么返回值必然相同(请参照示例一)。
* 反之,如果两个对象使用equals方法返回false,那么他们在调用hashCode()方法返回的值必然不同。在hash表上分配的时候,不会出现"hash碰撞"。
* 等介绍HashMap的源码时候会重点说。。。
*/
@HotSpotIntrinsicCandidate
public native int hashCode();
/**
* Object的equals方法比较的时两个对象的内容是否相等。
* == 比较的是两个对象的引用是否指向同一块内存。
* 【请参照示例一】
*/
public boolean equals(Object obj) {
return (this == obj);
}
/**
* 该方法返回的是一个对象的副本。而这里的"副本"的含义取决与对象是什么类型的。
* 下面几个表达式是成立的:
* 1.x.clone() != x -------- true
* 2.x.clone().getClass() == x.getClass() -------true
* 该方法可以实现对一个对象的浅拷贝。具体实现看下面示例。
* 【请参考示例二】
*/
@HotSpotIntrinsicCandidate
protected native Object clone() throws CloneNotSupportedException;
/**
* 每一个对象的默认toString()方法,都会调用Object类的实现。
* Class的name拼接上@符,拼上当前对象的hashCode()方法的十六进制。
*/
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
/**
* 该方法有final修饰,不能被重写。用于线程间通讯,当一个线程对象调用该方法时,会唤醒一个正在等待该对象的线程。如果有多个线程同时处于阻塞状态,那么只能唤醒一个线程。
* 【代码案例参照示例三】
*
*/
@HotSpotIntrinsicCandidate
public final native void notify();
/**
* 可以唤醒多个正在阻塞的线程。
* 【代码案例参照示例三】
*/
@HotSpotIntrinsicCandidate
public final native void notifyAll();
/**
* 调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)。
* 【代码案例参照示例三】
*/
public final void wait() throws InterruptedException {
wait(0L);
}
public final native void wait(long timeoutMillis) throws InterruptedException;
public final void wait(long timeoutMillis, int nanos) throws InterruptedException {
if (timeoutMillis < 0) {
throw new IllegalArgumentException("timeoutMillis value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0) {
timeoutMillis++;
}
wait(timeoutMillis);
}
/**
* 在JVM执行垃圾回收的时候,会执行该方法的回调。
*
*/
@Deprecated(since="9")
protected void finalize() throws Throwable { }
}
三、代码案例
示例一、
/**
* @author lengweijian
*/
public class ObjectTest {
public static void main(String[] args) {
m1();
m2();
}
private static void m2() {
Object o = new Object();
Object o2 = new Object();
Integer integer = new Integer(20);
Integer integer2 = new Integer(20);
System.out.printf("o为: %s\no2为: %s \no.equals(o2)结果:%s\n",o,o2,o.equals(o2));
System.out.printf("integer为: %s\n integer2为: %s \ninteger.equals(integer2) 结果:%s\n",integer,integer2,integer.equals(integer2));
System.out.println("----------------------------------分割线-------------------------------------");
String str1 = "hello";
String str2 = "hello";
// 由于声明字符串常量直接会存储在运行时常量池中,而同一字符串常量池只存一份,所以str1和str2指向的是常量池中的同一位置。
System.out.println(str1==str2);
System.out.println(str1.equals(str2));
String str3 = new String("hello");
String str4 = new String("hello");
// == 比较的是两个引用是否指向同一块内存区域。由于str3和str4指向的是堆内存的两块不同的区域,所以为false。
System.out.println(str3==str4);
System.out.println(str3.equals(str4));
}
/**
* 两个Object的equals方法为true,他们的hashCode方法必然返回同一hash值
*/
private static void m1() {
Integer integer = new Integer(20);
Integer integer2 = new Integer(20);
System.out.println(integer.equals(integer2));
System.out.println(integer.hashCode());
System.out.println(integer2.hashCode());
}
}
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wytkZciV-1577337202385)(http://49.232.140.46:8090/upload/2019/10/image-42cad0ca5103433bb874ee9421ec2cef.png)]
结论: == 比较的是两个对象的引用是否指向同一块内存。equals比较的是两个对象的值是否相等。
示例二:
浅拷贝:当一个类实现了Cloneable接口,重写clone方法时。当调用该类的实例的clone方法时,会实现一个示例的拷贝。当该类的成员变量是基本数据类型的时候,拷贝的是该变量的值的副本,在修改克隆对象的基本数据类型的成员变量时,不会对源对象的基本数据类型的变量进行修改;当源类的成员变量是引用类型的时候,那么拷贝的是源类的引用类型变量的引用,当修改克隆对象的引用类型变量时,会修改源类的引用类型成员变量的值。
样例代码:
第一步:创建ObjectTest.java类
/**
* @author lengweijian
* Person 实现Cloneable接口,重写clone方法。
*/
class Person implements Cloneable{
private int age;
private Address address;
private String name;
public Person(int age, Address address, String name) {
this.age = age;
this.address = address;
this.name = name;
}
.......省略setter、getter..........
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public String display() {
return "Person [age=" + age + ", name=" + name + ", address=" + address + "]";
}
}
public class ObjectTest {
public static void main(String[] args) throws CloneNotSupportedException {
m3();
}
/**
* 浅拷贝测试
*/
private static void m3() throws CloneNotSupportedException {
Person person = new Person(20, new Address("北京市", "回龙观东大街"), "lengwj");
Person clonePerson = (Person) person.clone();
System.out.println(person);
System.out.println(clonePerson);
System.out.println("---------------------上面输出说明了原对象与新对象是两个不同的对象。------------------------");
System.out.println(person.display());
System.out.println(clonePerson.display());
System.out.println("--------------------上边可以看到拷贝出来的新对象与原对象内容一致------------------------");
clonePerson.setName("zhanght");
Address cloneAddress = clonePerson.getAddress();
cloneAddress.setProvince("上海市");
cloneAddress.setStreet("虹桥机场");
clonePerson.setAddress(cloneAddress);
System.out.println(person.display());
System.out.println(clonePerson.display());
System.out.println("我们发现,修改了克隆对象的成员引用对象的属性后,原对象的成员引用也发生了变化,这就是浅拷贝。");
}
}
第二步,创建Address.java
public class Address {
private String province;
private String street;
public Address(String province, String street) {
this.province = province;
this.street = street;
}
.......省略setter、getter、toString.....
}
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pZhHzuL7-1577337202386)(http://49.232.140.46:8090/upload/2019/10/image-1a7157d7b355442e8397356f1ed2cdcd.png)]
示例二结论:
当Person实现了Cloneable接口时候,在Person类对象调用clone方法时候,可以完成对person对象的浅拷贝。当类的成员变量为基本数据类型的时候,拷贝的是该变量的值的副本;当变量为引用类型的时候,拷贝的是该变量的引用,而不是该对象的对象本身。所以,在修改克隆对象的基本数据类型的时候,不会对源类的基本类型数据造成改变;而在修改克隆对象的引用数据类型的时候,由于修改的是该引用所指向的对象本身,所以源类的引用类型成员变量的值所指向的对象也会发生改变。
深拷贝:了解了浅拷贝之后,深拷贝就很好理解了。就是当一个类实现了Cloneable接口,调用该类的clone方法时,会得到一个该类的拷贝。当修改该克隆对象的引用类型的成员变量时,不会修改源类的引用类型成员变量。实现深拷贝通常有以下两种方式。
第一种:
第一步,创建ObjectTest.java
package com.atlwj.jdk8.resource;
/**
* @author lengweijian
* 实现克隆接口
*/
class Person implements Cloneable{
private int age;
private Address address;
private String name;
public Person(int age, Address address, String name) {
this.age = age;
this.address = address;
this.name = name;
}
......省略gettter、setter、、、
@Override
protected Object clone() throws CloneNotSupportedException {
Person clonePerson = (Person) super.clone();
// 在这里调用引用变量的克隆方法得到引用变量的克隆,并set到克隆person对象中。
clonePerson.setAddress((Address) clonePerson.getAddress().clone());
return clonePerson;
}
public String display() {
return "Person [age=" + age + ", name=" + name + ", address=" + address + "]";
}
}
public class ObjectTest {
public static void main(String[] args) throws CloneNotSupportedException {
m3();
}
/**
* 深拷贝测试
*/
private static void m3() throws CloneNotSupportedException {
Person person = new Person(20, new Address("北京市", "回龙观东大街"), "lengwj");
Person clonePerson = (Person) person.clone();
System.out.println(person);
System.out.println(clonePerson);
System.out.println("---------------------上面输出说明了原对象与新对象是两个不同的对象。------------------------");
System.out.println(person.display());
System.out.println(clonePerson.display());
System.out.println("--------------------上边可以看到拷贝出来的新对象与原对象内容一致------------------------");
clonePerson.setName("zhanght");
Address cloneAddress = clonePerson.getAddress();
cloneAddress.setProvince("上海市");
cloneAddress.setStreet("虹桥机场");
clonePerson.setAddress(cloneAddress);
System.out.println(person.display());
System.out.println(clonePerson.display());
System.out.println("我们发现,修改了克隆对象的成员引用对象的属性后,原对象的成员引用没有发生变化,这就是深拷贝。");
}
}
第二步,创建Address.java类,实现Cloneable接口
package com.atlwj.jdk8.resource;
public class Address implements Cloneable{
private String province;
private String street;
public Address(String province, String street) {
this.province = province;
this.street = street;
}
.....省略getter、setter、、、、、、
@Override
public String toString() {
return "Address [province=" + province + ", street=" + street + "]";
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pRhGKw9Q-1577337202386)(http://49.232.140.46:8090/upload/2019/10/image-441e7c9fc7974264bb5979e9d836f1b7.png)]
深拷贝结论:不同于浅拷贝。当调用源类的clone方法得到一个克隆对象时,对克隆对象的引用类型成员变量进行值的修改时,不会对源类的引用类型变量进行修改。做法就是在Person类的克隆方法里面,对引用变量也进行clone操作,复制到person克隆对象中进行返回,就能得到深拷贝对象。
深拷贝第二种实现:
通过实现序列化接口Serializable,可以实现对该类的全部变量包括引用类型变量完成序列化,再将该类的字节序列反序列化,就可以得到该类的深拷贝对象。
案例代码:
第一步,创建ObjectTest.java
package com.atlwj.jdk8.resource;
import java.io.*;
/**
* @author lengweijian
*/
class Person implements Serializable {
private static final long serialVersionUID = 7899327867106360350L;
private int age;
private Address address;
private String name;
public Person(int age, Address address, String name) {
this.age = age;
this.address = address;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String display() {
return "Person [age=" + age + ", name=" + name + ", address=" + address + "]";
}
/**
*
* 通过序列化的方式完成对象深拷贝
*/
public Object deepCopy(){
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
return objectInputStream.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
public class ObjectTest {
public static void main(String[] args) {
Person originPerson = new Person(30, new Address("纽约", "华尔街"), "Lucy");
Person clonePerson = (Person) originPerson.deepCopy();
System.out.println(originPerson);
System.out.println(clonePerson);
Address cloneAddress = clonePerson.getAddress();
cloneAddress.setProvince("北京");
cloneAddress.setStreet("中关村创业街");
clonePerson.setAddress(cloneAddress);
System.out.println(originPerson.display());
System.out.println(clonePerson.display());
}
}
第二步,创建Address.java类
package com.atlwj.jdk8.resource;
import java.io.Serializable;
/**
* @author lengweijian
*/
public class Address implements Cloneable, Serializable {
private static final long serialVersionUID = -8630168245854630184L;
private String province;
private String street;
public Address(String province, String street) {
this.province = province;
this.street = street;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
@Override
public String toString() {
return "Address [province=" + province + ", street=" + street + "]";
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-09m03Gy0-1577337202387)(http://49.232.140.46:8090/upload/2019/10/image-25f48a1492dd410888c2a765e628fb09.png)]
示例二总结:对象的深拷贝可以通过引用的层层clone方法实现,也可以通过序列化和反序列化的方式实现。
示例三:
未完待续…