一、源码
/* 通过JNI对本地方法进行注册,映射本地函数与Java方法之间的关系*/
private static native void registerNatives();
/* 在对象初始化时自动调用 registerNatives() */
static {
registerNatives();
}
/* 返回此 Object 的运行时类*/
public final native Class<?> getClass()
/*返回该对象的哈希码值。默认情况下,该方法根据对象的地址来计算哈希码值*/
public native int hashCode()
/*比较两个对象是否同一 */
public boolean equals(Object obj)
/*本地CLONE方法,用于对象的复制 */
protected native Object clone() throws CloneNotSupportedException
/*返回该对象的字符串表示*/
public String toString()
/*唤醒在此对象监视器上等待的单个线程*/
public final native void notify()
/*唤醒在此对象监视器上等待的所有线程。*/
public final native void notifyAll()
/*在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待*/
public final native void wait(long timeout) throws InterruptedException
public final void wait(long timeout, int nanos) throws InterruptedException
public final void wait() throws InterruptedException
/*当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法*/
protected void finalize() throws Throwable {}
二、hashCode()
hashCode 的通用约定:
- 在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的散列值,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
- 如果根据 equals() 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的散列值。
- 如果根据 equals(t) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不要求一定生成不同的散列值。但是,程序员应该意识到,为不相等的对象生成不同散列值可以提高哈希表的性能。
- 当equals方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的散列值可。
三、equals()
- 对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法。
- 对于引用类型,== 判断两个变量是否引用同一个对象,而 equals() 判断引用的对象是否等价。
- equals() 具有对称性、自反性、传递性。
- 对任何为 null 的对象调用 x.equals(null) 结果都为 false
四、clone()
clone() 有 protected、native 关键字修饰,那么我们需要重写 Object 的 clone() 实现对象的克隆。
1、浅拷贝
浅拷贝只是复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的值,另一个也会随之变化。
public class Student implements Cloneable {
// 姓名
private String name;
// 年龄
private int age;
// 分数
private Score Score;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Student(String name, int age, Score score) {
super();
this.name = name;
this.age = age;
Score = score;
}
public Score getScore() {
return Score;
}
public void setScore(Score score) {
Score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ",mathematics score=" + Score.getMathematics()
+ ",chinese score=" + Score.getChinese() + "]";
}
// 重写Object的clone方法
public Student clone() throws CloneNotSupportedException {
return (Student) super.clone();
}
}
class Score {
private int mathematics;
private int chinese;
public Score(int mathematics, int chinese) {
super();
this.mathematics = mathematics;
this.chinese = chinese;
}
public int getMathematics() {
return mathematics;
}
public void setMathematics(int mathematics) {
this.mathematics = mathematics;
}
public int getChinese() {
return chinese;
}
public void setChinese(int chinese) {
this.chinese = chinese;
}
}
@Test
public static void testShallowClone() throws CloneNotSupportedException {
Score score = new Score(99, 90);
Student orgin = new Student("二牛", 22, score);
Student clone = orgin.clone();
System.out.println("orgin:" + orgin.toString());
System.out.println("clone:" + clone.toString());
orgin.setName("激动");
orgin.setAge(1);
score.setMathematics(100);
score.setChinese(80);
System.out.println("orgin:" + orgin.toString());
System.out.println("clone:" + clone.toString());
}
clone() 对于基本数据类型、String类型(值传递)为深拷贝,对象、数组等引用类型为浅拷贝。除了重写 clone() 方法进行浅拷贝,还可以通过拷贝构造方法实现浅拷贝。
2、深拷贝
深拷贝是将对象和值都复制过来,两个对象修改其中任意的值另一个值不会改变。
public class Student implements Cloneable {
// 姓名
private String name;
// 年龄
private int age;
// 分数
private Score Score;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Student(String name, int age, Score score) {
super();
this.name = name;
this.age = age;
Score = score;
}
public Score getScore() {
return Score;
}
public void setScore(Score score) {
Score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ",mathematics score=" + Score.getMathematics()
+ ",chinese score=" + Score.getChinese() + "]";
}
// 重写Object的clone方法
public Student clone() throws CloneNotSupportedException {
// 拷贝 Student 对象
Student stu = (Student) super.clone();
// 拷贝 Student 对象 的score属性
stu.score = stu.getScore().clone();
return stu;
}
}
class Score implements Cloneable {
private int mathematics;
private int chinese;
public Score(int mathematics, int chinese) {
super();
this.mathematics = mathematics;
this.chinese = chinese;
}
public int getMathematics() {
return mathematics;
}
public void setMathematics(int mathematics) {
this.mathematics = mathematics;
}
public int getChinese() {
return chinese;
}
public void setChinese(int chinese) {
this.chinese = chinese;
}
// 重写Object类的clone方法
public ScoreD clone() throws CloneNotSupportedException {
return (ScoreD) super.clone();
}
}
@Test
public static void testShallowClone() throws CloneNotSupportedException {
Score score = new Score(99, 90);
Student orgin = new Student("二牛", 22, score);
Student clone = orgin.clone();
System.out.println("orgin:" + orgin.toString());
System.out.println("clone:" + clone.toString());
orgin.setName("激动");
orgin.setAge(1);
score.setMathematics(100);
score.setChinese(80);
System.out.println("orgin:" + orgin.toString());
System.out.println("clone:" + clone.toString());
}
除了重写clone方法来实现深拷贝,还可以通过对象序列化实现深拷贝:将对象序列化为字节序列后,默认会将该对象的整个对象进行序列化,再通过反序列即可实现深拷贝。
cloneable 是一个标记接口,在实现这个接口的情况下,重写Object中的clone(),然后通过类调用 clone() 才能克隆成功。否则会抛出CloneNotSupportedException。另外,若某个属性被 transient 关键字修饰,那么该属性无法被拷贝。
3、clone() 的替代方案
使用 clone() 来拷贝一个对象即复杂又有风险,它会抛出异常,并且需要强制类型转换。Effective Java 书上讲到,最好不要去使用 clone(),可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象。