👽 博客主页: <芝士盘盘>
❤️🔥💗💓一键三连:👍 点赞 ✌️ 收藏 👌 关注
👻小小勉励:穷且益坚,不坠青云志
前言
在学习过程中,我们常常会忽略一些细节问题,这些问题往往隐藏在简单的语法下,很容易让人不去关注,但这些问题是切实存在的,不在意往往会导致编程中容易出现错误,想要成为优秀的程序员,我们应当多去深究这些看似影响不大的细枝末节。
一、"=="
“==”是一个运算符,其实大家很熟悉,在学习一开始学习C语言的时候就早已经遇到过,它最基本的作用肯定都知道,就是判断等号左右两边是否相等对吧。但是,在Java中,由于加入了对象🔍的概念,并且将数据类型分为了基本数据类型🔍和引用数据类型🔍,这就使得Java中使用它时候需要注意一些问题,接下来会进行讲解。
先说结论
“==”是运算符,如果是基本数据类型,则比较存储的值;如果是引用数据类型,则比较所指向对象的地址值。
ok,既然“==”左边和右边可以是基本数据类型和引用数据类型,那无非就是四种情况咯,现在一一说明
注:接下来的基本数据类型将用int举例,引用数据类型将用Integer举例。
左右两边都为基本数据类型
public class Test {
public static void main(String[] args) {
int a = 1;
int b = 1;
int c = 2;
System.out.println(a==b);
System.out.println(a==c);
}
}
输出结果:
true
false
这个不用多说,很显然。
左边为基本数据类型,右边为引用数据类型
public class Test {
public static void main(String[] args) {
int a = 1;
Integer b = 1;
Integer c = 2;
System.out.println(a==b);
System.out.println(a==c);
}
}
输出结果:
true
false
这里有一个细节,就是在比较的时候会进行自动拆箱🔍的操作,Integer类型与int类型用==比较时会自动拆箱成int类型与之比较,这时Integer变成了int,那就和上面的情况一样了,输出结果自然是一样的。
左边为引用数据类型,右边为基本数据类型
public class Test {
public static void main(String[] args) {
Integer a = 1;
int b = 1;
int c = 2;
System.out.println(a==b);
System.out.println(a==c);
}
}
输出结果:
true
false
这其实和上一种情况一样,只是左右顺序调换了而已。
左右两边都为引用数据类型
注意,这种情况是我们需要重点探讨的
public class Test {
public static void main(String[] args) {
Integer a = 100;
Integer b = 100;
Integer c = 200;
Integer d = 200;
System.out.println(a==b);
System.out.println(c==d);
}
}
输出结果:
true
false
诶?这时候小伙伴们就要问了,为啥呀,200和200难道不相等嘛?
其实原因很简单,前面说了,如果==比较的是引用数据类型,比较的是它们的地址,Integer本质上是一个类,我们给Integer赋值时,系统会进行一个操作,自动装箱🔍,也就是说将等号右边的int变成了Integer,这里调用了valueOf方法。
public class Test {
public static void main(String[] args) {
//Integer a = 100;
//Integer b = 100;
//Integer c = 200;
//Integer d = 200;
Integer a = Integer.valueOf(100);
Integer b = Integer.valueOf(100);
Integer c = Integer.valueOf(200);
Integer d = Integer.valueOf(200);
System.out.println(a==b);
System.out.println(c==d);
}
}
很显然,会导致问题出现的原因在于valueOf方法上,我们来看看valueOf的源码。
public static Integer valueOf(int i) {
if (i >= -128 && i <= 127)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
可以发现,当i >= -128 && i <= 127时,会返回一个缓存中已有的Integer对象,当i不在这个范围时,会返回一个新的对象。
噢!!这时候我们就可以明白了,我们赋值100的时候,属于-128到127的范围内,返回的引用都是同一个对象,那么同一个对象的地址当然是一样的咯。当赋值200的时候,不在范围内,返回的是一个新的对象,所以地址当然不一样,结果就是false。
二、“equals()”
equals是Object的方法,用来比较两个对象的内容是否相等。
方法源码:
public boolean equals(Object obj) {
return (this == obj);
}
很明显,我们可以看出这个方法还是比较2个对象引用地址是否相同,但是我们平时在调用String、Integer等封装类型时的equals方法时是比较的内容是否一致而不是地址一致,为此引用数据类型都重写🔍了equals方法。
下面,以String类型为例,我们来看看它的源码。
public boolean equals(Object anObject) {
//这里比较的是地址
if (this == anObject) {
return true;
}
//判断是否为String对象
if (anObject instanceof String) {
//是String对象进行强转
String anotherString = (String)anObject;
int n = value.length;
//判断两个string长度是否一样,一样的话进入循环
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
//循环判断每个字符是否一致
while (n– != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
根据以上源码可以看出封装类型的String对equals进行了重写,其他封装类型原理一样,只是根据不同的类型去判断值,所以如果还像上面比较200的时候就不会出现返回false的问题啦。
三、为何自定义类要重写“equals()”?
我们现在明白了引用类型都重写了Object类中的equals方法,这才使得我们在比较这些类型的时候比较的是他们的值而不是地址。那么,我们在自定义类的时候自然也要重写equals,因为所有类都是Object的子类,只有重写了equals才能实现比较的是值而不是地址的功能。
写在最后
完整的看完整篇博客,是不是觉得原来这里面还有这些门门道道呢,其实还有很多细节都值得我们去关注,只有在细节上钻研的越多,才能在写代码的时候犯的错误越少哦。