在JAVA中,==和equals两者都是比较是否相等,但是比较的对象有所不同。在开始讲两者的区别之前我们先来了解一下JAVA虚拟机中的内存结构。

Java最主要的三块内存:

栈——是线程私有的,用来存放局部变量(对象引用和基本数据类型,而不用于存储对象);堆——是程序共享的,存放对象实例数据(当需要一个对象时,使用new写一行代码,当执行这行代码时,会自动在堆里进行存储分配。);非堆——主要用来存放类信息(Class对象)。

各种数据存放的内存位置:

局部变量:栈中,包括基本类型(存放的是值)、对象引用、返回地址;

类变量:非堆,类变量算是类信息一部分;

字符串和基本类型常量:常量池(常量池已被放到堆中);

Class对象:非堆;

new对象:引用放到栈中,对象数据放在堆中;

总结一下:值类型是存储在内存中的栈中,而引用类型的变量在栈中仅仅是存储引用类型变量的地址,其本身的对象实例数据则存储在堆中。

==操作比较的是两个变量的值是否相等,对于引用型变量表示的是比较两个变量在栈中存储的地址是否相同。而equals操作表示两个变量是否是对同一个对象的引用,即堆中的内容是否相同。简单说就是,==比较的是栈内容,equals比较的是堆内容。因此,当equals为真时,==不一定为真。

一、String字符串的equals和==

1.测试样例一。具体代码如下

		String s1="Alex";
		String s2="Alex";
		
		//测试一
		System.out.println("测试样例一:");
		System.out.println(s1==s2);
		System.out.println(s1.equals(s2));

这里我们必须搞清楚String类型的数据存放在哪里?前面我们说,值类型(int a=1)存放在栈中,而常量(final int a=1)存放在堆的常量池中。因此我们可能会觉得String存放在栈中,但其实不是,它被存放在堆的常量池中。我们来看一下常量的定义

A constant variable is a final variable of primitive type or type String that is initialized with a constant expression . Whether a variable is a constant variable or not may have implications with respect to class initialization , binary compatibility , and definite assignment .

带final的基础类型或String类型并且用常量表达式初始化的才算是常量。常量表达式是一个代表基本数据类型或者String数据类型的表达式,是在编译期间能计算出来的值。

小结:

A.只有被final修饰的基本数据类型和String类型变量存放在常量池中。

B.而对于int a=1,数据是存在栈中的,因为这个值并不是常量

我们来看一下跑完的结果

原因:这里的s1,s2都属于都是字符串,属于常量,存放在堆的常量池中。当你创建s1对象的时候,程序会先去常量池中找是不是有相同的对象,也就是有没有"Alex"这个字符串,发现没有,于是它就在常量池中新建一个"Alex"对象。当你创建s2对象的时候,程序同样会先去常量池中寻找有没有"Alex"这个对象,发现有,于是就将s2引用到s1所引用的"Alex"对象。因此它们在栈中存储的引用地址相同,==返回true;在堆中的值也相同,equals返回true。

2.测试样例二。具体代码如下

		//测试二
		String s1="Alex";
		String s3=new String("Alex");
		System.out.println("测试样例二:");
		System.out.println(s1==s3);
		System.out.println(s1.equals(s3));

运行结果

原因:与s2不同,s3用了new关键字来声明。new关键字会明确地告诉程序,请给我单独创建一个新对象,不管常量池中有没有和我值相同的对象。由于对象不同,这时s1和s3在栈中的引用地址就会不同,因此==返回false。但是它们在堆中的值是相同的,因此equals返回true。

3.测试样例三。代码如下

		//测试三
		String s1="Alex";
		String s3=new String("Alex");
		s3=s3.intern();
		System.out.println("测试样例三:");
		System.out.println(s1==s3);
		System.out.println(s1.equals(s3));

运行结果

原因:跟前面的s3不同的是,这里的s3调用了intern()方法。这个方法会检查字符串池里是否存在值为"Alex"的字符串对象,如果存在,就返回池里的字符串对象;如果不存在,该方法会把"Alex"添加到字符串池中,然后再返回它的引用。因此这里s3又会和s1引用常量池中相同的对象。==和equals都会返回true。

二、基本数据类型及其封装类的测试

1.测试样例一。代码如下

	    //测试四:数据类型和对象
	    int a1=1;
	    int a2=1;
	    System.out.println(a1==a2);
	    System.out.println(a1.equals(a2));

上面的代码中最后一行会报错,原因很简单,int只是一个基本的数据类型,不是封装类,它并没有任何方法。第三行会返回true。因此==比较的是栈中的数据,而基本类型数据变量在栈中存的是值,显然a1和a2的值相等。在JAVA中数据类型的字母都是小写,而其相应的封装类首字母则是大写。比如int和Integer。

基本数据类型和引用类型的区别。基本类型只表示简单的字符或数字,引用类型可以表示复杂的数据结构,还可以定义操作这种数据结构的行为。对于基本类型,JVM会为其分配数据类型实际占用的内存空间,而对于引用类型变量,它仅仅是一个指向堆区中某个实例的指针。而在JAVA中字符串只有封装类String,没有基本数据类型string。因此我们前面可以直接用它的equals方法。

2.测试样例二。代码如下

	    Integer b1 =1;
	    Integer b2 =1;
	    System.out.println("测试样例二:");
	    System.out.println(b1==b2);
	    System.out.println(b1.equals(b2));

运行结果

3.测试样例三。代码如下

	    Integer b2 =1;
	    Integer b3 =new Integer (1);
	    System.out.println("测试样例三:");
	    System.out.println(b2==b3);
	    System.out.println(b2.equals(b3));

运行结果

样例二三的结果和前面String字符串是一样的,就不再解释了。

三、总结

1.String s1="xxx"和String s1=new String("xxx")是有一定区别的。一般情况下,new都会去新建一个对象。

2.equals是类中的一个方法。因此不同类的equals很可能是不同的,在写代码的过程中如果想要知道equals的具体功能,只需要查看它的源代码即可。当然如果我们也可以根据自己的需求去重新JAVA一些封装类的equals方法。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐