Java基础篇:equals()方法与==的区别

Java基础篇:equals()方法与==的区别

1、超类Object的equals()底层原理:

在Object超类中已经提供了equals()方法,源码如下:

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

所有的对象都拥有标识(内存地址)和状态(数据),同时“==”比较的是两个对象的内存地址,在Object的equals()底层调用的是==号,所以说Object的equals()是比较两个对象的内存地址是否相等,如果为true,则表示的引用的是同一个对象。

2、equals()与 == 的区别:

(1)== 号在比较基本数据类型时比较的是数据的值,而比较引用类型时比较的是两个对象的地址值;

(2)equals()不能用于基本的数据类型,对于基本的数据类型要用其包装类。

(3)默认情况下,从Object继承而来的 equals 方法与 “==” 是完全等价的,比较的都是对象的内存地址,因为底层调用的是 “==” 号,但我们可以重写equals方法,使其按照我们的需求方式进行比较,如String类重写equals()方法,使其比较的是字符的内容,而不再是内存地址。

3、equals()的重写规则:

  • 自反性。对于任何非null的引用值x,x.equals(x)应返回true。

  • 对称性。对于任何非null的引用值x与y,当且仅当:y.equals(x)返回true时,x.equals(y)才返回true。

  • 传递性。对于任何非null的引用值x、y与z,如果y.equals(x)返回true,y.equals(z)返回true,那么x.equals(z)也应返回true。

  • 一致性。对于任何非null的引用值x与y,假设对象上equals比较中的信息没有被修改,则多次调用x.equals(y)始终返回true或者始终返回false。

  • 对于任何非空引用值x,x.equal(null)应返回false。

4、有关equals()与 == 号的小例子:

public class Test {
	public static void main(String[] args) {	
		String str1 = new String("abc");
		String str2 = new String("abc");
		System.out.println(str1 == str2);//false
		System.out.println(str1.equals(str2));//true
		
		String str3 = "123";
		String str4 = "123";
		System.out.println(str3 == str4);//true
		System.out.println(str3.equals(str4));//true	
	}
}

根据上面的demo输出结果可以看到,前面的输出结果是false、true,后面的输出结果是true、true。为什么两次==的输出结果不一样呢?这其实涉及到了内存中的常量池,常量池属于方法区的一部分,当运行到创建str3对象时,如果常量池中没有“123”,则在常量池中创建一个”123″对象,运行到str4对象时,由于“123”已经存在常量池,就直接使用,所以str3和str4对象其实是同一个对象,他们的地址引用相同。

而对于str1和str2,它们其实创建了两次对象,一次在常量池创建了对象“abc”,所以str1和str2的地址值是不相等的。

5、重写equals()中 getClass 与 instaceof 的区别:

在重写equals() 方法时,一般都是推荐使用 getClass 来进行类型判断,不是使用 instanceof(除非所有的子类有统一的语义才使用instanceof)。我们都知道 instanceof 的作用是判断其左边对象是否为其右边类的实例,返回 boolean 类型的数据,可以用来判断继承中的子类的实例是否为父类的实现。

下来我们来看一个例子:父类Person

public class Person {
        protected String name;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Person(String name){
            this.name = name;
        }
        public boolean equals(Object object){
            if(object instanceof Person){
                Person p = (Person) object;
                if(p.getName() == null || name == null){
                    return false;
                }
                else{
                    return name.equalsIgnoreCase(p.getName ());
                }
            }
            return false;
       }
    }

子类 Employee:

public class Employee extends Person{
        private int id;
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public Employee(String name,int id){
            super(name);
            this.id = id;
        }
        /**
         * 重写equals()方法
         */
        public boolean equals(Object object){
            if(object instanceof Employee){
                Employee e = (Employee) object;
                return super.equals(object) && e.getId() == id;
            }
            return false;
        }
    }

上面父类 Person 和子类 Employee 都重写了 equals(),不过 Employee 比父类多了一个id属性,而且这里我们并没有统一语义。测试代码如下:

public class Test {
        public static void main(String[] args) {
            Employee e1 = new Employee("employee", 23);
            Employee e2 = new Employee("employee", 24);
            Person p1 = new Person("employee");
            System.out.println(p1.equals(e1));//true
            System.out.println(p1.equals(e2));//true
            System.out.println(e1.equals(e2));//false
        }
    }

上面代码我们定义了两个员工和一个普通人,虽然他们同名,但是他们肯定不是同一人,所以按理来说结果应该全部是 false,但是事与愿违,结果是:true、true、false。对于那 e1!=e2 我们非常容易理解,因为他们不仅需要比较 name,还需要比较 ID。但是 p1 即等于 e1 也等于 e2,这是非常奇怪的,因为 e1、e2 明明是两个不同的类,但为什么会出现这个情况?首先 p1.equals(e1),是调用 p1 的 equals 方法,该方法使用 instanceof 关键字来检查 e1 是否为 Person 类,这里我们再看看 instanceof:判断其左边对象是否为其右边类的实例,也可以用来判断继承中的子类的实例是否为父类的实现。他们两者存在继承关系,肯定会返回 true 了,而两者 name 又相同,所以结果肯定是 true。所以出现上面的情况就是使用了关键字 instanceof,这是非常容易导致我们“钻牛角尖”。故在覆写 equals 时推荐使用 getClass 进行类型判断。而不是使用 instanceof(除非子类拥有统一的语义)。 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/114675.html原文链接:https://javaforall.net

(0)
上一篇 2021年10月4日 上午8:00
下一篇 2021年10月4日 上午9:00


相关推荐

  • 基于Html5的移动端APP开发框架「建议收藏」

    基于Html5的移动端APP开发框架「建议收藏」快速增长的APP应用软件市场,以及智能手机的普及,手机应用:Native(原生)APP快速占领了APP市场,成为了APP开发的主流,但其平台的不通用性,开发成本高,多版本开发等问题,一直困扰着专业APP开发企业,和APP服务提供商。安卓和IOS的操作方式,开发模式,界面UI显示方面的差别,也使得原生APP的不同版本体验有很大的区别,光是做兼容性调测,都要花费开发企业不少的时间。近年来,另一种

    2022年6月15日
    170
  • linux 文本编辑器vi常用命令

    linux 文本编辑器vi常用命令linux之文本编辑器vi常用命令由于经常在linux下面文本操作,所以这里稍微系统的总结一下自己常用的vi命令1、打开命令:vi+filename(还有各种打开的姿势,只不过我比较顺手这个)2、退出命令::q退出而且不保存修改的内容:q!强制退出不保存修改的内容:wq退出并且保存修改的内容:wq!强制保存修改的内容然后退出(修改了只读文件会用到)ZZ退出并且保存修改的内容,相当于:wq,看个人习惯3、光标移动命令个人比较喜欢上下左右方向键,字母h(左),j

    2022年7月26日
    6
  • eclipse+birt报表开发

    eclipse+birt报表开发工具下载 https pan baidu com s 1pMEZwfh1 创建一个 Java 项目 2 制定一个存放报表模板的包 创建 report 模板 newreport 创建名为 mytest 1 rptdesign 的模板 3 创建数据源和数据集 3 新建数据源和数据集方式一 基于数据库 sql3 1 创建数据源 DataSources nbsp

    2026年3月26日
    2
  • Linux下ffmpeg安装教程(亲测有效)「建议收藏」

    Linux下ffmpeg安装教程(亲测有效)「建议收藏」Linux下ffmpeg安装教程(亲测有效)

    2025年11月8日
    3
  • 不定积分知识结构图_不定积分计算法则总结

    不定积分知识结构图_不定积分计算法则总结本篇幅是关于大部分不定积分计算的总结 该部分内容会涉及到某些三角函数的知识 大家有空的时候去看下我之前的文章 山城门徒 高中三角函数公式推理 amp 记忆 zhuanlan zhihu com 文中若有错误的地方 恳请广大 乎友 带佬 们指正 若对你的学习有帮助 请不忘点个赞 不要只收藏 或转发给你身边正在备考 学习的同学 在下表示万分感谢 图 1 分割线内容概要 不定积分的相关概念 常用不定积分公式

    2026年3月18日
    2
  • InvalidateRect说明

    InvalidateRect说明nbsp nbsp nbsp nbsp InvalidateRe 是将窗口中的一块矩形区域标注为 无效 系统会不断向窗口发送 WM PAINT 消息令其重绘 在响应 WM PAINT 消息时 需要调用 BeginPaint 获取 DC 来进行重绘 该函数会合并所有 无效 区域 对 DC 进行裁剪 将整个窗口标注为 有效 清除 WM PAINT 消息 DC 经裁剪之后 在进行绘制时 超出 DC 范围的操作将不被处理 所以即使在响应 W

    2026年3月17日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号