本篇博文分析基本类型,引用类型(包装类型)‘’==‘’和equals的区别和使用过程中的注意事项。
大家好,我是27岁的小学生liu__peng
面试题:== 和 equals 的区别是什么?
答案:
== 的作用:
基本类型:比较值是否相等
引用类型:比较内存地址值是否相等
equals 的作用:
引用类型:默认情况下,比较内存地址值是否相等。重写后比较的是地址值。当然,也可以根据自己的需求进行重写。
以下对具体的情况进行了说明,回顾相应的内容,也算是对本面试题的扩展与补充。
1.八大基本类型中,==和equals的使用
//基本类型中没有equals的方法,只能用==比较数值的大小是否相等。
//之前知识回顾,byte/short/char 三种比int小的整数可以用范围内的值直接赋值
byte b=10;
byte b1=10;
System.out.println(b==b1);//true
short s=110;
short s1=110;
System.out.println(s==s1);
//整数类型字面值是int类型,1000int类型,由小转大隐性转换,转换long类型。如果超过等号右面值超出int类型范围,必须加L或l
long l=1000;
long l1=1000;
System.out.println(l==l1);//true
long l2=10000000000;//编译错误
long l2=10000000000;//正确编辑方式
int i=200;
int j=200;
System.out.println(i==j);//true
double d=3.15;
double d1=3.15;
System.out.println(d==d1);//true
//浮点型 字面值是double,需要加后缀转换成double类型
float f=5.78f;
float f1=5.78f;
System.out.println(f==f1);//true
char c=1;
char c1=1;
System.out.println(c==c1);//true
//boolean比较特别一些,只有在比较的两个数都是true时才是true
boolean bo1=false;
boolean bo2=false;
System.out.println(bo1=bo2);
boolean bo3=true;
boolean bo4=true;
System.out.println(bo3=bo4);//true
2.引用类型 String,new的对象,包装类型
引用类型等号是比较的地址值;
equals 没重写前比较的是地址值,(实例化没有重写equals的引用类型);
equals 重写后比较的是数据的属性值,(String,Integer,Double,Short,Long, BigDecimal等)。idea开发软件已经重写过了,比较的都是地址值
下面对各种情况进行了具体的分析,篇幅比较长
2.1 String类型
通过底层代码可以知道,String是一个底层为value的char数组,是final修饰的,是不可变得字符串,存储在常量池中。但再次创建时,会首先从常量池中获取。
// String类型
String s="aa";
String s1="aa";
System.out.println(s==s1);//true 引用类型比较地址值,地址值相同
System.out.println(s.equals(s1));//true String类型开发工具改写,比较的是属性值
第一种情况:当采用字面量(给基本类型变量赋值的方式就叫做字面量或者字面值)创建字符串是,JVM首先会在字符串常量池中查找是否存在“aa”这个对象:
如果不存在,则在字符串常量池中创建这个对象,然后将这个对象的地址引用返回给引用s;
如果存在,则直接将这个对象的地址引用传递给引用s1,此时s和s1指向的是同一个常量池中的“aa”对象
String s1 = new String("abc");
String s2 = new String ("abc");
System.out.println(s1 == s2);//返回false
第二种情况:采用new关键字创建一个字符串对象时,JVM首先在字符串常量池中查找有没有“abc”字符串对象,如果有,则将这个字符串对象的引用传递给引用s1,然后在堆中创建一个"abc"字符串对象,最后将堆中的字符串地址返回给s1;
如果没有,则先在字符串中创建一个"abc"对象,然后在堆中创建一个"abc"字符串对象,最后将堆中"abc"字符串的地址返回给s1。
s2则指向堆中创建的另一个"abc"对象,这样s1和s2的内容是相同的,但是指向两个不同的"abc"对象。
就是说,通过new 构造器的方法创建之后,在heap堆中分别分配了两个内存地址。a 和 b 分别指向了堆中的两个不同的对象,不同的对象就会有不同的地址分配。
这里还有另一个面试题 String s=new String(“abc”)创建了几个对象?,后期会再整理
应用的情况分析
建议在平时的应用中,应尽量使用String x = "abcd"这种方式来创建字符串,而不是String x = new String(“abcd”); 因为用new构造器的方式肯定会开辟一个新的heap堆空间,而双引号的方式则是采用了String interning(字符串驻留)方式进行了优化,效率会比构造器的方式高。
扩展
判断各种字符串对象相等的情况
String s = "ab";
String s1 = "cd";
String s2 = s + s1;
String s3 = "abcd";
String s4 = "ab" + "cd";
String s5 = s + "cd";
String s6 = "ab" + new String("cd");
System.out.println(s2 == s3); // false
System.out.println(s2 == s4); // false
System.out.println(s3 == s4); // true
System.out.println(s3 == s5); // false
System.out.println(s2 == s5); // false
System.out.println(s3 == s6); // false
1)String类型运算时,默认new了StringBuilder对象,然后用append()方法追加字符串,最终以StringBuilder的toString()方法返回,而它的toString()方法是直接new了一个String对象返回的,那么 == 去比较两个String对象的地址,肯定是不相等的。
2)像"a"+"a"这种没有变量参与则在编译时就能确定,这种拼接会被优化,编译器直接帮你拼好,就不需要new的操作。
String a="a";
String b="b";
String c="c";
String d=a+b+c;
//等效于
String d=new StringBuilder().append(a).append(b).append(c).toString();
任重而道远
**2.2 包装类型 Integer Double Short等 的分析
2.2.1 ===的分析**
//以Integer为例
//第一种情况
Integer integer1 = 127;
Integer integer2 = 127;
System.out.println(integer1 == integer2);//true
//第二种情况
Integer integer3 = 128;
Integer integer4 = 128;
System.out.println(integer3 == integer4);//false
Integer类的底层为我们创建了int类型的-128~127一共256个对象。如果在自动装箱的时候给局部变量的int型值是在范围之内,就会直接把之前创建好的对象的地址值赋给等号左边的变量。如果在范围之外,就会重新new对象,两次new的对象就不再指向同一个对象了。在new的过程中并没有检测已有的空间,而是无条件的另外开辟了一个空间。
//比较两个new对象
Integer a = new Integer(5);//创建对象
Integer a1 = new Integer(5);//创建对象
System.out.println(a==a1);//false
每new一次就会在堆内存开辟空间,new两次就开辟两次空间,所以两次的地址值是不同的。
Integer b = Integer.valueOf(5);//读取缓存
Integer c = Integer.valueOf(5);//读取缓存
System.out.println(b==c);//true
Integer b = Integer.valueOf(128);//读取缓存
Integer c = Integer.valueOf(128);//读取缓存
System.out.println(b==c);//flase
调用Integer.value()的方法的判定方法和上面情况一致,先看看有没有超出int类型的范围。
总结:
1.Integer变量实际上是对一个Integer对象的引用,所以两个通过new生成的Integer变量永远是不相等的(因为new生成的是两个对象,其内存地址不同)
2.nteger变量和int变量比较时,只要两个变量的值是向等的,则结果为true(因为包装类Integer和基本数据类型int比较时,java会自动拆包装为int,然后进行比较,实际上就变为两个int变量的比较)
3.非new生成的Integer变量和new Integer()生成的变量比较时,结果为false。(因为非new生成的Integer变量指向的是java常量池中的对象,而new Integer()生成的变量指向堆中新建的对象,两者在内存中的地址不同)
4.对于两个非new生成的Integer对象,进行比较时,如果两个变量的值在区间-128到127之间,则比较结果为true,如果两个变量的值不在此区间,则比较结果为false。
Integer i1=128;
int i2=128;
Integer i3=Integer.valueOf(128);
Integer i4=new Integer(128);
System.out.println(i1 == i2);//true
System.out.println(i1 == i3);//false
System.out.println(i3 == i4);//false
System.out.println(i2 == i4);//true
//涉及到自动拆箱和自动装箱的问题
2.2.2 equals的对比
针对于包装类中的比较简单。包装类中的equals的方法都是经过重写的,比较的都是属性值,因此,只要属性值一致就返回true,反之返回false。
//重写后的代码如下
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
//测试,四种赋值方式情况下,equals的对比
Integer i1=128;
int i2=128;
Integer i3=Integer.valueOf(128);
Integer i4=new Integer(128);
System.out.println(i1.equals(i2));//true
System.out.println(i1.equals(i3));//true
System.out.println(i3.equals(i4));//true
System.out.println(i1.equals(i4));//true
你以为你真的会了吗?
看一个面试题
Integer i1=125;
Integer i2=125;
Integer i3=0;
Integer i4=new Integer(127);
Integer i5=new Integer(127);
Integer i6=new Integer(0);
System.out.println(i1==i2);
System.out.println(i1==i2+i3);
System.out.println(i4==i5);
System.out.println(i4==i5+i6);
i3=5;
Integer i7=130;
System.out.println(i7==i2+i3);
//如果你做对了,说明你已经正在掌握了相应的知识点。
对于 i1 == i2 + i3 、 i4 == i5 + i6 和 i7 == i2 + i3 结果为 true,是因为,Java 的数学计算是在内存栈里操作的,Java 会对 i5、i6 进行拆箱操作,其实比较的是基本类型(127=127+0),他们的值相同,因此结果为 true。对 i2+i3 来说,结果是在内存栈中(同 int 基本类型一样),所以不管是与 i1 还是 i7 比较,返回结果都为 true。
//答案
true
true
false
true
true
大家好,我是27岁的小学生liu__peng;
2021.01.07 0:21版本