Java的String详解(反复研究)

JAVA中的equals和==的区别

 

==比较的是2个对象的地址,而equals比较的是2个对象的内容。显然,当equals为true时,==不一定为true。

 

折腾了一阵子又查了查书,终于对 String 这个特殊的对象有了点感悟

public class TestString {

    public static void main(String[] args) {

        String s1 = "Monday";

        String s2 = "Monday";

    }

}

有什么问题呢?

1. 来自 String 的忧虑

上面这段程序中,到底有几个对象呢?

可能很多人脱口而出:两个,s1 和 s2

为什么?

String 是 final 类,它的值不可变。

看起来似乎很有道理,那么来检测一下吧,稍微改动一下程序

就可以看到结果了:

public class TestString {

    public static void main(String[] args) {

        String s1 = "Monday";

        String s2 = "Monday";

        if (s1 == s2)

            System.out.println("s1 == s2");

        else

            System.out.println("s1 != s2");

    }

}

编译并运行程序,输出:s1 == s2

为什么 s1 == s2 ?

== 分明是在说:s1 与 s2 引用同一个 String 对象 -- "Monday"!

2. 千变万化的 String

再稍微改动一下程序,会有更奇怪的发现:

public class TestString {

    public static void main(String[] args) {

        String s1 = "Monday";

        String s2 = new String("Monday");

        if (s1 == s2)

            System.out.println("s1 == s2");

        else

            System.out.println("s1 != s2");

        if (s1.equals(s2))

            System.out.println("s1 equals s2");

        else

            System.out.println("s1 not equals s2");

    }

}

我们将 s2 用 new 操作符创建

程序输出:

s1 != s2

s1 equals s2

嗯,很明显嘛

s1 s2分别引用了两个"Monday"String对象

可是为什么两段程序不一样呢?

3. 在 String 的游泳池中游泳

哈哈,翻了翻书终于找到了答案:

原来,程序在运行的时候会创建一个字符串缓冲池

当使用 s2 = "Monday" 这样的表达式创建字符串的时候,JAVA首先会

在这个String缓冲池中寻找相同值的对象,在第一个程序中,s1先被

放到了池中,所以在s2被创建的时候,程序找到了具有相同值的 s1

将 s2 引用 s1 所引用的对象"Monday"

第二段程序中,使用了 new 操作符,他明白的告诉程序:

“我要一个新的!不要旧的!”与是一个新的"Monday"Sting对象被创

建在内存中。他们的值相同,但是位置不同,一个在池中游泳

一个在岸边休息。哎呀,真是资源浪费,明明是一样的非要分开做什么呢?

4. 继续潜水

再次更改程序:

public class TestString {

    public static void main(String[] args) {

        String s1 = "Monday";

        String s2 = new String("Monday");

        s2 = s2.intern();

        if (s1 == s2)

            System.out.println("s1 == s2");

        else

            System.out.println("s1 != s2");

        if (s1.equals(s2))

            System.out.println("s1 equals s2");

        else

            System.out.println("s1 not equals s2");

    }

}

这次加入:s2 = s2.intern();

哇!程序输出:

s1 == s2

s1 equals s2

原来,程序新建了 s2 之后,又用intern()把他打翻在了池里

哈哈,这次 s2 和 s1 有引用了同样的对象了

我们成功的减少了内存的占用

5. == 与 equals() 的争斗

String 是个对象,要对比两个不同的String对象的值是否相同

明显的要用到 equals() 这个方法

可是如果程序里面有那么多的String对象,有那么多次的要用到 equals ,

更好的办法:

把所有的String都intern()到缓冲池去吧

最好在用到new的时候就进行这个操作

String s2 = new String("Monday").intern();

现在我可以无所顾忌的用 == 来比较 String 对象的值了

真是爽啊,又快又方便!

 

测试代码:

public class StringAddress {

 public static void main(String[] args) {
  String a = "hehe";
  String b = "hehe";
  String c = new String("hehe");
  String d = a;
  String e = c.intern();
  if (a == b)
   System.out.println("a==b---使用 a='hehe'创建字符串的时候,JAVA首先在String缓冲池中寻找相同值,若找到则不新建对象");
  if (a == c)
   System.out.println("a==c---使用new操作符,重新申请内存");
  if (a.equals(c))
   System.out.println("a.equals(c)---值相同,内存地址不同");
  if (a == d)
   System.out.println("a==d---a==d ==> a.equals(d)");
  if (a.equals(d))
   System.out.println("a.equals(d)");
  if (a == e)
   System.out.println("a==e---intern()后都到String池中");
  System.out.println(System.getProperty("user.dir"));
 }
}

-------------------------------------------------------------------------------------------- 

 

String和StringBuffer的区别

而在某些特别情况下, String 对象的字符串拼接其实是被 JVM 解释成了 StringBuffer 对象的拼接,所以这些时候 String 对象的速度并不会比 StringBuffer 对象慢,而特别是以下的字符串对象生成中, String 效率是远要比 StringBuffer 快的: String S1 = “This is only a” + “ simple” + “ test”; StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”); 你会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在 JVM 眼里,这个 String S1 = “This is only a” + “ simple” + “test”; 其实就是: String S1 = “This is only a simple test”; 所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如: String S2 = “This is only a”; String S3 = “ simple”; String S4 = “ test”; String S1 = S2 +S3 + S4; 这时候 JVM 会规规矩矩的按照原来的方式去做

// String+String
  String tempstr = "abcdefghijklmnopqrstuvwxyz";
  int times = 3500;
  long lstart1 = System.currentTimeMillis();
  String str = "";
  for (int i = 0; i < times; i++) {
   str += tempstr;
  }
  long lend1 = System.currentTimeMillis();
  long time1 = (lend1 - lstart1) / 1000;
  System.out.println("String+String时间为: " + time1 + "毫秒");
  
  // "abcdefghijklmnopqrstuvwxyz"+"abcdefghijklmnopqrstuvwxyz"
  long lstart2 = System.currentTimeMillis();
  for (int i = 0; i < times; i++) {
   str = "abcdefghijklmnopqrstuvwxyz"+"abcdefghijklmnopqrstuvwxyz";
  }
  long lend2 = System.currentTimeMillis();
  long time2 = (lend2 - lstart2) / 1000;
  System.out.println("abcdefghijklmnopqrstuvwxyz+abcdefghijklmnopqrstuvwxyz时间为: " + time2 + "毫秒");

  // StringBuffer.append()
  long lstart3 = System.currentTimeMillis();
  StringBuffer sb = new StringBuffer();
  for (int i = 0; i < times; i++) {
   sb.append(tempstr);
  }
  long lend3 = System.currentTimeMillis();
  long time3 = (lend3 - lstart3) / 1000;
  System.out.println("StringBuffer.append()时间为: " + time3 + "毫秒");

 

 

---------------------------------------------------------------------------------------------

创建了几个对象

String   s   =   new   String( "xyz ");
这个语句创建了几个对象?
可能是一个,因为常量池中可能已经存在“xyz”这个对象了,所以这个语句不会再在常量池中创建了,只在堆里创建一个对象。如果常量池中不存在“xyz”,则会创建两个。

 

String s = "a" + "b";创建了几个对象

下面简单证明我的推断,首先编译这个类:

 

public class Test { 
private String s = "ab"; 
}

复制class文件备用,然后修改为

 

public class Test { 
private String s = "a" + "b"; 
}

再次编译,用ue之类的文本编辑器打开,察看二进制内容,可以发现,两个class文件完全一致,连一个字节都不差.

ok,真相大白了.根本不存在运行期的处理String b = "a" + "b";这样的代码的问题,编译时就直接优化掉了。

 

只有使用""和+创建的String对象才会加入String池中,其他用new或者String变量+""的形势均在用于存储对象的堆中。

栈(Stack):用于保存基本类型(byte,int,char)和对象引用,速度仅次于寄存器

堆(heap):用于存储复杂对象。String中有一个Value属性,由char[]型数组存储

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值