==和equals总结

104 阅读6分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情

前言

在java中一般分为值类型和引用类型,值类型就是我们一般所使用的int、float、double等,而引用类型就是我们所使用的值类型所对应的包装类。包装类浅显地理解就是在值类型的基础上加入了对象的特性。一般在开发过程中我们常常会用到比较两个变量的值是否相等,这个时候就会使用到==和equals这两个东西

正文

首先值类型,平时在编程的时候,不论是Java还是使用其它的语言,值类型应该就是在数值层面上的比较。虽然这是一句废话,同为数值类型的变量如 short int long float double

之间可以相互进行比较,虽然存在短整型和长整型、单精度和双精度、整型和浮点数之间跨类型的比较,但是由于都属于一种大类数字,可以通过强转升级的模式来进行比较,例如

public static void main(String[] args) {
    int a = 1;
    double b = 1.00;
    System.out.println(a == b);
}
true

一个是整型另一个是浮点型,整型的会向浮点做升级,扩展到1.00,精度上保持一致,然后再来比较是否相等。

那么在等号两边如果类型不同呢?这里主要是针对于大类型不同,例如字符类、布尔类、字节类。因为在编写代码的过程中,常常我们会因为不够仔细,而使用不同类型的变量进行比较,那边这种做法会带来什么影响呢?我是用了一些例子来验证:

public static void main(String[] args) {
    int a = 1;
    char b = '1';
    int c = 49;
    double d = 49.00;
    boolean e = true;
    byte f = 49;
    System.out.println(a == b);
    System.out.println(b == c);
    System.out.println(b == d);
    System.out.println(a == f);
    System.out.println(b == f);
    // System.out.println(a == e);
    // System.out.println(b == e);
}
false
true
true
false
true

先说布尔类型,这个类型主要是有true和false,很简单。布尔类型和其它类型比较在编译的时候是不被允许的,在IDE中这样写无法通过的。

再说字符类型,在第一条输出返回了false,数值的1和字符的1通过==判断是不相等的。这说明两者在类型转换后的值并不相同。这里应该是涉及到了类型的升级,那么是数值类型转成了字符类型,还是字符类型转成了数值类型呢?

结论是字符类转成了数值类型,在代码中声明了一个49的整型变量,而字符1的ASCII码正是对应了49,两者转换后值相等。

在第三行输出中浮点数49.0和字符1的==判断为相等,说明字符类型会首先升级到整型,如果相比较的变量类型是浮点型,级别更加高,那么它还会升级到浮点型在统一进行比较。

最后说字节类,一开始我对字节类能和别的类型一同进行比较是有点不敢相信的。但是后面了解了Java类型之间的相互转换和升级,首先这几个类型的长度分别为

      byte 1个字节
      char 2个字节
      short 4个字节
      int 8个字节
      long 16个字节
      float 16个字节
      double 32个字节

低字节的可以向高字节的坐转换,这也就可以理解byte可以和int作比较了。所以在对于基本类型的时候,==之间的判断,往往是依赖于类型转换,将等号两边的变量升级到较高的类型,作统一的比较。

那么引用类型呢?首先,经过实验发现,像Integer,Long这样的引用类型是不能直接使用==来作比较的,IDE也会提示错误,除非你的包装类先进行拆包成基本类型,在基本类型的基础上进行比较。而String类型可以使用==来进行比较。底层上应该是String类型转换成char[]数组,然后进行for循环的比较,判断数组中的每个字符是否相等。

equals方法是属于Object父类中的一个方法,其它类只是重写了Object的这个equals方法。比如我们以那个Integer那举例

public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}

可以看到首先判断两个比较的类型是不是同一个类型,如果不是同一个类型直接返回false

如果判断是同一个类型那么首先就可以不报错的进行强转(因为已经判断了是同一个类型),强行转换之后进行拆包的操作。那么就可以等价之前的==操作来进行值的比较。

下面再举例一下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) {
            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;
}

和Integer一样首先都会判断两个变量的类型是否相同,如果相同,那么会判断两个字符串的长度是否一致,如果类型可以强转成功为String,同时两个的长度一样,那么两个转换成字符数组进行遍历比较

所以可以认为包装类的equals方法是首先判断是否可以强转类型,然后进行拆包进行==的值比较。另外我们经常会遇到的就是对象之间的比较。按照默认Object的equals方法。是比较两个对象的hashCode,一个实例对象它每次计算的hashcode是一致的,所以可以以它作为判断对象的唯一凭证

public static void main(String[] args) {

   User user1 = new User();
   User user2 = new User();
   System.out.println(user1.equals(user2));
   System.out.println(user1 == user2);
   System.out.println(user1.hashCode());
   System.out.println(user2.hashCode());
}

false
false
1160460865
1247233941

可以发现对象之间,只要是多次实例化的,不论是==还是equals判断,两者都是不相等的。

总结一下,==判断通常是将两个类型的变量通过类型转换的方式,统一到较高的类型然后进行值的比较。equals方法不会进行类型的强制转换使两个变量的类型统一,Object的equals方法是比较两个对象变量的hashcode,由于Object是所有类的基类,一般其它类会重写这个方法,例如包装类会进行拆包处理,然后就和==一样进行值的比较。