Java基础(一):==与equals()的区别

==和equals()最大的区别:
就在于==是一个运算符,equals()是一个方法。

一、对==符号的理解

==可以对不同类型数据进行操作,其中包括:

  • 基本数据类型:(byte、short、int、long、float、double、boolean、char)
  • 引用数据类型:基本数据包装类(Byte、Short、Integer、Long、Float、Double、Boolean、Character),普通对象类型(User、Book等自定义或预定义对象)
  • String类型:也属于引用数据类型,但个人觉得比较特殊,就没放在一起,这里单独讨论下。

对各类数据的比较方式如下:

  • 对于基本数据类型,==就只看两个操作数据的值是否相等。比如两个int 型的变量,就只看两个变量的值是否相等。
  • 对于引用数据类型,==就只看两个数据的堆内存地址。比如两个new的User对象,new一次就新分配一个内存空间,因此两个new的User对象地址值就是不一样的。
  • 对于String类型,也是比较的两个String对象指向的内存是否相同,稍后详细分析。

二、对equals()的理解

    equals()方法在Object类中就有,由于Java中的每一个类都直接或者间接得继承了Object类,因此Java中的所有类都有equals()方法。

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

    查看Object类的equals()源码,可以发现,equals()中也是使用的==,这里也是比较当前调用对象的引用与被比较对象obj的引用是否相同,本质上还是和==一样。举个例子(如果类User的equals()方法没被重写,重写后会有其他结果),user.equals(otherUser)与user==otherUser本质上是一样的。
    就现在来看,equals()与==的第二种情况(引用数据类型)没区别,不过正因为equals()是一个方法,Java可以重写方法,所以在类的继承过程中,equals()的功能也是可能会发生变化。

三、重写equals()

1.String中的equals()

    String也属于引用对象,我们平常比较两个字符串是否相等主要看的是内容是否相等。但是如果使用==或者刚刚提到的Object中的原始equals()方法,比较的却是两个字符串的地址是否相等,与期望目标不一样,这该怎么办呢?好在Java设计者们早已想到这个问题,重写了String中的equals()方法。

public boolean equals(Object anObject) {
	if (this == anObject) {
		return true;C
    }
	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;
}

    上面是String源码中重写后的equals()方法,这个重写后的方法可以实现比较两个字符串的内容是否相等。步骤:(1)先看引用是否一致,(2)再看类型是否一致,(3)然后再看长度是否一致,(4)最后再看具体每个字符是否一致。

2.String.equals()的举例

public static void main(String[] args) {
	String s1 = "abcd";
	String s2 = new String("abcd");
	String s3 = "abcd";
	String s4 = s3;
	System.out.println(s1.equals(s2)); // true
	System.out.println(s1.equals(s3)); // true
	System.out.println(s1.equals(s4)); // true
	System.out.println(s2.equals(s3)); // true
	System.out.println(s2.equals(s4)); // true
	System.out.println(s3.equals(s4)); // true

	System.out.println(s1 == s2); // false
	System.out.println(s1 == s3); // true
	System.out.println(s1 == s4); // true
	System.out.println(s2 == s3); // false
	System.out.println(s2 == s4); // false
	System.out.println(s3 == s4); // true
}

    上述代码中定义了4个字符串,将4个字符串用==与equals()分别就进行了比较。s1为字符串常量,s2为新建的字符串对象,s3为字符串常量,s4引用了s3指向的地址,因此s1,s2,s3,s4的内容都是一样的,使用equals()进行比较时都相等。
    在采用==进行比较时,就要看具体的地址了。String是最基础的引用数据类型,也是Java中使用频率最高的数据类型之一,为了提高其性能,Java 设计者为 String 提供了字符串常量池的机制,因此在给字符串赋值、创建字符串对象时有其他处理。(具体内容可以参考:https://www.cnblogs.com/fanBlog/p/11311533.html,下图也为文中图片)
内存分配示意图
    新建字符串时都会先去常量池中查找是否有现成的字符串可以引用。所以直接给s1赋值字符串时,先去常量池查看是否有字符串”abcd”,若没有则在常量池新建一个,并把字符串引用给s1;创建s2会先去常量池查找是否有”abcd”,如果有的话会直接引用,再把新建的对象给s2;给s3赋值时,发现常量池有”abcd”了,因此直接把常量池中”abcd”的地址给s3;s4是直接得到s3的引用。
内存分配示意图
    上述代码的内存分配情况可以由上图显示,可见s1,s3,s4的指向地址一致,s2的指向地址与s1,s3,s4都不同,因此当==两侧有一边为s2时,结果都为false,其余情况都为true。

3.自定义类中的equals()

public class User {
	int id;
	String name;
	public User(){
		super();
	}
	public User(int id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
}

    先定义一个类User,此时没有重写equals()方法。

public class UserDemo {
	public static void main(String[] args){
		User user1 = new User(1, "Alice");
		User user2 = new User(1, "Alice");
		System.out.println(user1 == user2); // false
		System.out.println(user1.equals(user2)); // false
	}
}

    由于没有重写equals()方法,采用==与equals()的结果都为false,因为二者的本质都是比较两个对象的堆内存地址是否一致,但是两个对象都是新分配的空间,所以堆内存地址不一致。

public class User {
	int id;
	String name;
	public User(){
		super();
	}
	public User(int id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		User other = (User) obj;
		if (id != other.id)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
}

    重写User中equals()方法,重写用的eclipse的自动重写方法。可以发现重写方法比较的是User对象与被比较对象中的属性(属性为引用对象时也用的equals()来比较值)是否完全一致。

public class UserDemo {
	public static void main(String[] args){
		User user1 = new User(1, "Alice");
		User user2 = new User(1, "Alice");
		System.out.println(user1 == user2); // false
		System.out.println(user1.equals(user2)); // true
	}
}

    重新执行刚刚的代码,发现使用==与equals()的结果已经不一样了,虽然user1与user2的地址不一样,但是两个对象的属性值是完成一样的。因此==为false,equals()为true。

四、总结

  • 两个基本数据类型(不考虑类型转换)比较:
    • 只能用==,比较的是值是否相等
  • 两个引用数据类型比较:
    • 用==,比较两个引用类型的堆内存地址是否一致
    • 用equals(),未重写,比较两个引用类型的堆内存地址是否一致
    • 用equals(),已重写,根据具体重写方法进行比较
  • 一个基本数据类型(a)和一个引用数据类型(基本数据包装类obj)
    • 只能用obj.equals(a)形式进行比较,最终结果是看两个值是否相等

五、一点问题

public static void main(String[] args) {
	String s1 = "abcd";
	String s2 = "ab"+"cd";
	String s3 = "ab";
	String s4 = s3 +"cd";
	System.out.println(s1 == s2); // true
	System.out.println(s1 == s4); // false
	System.out.println(s2 == s4); // false
	String s5 = new String("abcd");
	s5 = s5.intern();
	System.out.println(s1 == s5); // true
	System.out.println(s2 == s5); // true
	System.out.println(s4 == s5); // false
}

    为什么会得到这六个结果呢,这个就涉及到编译的一些操作了,String复杂,可以深究的点非常多,自己很菜,也在学习中,以后有空整理整理。

六、参考资料

[1] 再也不要对java中==和equals的区别有困惑了,这篇文章保证你能懂
[2] equal与==的区别
[3] 在java中==和equals()的区别
[4] Java String:字符串常量池(转)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值