JDK1.8源码阅读(1):java.lang.Object
一、hashcode()
1、hashCode方法返回值是int类型的散列码,对象的散列码是为了更好的支持基于哈希机制的java集合类,例如Hashtable,HashMap,HashSet。如果重写equals方法,也要重写hashCode方法。
2、hashCode方法的一致约定要求
(1)在java应用的一次执行过程中,对于同一个对象的hashCode方法的多次调用,他们应该返回同样的值(前提是该对象的信息没有发生变化);
(2)对于两个对象来说,如果使用equals方法比较返回true,那么这两个对象的hashCode值一定是相同的;
(3)对于两个对象来说,如果使用equals方法返回false,那么这两个对象的hashCode值不要求一定不同(可以相同,可以不同),但是如果不同则可以提高应用的性能。
(4)对于Object类来说,不同Object对象的hashCode值是不同的(Object类的hashcode值表示的是对象的地址)。
3、 hashCode方法应用举例:
当使用HashSet时,hashCode()方法就会得到调用,判断已经存储在集合对象中的hashCode值是否与增加的对象的hashCode值一致;如果不一致,直接加进去;如果一致,在进行equals方法比较,equals方法返回true,表示对象已经加进去了,就不会再增加新的对象,否则加进去。
4、hashCode编写指导
在编写hashCode时,由于返回值是个int值,故不能溢出。不同对象的hash尽量不同,避免hash冲突。下面是解决方案:
- 定义一个int类型的变量hash,初始化为7。
- 接下来将你认为重要的字段(equals中衡量相等的字段)参与散列运算,每一个重要字段都会产生一个hash分量,为最终的hash值做出贡献
- 最后通过递归计算所有的分量的总和起,注意并不是简单的相加。选择一个倍乘的数字31,参与计算。
int hash = 7;
hash = 31 * hash + 字段1贡献分量;
hash = 31 * hash + 字段2贡献分量;
.....
return hash;
二、getClass()
用于获取对象的运行时对象的类。与.class区别:String.class 是能对类名的引用取得在内存中该类型class对象的引用,而new String().getClass() 是通过实例对象取得在内存中该实际类型class对象的引用。
我们可以使用一个小例子来看两者的不同:
1.抽象类
package com.kang;
public abstract class Animal {
}
2.实例类
package com.kang;
public class Dog extends Animal {
public static void main(String[] args) {
Animal animal = new Dog();
System.out.println(animal.getClass().getName());
//输出com.kang.Dog
System.out.println(Animal.class.getName());
//输出com.kang.Animal
}
}
3.结果
com.kang.Dog
com.kang.Animal
4.解释
animal.getClass().getName()是在程序运行时获得运行时实例的类类型。而Animal.class.getName()是在编译阶段就确定了的,与运行时的状态无关。
Object中的equals方法是直接判断this和obj本身的值是否相等,即用来判断调用equals的对象和形参obj所引用的对象是否是同一对象,所谓同一对象就是指内存中同一块存储单元,如果this和obj指向的是同一块内存对象,则返回true,如果this和obj指向的不是同一块内存,则返回false,注意:即便是内容完全相等的两块不同的内存对象,也返回false。
那么equals()方法与之前的“==”有什么区别呢?
public class Equals{
public static void main(String[] args){
String s1="apple";
String s2="apple";
System.out.println(s1==s2); //true
System.out.println(s1.equals(s2)); //equals比较的是内容,true
String s3=new String("apple");
String s4=new String("apple");
System.out.println(s3==s4); //false
System.out.println(s3.equals(s4)); //true
}
}
从上面的实例可以看出,“==”比较的是两个引用的对象是否相等,而equals()方法比较的是两个对象的实际内容。我们结合上面的内存的划分来理解这个区别。
String str1=new String("apple");
String str2=new String("apple");
System.out.println(s3==s4); //false
System.out.println(s3.equals(s4)); //true
上述几行代码内存分析如下图所示:
因为“==”比较的是两个引用的对象是否相等,从上图很容易看出来不等,所以System.out.println(s3==s4),结果为False;而equals()方法比较的是两个对象的实际内容,从图中可以看出s3和s4都指向apple,内容是相同的,所以System.out.println(s3.equals(s4)),结果为True。
再来看
String s1="apple";
String s2="apple";
内存分析如下图:
很容易看出System.out.println(s1==s2)的结果为True。
四、clone()
Object clone() 方法用于创建并返回一个对象的拷贝。
clone 方法是浅拷贝,对象内属性引用的对象只会拷贝引用地址,而不会将引用的对象重新分配内存,相对应的深拷贝则会连引用的对象也重新创建。
使用Object的clone方法必须实现 Cloneable 接口,否则调用会发生 CloneNotSupportedException 异常。
以下实例创建了 obj1 对象,然后拷贝 obj1 给 obj2,通过 obj2 输出变量的值:
class RunoobTest implements Cloneable {
// 声明变量
String name;
int likes;
public static void main(String[] args) {
// 创建对象
RunoobTest obj1 = new RunoobTest();
// 初始化变量
obj1.name = "Runoob";
obj1.likes = 111;
// 打印输出
System.out.println(obj1.name); // Runoob
System.out.println(obj1.likes); // 111
try {
// 创建 obj1 的拷贝
RunoobTest obj2 = (RunoobTest) obj1.clone();
// 使用 obj2 输出变量
System.out.println(obj2.name); // Runoob
System.out.println(obj2.likes); // 111
} catch (Exception e) {
System.out.println(e);
}
}
}
以上程序执行结果为:
Runoob
111
Runoob
111
五、toString()
用于返回对象的字符串表示形式。
默认返回格式:对象的 class 名称 + @ + hashCode 的十六进制字符串。
以下实例演示了 toString() 方法的使用:
class RunoobTest {
public static void main(String[] args) {
// toString() with Object
Object obj1 = new Object();
System.out.println(obj1.toString());
Object obj2 = new Object();
System.out.println(obj2.toString());
Object obj3 = new Object();
System.out.println(obj3.toString());
}
}
以上程序执行结果为:
java.lang.Object@d716361
java.lang.Object@6ff3c5b5
java.lang.Object@3764951d
六、finalize()
当对象的没有被引用时,GC在收集垃圾时就会调用这个方法,即对象在被回收时回回调此方法
举例:
public class MyClass extends Object {
public static void main(String[] args) {
new MyObject();
System.gc();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static class MyObject extends Object{
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize");
}
}
}
控制台输出结果:
finalize