Java程序员答题干货

Java中 ==、equals、hashCode

2019-03-19  本文已影响7人  小怪兽大作战

==

==是java重载的一个双目运算符,比较的是左右两边对象在堆内存中的地址是否相等,也就是比较两边是不是同一个对象。
看下面一段程序

    public static void main(String[] args) {
        HashMap map1=new HashMap();
        HashMap map2=new HashMap();
        System.out.println(map1==map2);
        int a=123;
        int b=123;
        System.out.println(a==b);
        String s1="123";
        String s2="123";
        System.out.println(s1==s2);
        Integer a1=1;
        Integer b1=1;
        System.out.println(a1==b1);
        Integer a2=128;
        Integer b2=128;
        System.out.println(a2==b2);
    }

输出结果


image.png

下面我们来分析一下。
1.我们new一个常规对象,其实例放在堆内存中,其引用方法栈内存中。每次new一个对象,都会在堆内存中重新分配一块内存并创建新的实例。所以,在

HashMap map1=new HashMap();
HashMap map2=new HashMap();

中,我们创建了两个实例对象,这两个实例对象保存在堆内存中,两个应用变量map1和map2保存在栈内存中main方法的栈帧的局部变量表中,map1和map2存放的值是实例在堆内存中的地址。因为map1和map2指向了不同的实例对象,所以map1和map2保存的值也不一样,所以System.out.println(map1==map2)输出为false。
2.我们又创建了两个int型的局部变量,值都是123。对于基本数据类型(byte,short,char,int,float,double,long,boolean)来说,他们是作为常量在方法区中的常量池里面以HashSet策略存储起来的,常量池中相同的变量只会有一个实例,因此a和b指向同一个地址,因此System.out.println(a==b)输出为true。
3.我们又创建了两个String类型的局部变量,值都是“123”.对于String,在方法区中有一个字符串常量池,字符串常量池中相同的字符串只会有一个实例,我们使用如下代码创建的两个局部变量。

String s1="123";
String s2="123";

两个引用s1和s2保存在栈中,“123”保存在字符串常量池中,因此s1和s2指向的是同一个字符串实例,因此System.out.println(s1==s2)输出true。
4.我们又使用下面的代码创建了两个Integer对象。

        Integer a1=1;
        Integer b1=1;
        System.out.println(a1==b1);

对于基本数据的包装类型(Byte, Short, Character,Integer,Float, Double,Long, Boolean)除了Float和Double之外,其他的六种都是实现了常量池的。所以,a1和b1指向一样的地址,所以输出true。
5.我们使用下面的代码创建了两个Integer对象

        Integer a2=128;
        Integer b2=128;
        System.out.println(a2==b2);

虽然Integer实现了常量池,但是 Integer 在常量池中的存储范围为[-128,127],127在这范围内,因此是直接存储于常量池的,而128不在这范围内,所以会在堆内存中创建一个新的对象来保存这个值,所以a2和b2指向了不同的地址,所以输出false。

所以,==就是比较前后两个变量的值,因为引用变量里面保存的是实例变量的地址,我们要尤其注意以下。

equals(Object o)

equals是object类中的一个public方法,因此java中所有的类都具有equals方法,只不过有些类重写了equals方法。首先我们看以下Object类中的equals方法。

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

我们看到,在Object类中,就是使用==比较两个对象。所以,如果一个类没有重写equals方法,那么使用他的equals方法和使用==比较的结果是一样的。
但是很多类对equals方法进行了重写。
如String类的equals方法

    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) {           //比较两个String的值是否相等。
                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;
    }

在上述方法中,首先使用==比较两个对象,如果相等直接返回true。然后在比较两个String对象保存的值是不是相等。

HashCode

hashCode是object类中的一个方法,我们查看该方法如下。

    public native int hashCode();

在Object类中,该方法是一个native方法, 他的实现在native层中。很多类都对hashCode方法进行了重写。
如String方法中的hashCode

    public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

String方法的hashCode中,最终生成的hashCode和String保存的值有关。因此,如果两个Stnring的值相等,那么他们的hashCode也相等。
对于一个类来说,如果我们在散列集合(HashMap,HashSet,HashTable)中使用他,那么我们没必要重写他的hashCode方法。但是,如果我们需要在散列集合中使用他,我们就应该正确地书写这个hashCode方法。我们知道,在散列集合中,元素存储的位置是和他的hash值有关的,并且散列集合中相同的元素只能有一个。因此,我们设计的hashCode算法应该满足这样的条件:
1.如果A.equals(B),则A的hash值和B的相等。
2.如果A的hash值和B的相等,A不一定equalsB(HashCode冲突)。

上一篇下一篇

猜你喜欢

热点阅读