Equals和==的区别
来源:互联网 发布:批量域名查询 编辑:程序博客网 时间:2024/06/06 00:09
在阐述equals和==的区别前,我们要先简单说一下JVM中内存分配的问题。
当我们创建一个对象时,会在堆内存中开辟一段空间来存储对象数据,同时在栈内存中生成该对象的引用。后续代码调用时使用的都是栈内存中的引用。
特别地,基本数据类型是存储在栈内存中的。
基本数据类型
对于基本数据类型,直接用双等号(==)去比较它们的值。
复合数据类型
JAVA所有的类都是继承自Object这个基类的,可以从Object类中的equals方法实现中看出,equals方法比较的是对象的引用是否相同。这点上,和==是一致的。
除非,如String、Integer、Date等,一些类中的equals方法被重写,它们不再是比较类在堆内存中的存放地址了。
public boolean equals(Object obj) { return (this == obj); }
String
重点看一下String对象的equals方法应用。
我们可以通过很多途径创建String对象,既可以通过new操作,双引号(“”)和”+”操作符进行实例化,也可以从char array, byte array, StringBuffer and StringBuilder中获得String对象。而JVM使用字符串常量池保存了所有的String对象。
我们来看下面一段代码。
String s = new String("abc"); String s1 = "abc"; String s2 = new String("abc"); System.out.println(s == s1); System.out.println(s == s2); System.out.println(s1 == s2);
(1)String s = new String("abc");
这条语句的实际执行过程是,括号里的”abc”先到字符串常量池中看看有没”abc”这个对象,没有则在pool里创建这个对象,所以这里就在pool中创建了一个”abc”对象。然后通过new操作实例化了一个”abc”对象,存放在堆区。这里的s不是对象,只是引用,指向堆区里的对象。
(2)String s1 = "abc";
这条语句的实际执行过程是,”abc”到字符串常量池中看看有没”abc”这个对象,很显然,上一条语句已经在pool里创建了一个”abc”。所以这条语句没有创建对象,s1指向的是pool中的”abc”。
(3)String s2 = new String("abc");
这条语句和第一条语句类似,不同的是括号里的”abc”在pool中会找到之前创建的”abc”,它不会在pool中再创建对象了。相同的是会在堆区开辟一段内存,实例化一个新的”abc”对象。s2指向的是这个新的”abc”对象。当然,s和s2指向的是不同的对象。
来分析下运行结果,
s指向堆里的对象,s1指向字符串常量池里的对象,明显不同。
s与s2指向堆里不同的对象,虽然内容都是”abc”,但它们的物理地址并不相同。
s1与s2的比较同第一种比较,都返回false。
关于intern()方法的使用。
String s = new String("abc"); String s1 = "abc"; String s2 = new String("abc"); System.out.println(s == s1.intern()); System.out.println(s == s2.intern()); System.out.println(s1 == s2.intern());
s1.intern()的执行流程是在字符串常量池里去查找s1对应的内容(也就是”abc”)。如果找到,则返回pool里的对象,如果就在pool里创建这个对象并返回。
这样就很容易理解了,来看一下运行结果,
s1.intern()返回的是pool里的”abc”对象,与s指向的堆里的对象肯定不同,返回false。
s2.intern()返回的是pool里的”abc”对象,与s指向的堆里的对象肯定不同,返回false。
s1与s2.intern()都指向了pool中的”abc”对象,返回true。
关于字符串拼接的场景。
String hello = "hello"; String hel = "hel"; String lo = "lo"; System.out.println(hello == "hel" + "lo"); System.out.println(hello == "hel" + lo);
首先明确的是”hello”、”hel”和”lo”这三个都指向pool中的对象。
“hel”+”lo”按照内容来说,相加也就是”hello”。这时会返回pool中的”hello”对象。所以,hello == “hel” + “lo”返回的是true。
而”hel”+lo虽然内容也是”hello”,但是它将在堆里面生成一个”hello”对象,并返回这个对象,所以这里的结果是false。
关于对象拼接的场景。
intern()方法的功能是“如果常量池中存在当前字符串,就会直接返回当前字符串。如果常量池中没有此字符串,会将此字符串放入常量池中后,再返回”。设计的初衷就是重用String对象,以节省内存消耗。
JDK1.6以及以前版本中,常量池是放在Perm区(属于方法区)中的,熟悉JVM的话,应该知道这是和堆区完全分开的。
JDK1.6以后常量池被放置在了堆空间,因此常量池位置的不同会影响String的intern()方法的表现。
String s3 = new String("1") + new String("1"); s3.intern(); String s4 = "11"; System.out.println(s3 == s4);
(1)String s3 = new String("1") + new String("1");
这行代码在字符串常量池中生成“1” ,并在堆空间中生成引用s3指向的对象(内容为”11”)。注意,此时常量池中是没有”11”对象的。
(2)s3.intern();
这一行代码将s3中的“11”字符串放入字符串常量池中。
JDK1.6的做法是直接在常量池中生成一个”11”的对象;在JDK1.7中,常量池中不再存储一份对象了,而是直接存储堆中的引用。这份引用直接指向 s3引用的对象,也就是说s3.intern()==s3会返回true。
(3)String s4 = "11";
这一行代码会去常量池中创建”11”,但发现已经有这个对象了,也就是指向s3引用对象的一个引用。因此,s3 == s4返回了true,也就是说它们都指向了堆区存储的对象。
最后,再看一个对象拼接的例子。
String str1 = new String("a") + new String("bc"); System.out.println(str1.intern() == str1); System.out.println(str1 == "abc");
str1.intern() == str1
就是上面例子中的情况,str1.intern()发现常量池中不存在”abc”,便指向了str1。”abc”在常量池中创建时,也就直接指向了str1了。两个判断都会返回true。
String str2 = "abc";//新加的一行代码,其余不变 String str1 = new String("a") + new String("bc"); System.out.println(str1.intern() == str1); System.out.println(str1 == "abc");
str2在常量池中创建了”abc”,那么str1.intern()就直接指向了str2,后面的”abc”也一样指向str2。那么和堆空间中的str1都无关了,两个判断都会返回false。
参考:http://blog.csdn.net/seu_calvin/article/details/52291082
- “=”和“equals()”的区别
- java ==和equals、equals和hashCode的区别
- 关于“==”和“equals”的区别
- 关于Equals 和== 的区别
- equals和==的区别
- equals 和 == 的区别
- equals 和 == 的区别
- ==和Equals的区别
- equals 和 == 的区别
- equals 和 == 的区别
- equals和==的区别
- equals 和 == 的区别
- equals和==的区别
- 关于==和equals的区别
- Equals和“==”的区别
- == 和equals() 的区别
- equals("")和“==”的区别
- equals 和 == 的区别
- iFunk:用户全方位享乐体验的升级
- 学习C++要做笔记1(引用)
- 阿里云加密服务使用教程
- 微信开发笔记(1)——配置微信服务器、验证信息安全性
- Toolbar快速实现,让你一分钟上手
- Equals和==的区别
- Python学习 -- filter
- 逼格极高的注释
- TCP/UDP下的socket编程
- Java中创建对象的5种方式
- 测试mapper的工作
- forward 和redirect的区别
- 监控windows某个服务当服务停止后自动重启服务
- 仿 IOS-SlideBar