Java:Object类详解

来源:互联网 发布:c excel重复数据删除 编辑:程序博客网 时间:2024/06/05 08:10

首先,我们都知道Java所有的类都继承自超类【Object】,也就是说所有的类都是Object的子类,这是一个隐式的,我们并不需要特别的指出。

我们定义一个类A。

class A {    public A() {        super();    }}

代码并不难懂,就是写了类A的构造函数。但是这里需要注意的是super关键词,调用的是父类的构造函数,我们可以跟进一下,就会跳到Object类去了。也就是说类A虽然没有明确的写出继承Object,但是Java还是让其继承了Object类。

Object类是有很多好处的,在本文中,我会慢慢讲述。
我们首先看看Object类主要的方法

protected Object clone()创建并返回此对象的一个副本。
boolean equals(Object obj)指示其他某个对象是否与此对象“相等”。
protected void finalize()当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
Class

public class Main {    public static void main(String[] args) {        A a = new A("Aiden");        A b = a;        System.out.println(a);        System.out.println(b);        System.out.println(a.name.hashCode());        System.out.println(b.name.hashCode());    }}class A {    String name;    public A(String name) {        this.name = name;    }}

我们看看输出结果:

test.A@2a139a55test.A@2a139a556325626163256261

可以看出对象a和对象b输出的内容是一样的。@前面表示的是对象映射的类,@后面表示的是对象的哈希地址。而name的哈希码也是相同的。

在更多的时候,我们需要深拷贝,即拷贝出两个内容相同、但引用地址不同的对象。我们就可以重写clone()方法去实现了。
下面是简单的实现。

public class Main {    public static void main(String[] args) {        A a = new A("Aiden");        A b = a.clone();        System.out.println(a);        System.out.println(b);        System.out.println(a.name.hashCode());        System.out.println(b.name.hashCode());    }}class A {    String name;    public A(String name) {        this.name = name;    }    @Override    protected A clone() {        String name = new String(this.name);        return new A(name);    }}

我们来看看输出的结果:

test.A@2a139a55test.A@15db97426325626163256261

我们看看,两个对象的引用确实不同了吧。可是两个对象的内容name初始的哈希码怎么还是一样的呢?好神奇。这是因为String是常量,所有的内容存储于常量池中,每次的赋值都会去常量池中找,如果找到了就将内容匹配的地址赋值给name,如果找不到,就新建一块内存,用于存储想要赋值的内容。

2、boolean equals(Object obj)方法
这个方法是用于比较两个对象是否相同的。相同的定义包括了它们所属的类相同以及哈希码相同。我们可以跟进Object类中的equals()方法。

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

如果不重写equals()方法的话,equals就是 == 符号的作用。

A a = new A("Aiden");A b = new A("Aiden");System.out.println(a.equals(b));b = a;System.out.println(a.equals(b));

输出结果:

falsetrue

但是,我们经常看到String对象如果要比较内容的话,使用的是equals()方法,也就是说String类的实现肯定是重写了equals()方法的。我们可以看看源码:

public boolean equals(Object anObject) {    if (this == anObject) { // 如果两个对象的引用相同,则返回true        return true;    }    if (anObject instanceof String) { // 如果anObject是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;}

源码还是很简单的,注释也写了,就不多说了。

3、protected void finalize()
该方法是用于回收”垃圾“的。当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。详情可以参考博文:点击参考

4、int hashCode()方法
该方法返回该对象的哈希码。关于哈希码的定义,官方是这么说的

hashcode方法返回该对象的哈希码值。支持该方法是为哈希表提供一些优点,例如,java.util.Hashtable 提供的哈希表。   hashCode 的常规协定是:   在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。   如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。   以下情况不 是必需的:如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法必定会生成不同的整数结果。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。   实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)   当equals方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。

官方定义给出我们对象的哈希码最好是不一致的。这样在equals()方法才能不同。
我们先看看如果给出相同哈希码的两个对象做比较时,会出现怎样的情况。我们就重写了hashCode()方法、equals()方法。

import java.util.HashSet;import java.util.Set;public class Main {    public static void main(String[] args) {        A a = new A("Aiden");        A b = new A("Aiden");        System.out.println(a == b);        System.out.println(a.equals(b));        Set<A> set = new HashSet<>();        set.add(a);        set.add(b);        System.out.println(set.size());    }}class A {    String name;    public A(String name) {        this.name = name;    }    @Override    public int hashCode() {        return 0;    }    @Override    public boolean equals(Object object) {        if (object == null)             return false;        if(object == this)            return true;        if (object instanceof A == false)            return false;        A b = (A)object;        if(name.equals(b.name))            return true;        return false;    }}

我们用Set对象去检测两个对象是否哈希吗是否相同。我们看看具体的输出。

falsetrue[test.A@0]

第一行输出false,理所当然,==号是比较两个对象的引用。
第二行输出true,是因为重写了equals()方法,比较的是两个对象的name是否相同。
第三行输出却只有一个内容,也就是说HashSet在add的时候,是比较两个对象的哈希码的,如果哈希码已经存在于Set集合中,就不加入了。如果不存在Set集合中,就加入。

看来哈希码,在HashMap、HashSet、Hashtable中有广泛的应用啊。可是怎样才能实现多个对象,哈希码不相同呢?
这里给出了通用的重写hashCode()的方案:
1. 初始化一个整形变量,为此变量赋予一个非零的常数值,比如int result = 17;
2. 选取equals方法中用于比较的所有域,然后针对每个域的属性进行计算:
(1) 如果是boolean值,则计算f ? 1:0
(2) 如果是byte\char\short\int,则计算(int)f
(3) 如果是long值,则计算(int)(f ^ (f >>> 32))
(4) 如果是float值,则计算Float.floatToIntBits(f)
(5) 如果是double值,则计算Double.doubleToLongBits(f),然后返回的结果是long,再用规则(3)去处理long,得到int
(6) 如果是对象应用,如果equals方法中采取递归调用的比较方式,那么hashCode中同样采取递归调用hashCode的方式。  否则需要为这个域计算一个范式,比如当这个域的值为null的时候,那么hashCode 值为0
(7) 如果是数组,那么需要为每个元素当做单独的域来处理。如果你使用的是1.5及以上版本的JDK,那么没必要自己去    重新遍历一遍数组,java.util.Arrays.hashCode方法包含了8种基本类型数组和引用数组的hashCode计算,算法同上,

public static int hashCode(long a[]) {      if (a == null)          return 0;      int result = 1;      for (long element : a) {          int elementHash = (int)(element ^ (element >>> 32));          result = 31 * result + elementHash;      }      return result;  }  

5、String toString()返回该对象的字符串表示。
我们可以重写这个方法,也可以重写。如果不重写,一般来说就会返回”className@hashCode“格式的字符串。也就是返回类名+”@“+哈希码。
其实我们在System.out.println(a);的时候就默认调用了a.toString()方法,所以才会输出刚刚的格式。

1 0
原创粉丝点击