JAVA中,字符串相加不一定相等的原因

//JAVA字符串的研究
public class JustString {
public static void main(String[] args) {
String s1 = "ZhouJie";
String s2 = "Zhou";
String s3 = "Jie";
String s4 = "Zhou" + "Jie";
String s5 = s2 + s3;
String s5s = s5.intern();
String s6 = "Zhou" + s3;
String s6s = s6.intern();

String s7 = new String("ZhouJie");
String s7s = s7.intern();

System.out.println(s1 == s4);// 1、true
System.out.println(s1 == s5);// 2、false
System.out.println(s1 == s5s);// 3、true
System.out.println(s1 == s6);// 4、false
System.out.println(s1 == s6s);// 5、true
System.out.println(s1 == s7);// 6、false
System.out.println(s1 == s7s);// 7、true

/*
 * 字符串常量+字符串常量=字符串常量? 仔细分析上面的结果后,很容易就发现这个等式貌似不成立,其实这句话是 正确的。
 * 问题在于+的两边真的是字符串?我们看到的都不一定是真实的,另外,我们也不能简单的用结果来反正我们的所见。
 * 
 * 分析一下:
 * 
 * 【一】 对于1和6的结果,这个很简单,s1是直接的赋值,其值在字符串常量池中,所以s1指向的是字符串常量池中的一个地址       引用 s7是通过new得到的,只要是new出来的东西就一定在堆中,所以s7指向的是堆中的一个引用,这两个地址值必然不 相等。
 * 故为false,相信都能看懂明白。
 *
 * 【二】 2和4放在一起说,因为他们的本质是一样的。
 * 对于2和4,如果用开始的那个等式来套用下,结果应该都是true,但java给出了false,看来不是这个等式有问题就是java对这个
 * 等式的处理有问题。事实是等式没有问题,问题在java对这个等式的处理方式上。
 * 当+号中有变量时(这里的变量指严格意义上的变量,除了直接的字面上的字符,所有需要一次以上处理才能成为直接 字面字符的     值存在时  java都一律将其视为变量),因为对于s2+s3和"Zhou"+s3,java并不知道这里面到底是什么,从上面的定义看,它只 知     道这个 变量是String类型的(对于运行前的编译来说这些信息足够了),既然是变量,那么java都会放在运行期才会去确定变量 的具     体值。
* ——PS:这里的解释放在多态里面也是通用的,即:子类的对象引用赋值给父类的引用,编译时只会检查是否具有继承关系,也    就是检查是否 是同一个类型的,至于具体的值不管,运行期间再说,最终的效果就是运行时的多态。

* 所以有句话很经典:覆盖是运行时的多态。因为子类的行为状态覆盖了父类的行为状态。

* 接着说字符串,既然都是变量,跟多态的原理一样,java会在运行的时候去处理+号两边的变量。但是具体的方式不一样,对于字    符串的 运算,如果是变量 ,那么java会先new出一个StringBuilder(在JDK6.0之前是StringBuffer),然后调用append()方法
* 来将+号两遍的字符串拼接起来(底层就是char数组的拼接),然后toString()之后返回给=号左边的变量,也就是说,最后得到的
* 是一个new出来的字符串,那么这个字符串必然是在堆中了。结果就必然会是false了。
* 2和4,一个+两遍都是变量,另一个只有一个变量,但对于java来说,只要是变量,它就会去new一个StringBuilder来拼接,对
* 它来说这没有什么区别。
* 顺便解释一下s4的运算机制,对于s4的+两边都是字面字符常量来说,java在编译期就将+两边拼接合并了而不会等到运行时再        算,因为在 java看来,这样的运算即使放到运行时也不会有变化,运行期间重点要解决的是变化的东西。
* 【三】 这里说下3、5、7的结果,在看过【二】以后,很容易就能明白其实3、5、7的本质也是一样的,以为在intern()之前,他    们都是new 出来的字符串。所以问题的关键就是 intern()这个方法 ,这是java.lang.String下的一个方法,返回值是一个String类    型,  这个方法 API 里的解释是: “当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串 (用equals(Object)    方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并返回此 String 对象的引用。”
* 也就是说,只要是String类型的引用变量(无论是否是new出来的),调用该方法最终会返回一个与其本身相等的存在于常量池中的
* 该字符串的地址引用。常量池中相同的字符串只有一份,调用过的都会得到唯一一个地址值,结果必然是true了。
* ——ps:intern()这个方法在数据库操作中的效果不容小觑,因为他能将所有相同的字符串引用都转化为统一的一个字符串常量池      中的引用, 而数据库的操作十分消耗内存资源,尤其是在要操作的数据量又十分庞大时,如果不使用intern(),相同的一个字符串    会因为多次的new 急剧的消耗有限的内存空间。比如一个数据库中-地区:"北京" 这个字符串占用4个字节,如果数据库有有十万    条数据,那么就会消耗十万*4字节, 这中消耗速度是很恐怖的。而调用intern()后,就只需要一个4字节的内存就够了。当然,框    架开发时一般不需要考虑这些问题。
* 另外,使用了intern()必然会在这个操作上消耗一些时间(很少), 但这些代价对于不使用intern()时内存的消耗和直接操作字符串     的耗时(更少)来说....呵呵,你懂的...
*   空间和内存的选择题在代码中时刻在上演着~
* 现在再来看看这个等式:字符串常量+字符串常量=字符串常量       ——豁然开朗。
*/

}
}
  • 12
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值