java中的==和equals

在了解==和equals之前, 我们先要了解java基本类型和引用类型的概念:

基本类型

java中一共分为8种基本数据类型:byte、short、int、long、float、double、char、boolean,其中byte、short、int、long是整型。float、double是浮点型,char是字符型,boolean是布尔型。

引用类型

我们可以这么理解, java中除去基本类型, 就是引用类型, 就像 Object obj = new Object(); 我们new出一个对象, 然后obj指向了它, obj就是引用类型变量. 上面讲述的8种基本类型, 它们对应着有8种封装类型, 如Integer, 它们也都是引用类型.

通过直接指针方式访问对象

图片来源 深入理解Java虚拟机 p49

栈中存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型, 它存储的直接就是对象地址). 上图有个小细节, double占了两个格子, 那是因为64位长度的long和double类型的数据会占用2个局部变量, 其余的数据类型只占用1个.

接下来我们正式来比较==和equals的用法

==

说在前面: ==是关系操作符, 生成的是一个boolean结果, 它们计算的是操作数的值之间的关系.
我们先看下面的代码

    public static void main(String[] args) {
        Integer i = new Integer(7);
        Integer j = new Integer(7);

        int k = 7;
        int m = 7;

        System.out.println(k == m); //true
        System.out.println(i == j);  //false
        System.out.println(i == k); //true
      
    }

这段代码一共包含了三种情况,我们来一一比较.

一、基本类型 == 基本类型

两个基本类型变量的比较, 比较的是他们的数值, 所以为true

二、引用类型 == 引用类型

两个引用类型变量的比较, 比较的是它们的地址, 也就是它们是否指向同一个对象, 如果指向的是同一个对象, 为true,否则为false. i和j各自都在堆中new出了各自的对象, 所以它们的比较为false

三、基本类型 == 引用类型

也就是基本类型变量和它的封装类型变量比较, 封装类型将会自动拆箱变为基本类型后再进行比较, 也就是两个基本类型变量进行比较, 比较的是它们的数值, 所以为true

可能遇到面试的踩坑点:

public class Main {

    public static void main(String[] args) throws InterruptedException {
        Integer i = 123;
        Integer j = 123;
        System.out.println(i == j); // true

        Integer a = 321;
        Integer b = 321;
        System.out.println(a == b); // false
    }

}

这里要求你了解java的自动装箱还有是否看过Integer的源码
i和j会被自动装箱, 所有Integer i = 123 等同于 Integer i = Integer.valueOf(123), 我们一起来看看valueOf的源码:


Integer valueOf源码

我们可以看到, 代码并不会直接return一个new Integer(i), 而是会先经过一层判断, 我们来看看 IntegerCache, 它是Integer的私有静态内部类


IntegerCache

我们可以看到默认的缓存最小值low是-128, 最大值high是127
我们再来看上面的程序 i和j的值是小于127的, 所以它们指向的是同一个对象, 而a和b是大于127的, 它们都是new Integer(i)创建了新的对象, 所以会返回false

小结: 带上基本类型变量的==比较, 将会比较它们的数值; 两个引用类型变量的比较, 会比较它们的地址.
要了解封装类型的自动装箱缓存.

equals

equals方法是基类Object中的方法, 我们先来看下Object中的写法


Object中equals方法

我们可以看到, 源码中的equals比较方法也是用==实现, 也就是比较的它们的地址, 如果指向的是同一个对象, 为true, 否则为false. 我们一起用测试代码来观察一下 :

    public class Person {

        private String name;

        private Integer age;

        public Person(String name, Integer age) {
            this.age = age;
            this.name = name;
        }

        public static void main(String[] args) {
            Person p1 = new Person("test", 24);
            Person p2 = new Person("test", 24);
            System.out.println(p1.equals(p2));// false
        }

    }

当我们定义Person这么一个实体类的时候, 我们希望p1和p2的equals返回的结果是true, 因为我们传入了相同的参数, 但是事实是p1和p2指向了堆中各自的两个对象, 用Object的equals只能是返回false, 那怎样才能如我们所想返回true呢, 方法就是重写Person的equals方法(下面的代码我隐去了get和set方法,小伙伴们自己敲的时候记得加上):

public class Person {

    private String name;

    private Integer age;

    public Person(String name, Integer age) {
        this.age = age;
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Person person = (Person) o;

        if (name != null ? !name.equals(person.name) : person.name != null) return false;
        return age != null ? age.equals(person.age) : person.age == null;
    }

    public static void main(String[] args) {
        Person p1 = new Person("test", 24);
        Person p2 = new Person("test", 24);
        System.out.println(p1.equals(p2));// true
    }

}

我们在Person类中定义了自己的equals方法, 就是当类型相同并且姓名和年龄相同时, 我们就返回true, 代表相同. 我们可以在生产环境中根据实际情况自己定义equals的逻辑. 细心的小伙伴可能发现我们在equals方法里也用了name.equals和age.equals , 那这些equals就不用重写吗? no , 这是因为String类和Integer类自己重写了equals方法, 有兴趣的小伙伴可以自己看看这些重写的equals方法.

注意: 如果你重写了equals方法, 那么你也需要重写hashCode方法

总结: 在==和equals上, 我们只要理解基本类型和引用类型的区别, 就能更好的理解这两者的区别

参考

  • 深入理解Java虚拟机 第二章
  • Java编程思想 P.44
  • 浅析Java基础类型与封装类型的区别
  • Java中九种基本数据类型以及他们的封装类
  • java基本类型与引用类型
  • Java-从堆栈常量池解析equals()与==

你可能感兴趣的:(java中的==和equals)