Java中的String
- 格式:doc
- 大小:40.00 KB
- 文档页数:9
Java中的String(1)——趣说 初学Java有段时间了,感觉似乎开始入了门,有了点儿感觉。但是发现很多困惑和疑问而且均来自于最基础的知识,折腾了一阵子又查了查书,终于对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" 这样的表达是创建字符串的时候,程序首先会在这个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 对象的值了。真是爽啊,又快又方便!
请看这样: CODE: String s1=new String("abc"); String s2=new String("abc"); System.out.println(s1.equals(s2)); System.out.println(s1==s2);
这样的结果是第一个为True,第二个则为false,那么Java做了什么呢?为什么String s1="abc"; String s2="abc";时第二个返回是真,这样的就不行呢?来看它的构造函数:
CODE: private char value[]; public String(String value) { count = value.length(); this.value = new char[count]; value.getChars(0, count, this.value, 0); }
与它的 equals 函数: CODE: public boolean equals(Object anObject) { if (this == anObject) { //第一步 , 如果 引用为同一对象 ,肯定相等 ,So 反回真值. return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = count; if (n == anotherString.count) { char v1[] = value; char v2[] = anotherString.value; int i = offset; int j = anotherString.offset; while (n-- != 0) { if (v1[i++] != v2[j++]) { //循环比较每一个字符 ,有不同的就反回假了. return false; } } return true; } } return false; }
String啊String ,让我说你什么好呢?你为我们Java程序员带来所有的困扰还不够吗?看看String这一次又怎么闹事儿吧。 1. 回顾一下坏脾气的String老弟 例程1: class Str { public static void main(String[] args) { String s = "Hi!"; String t = "Hi!"; if (s == t) System.out.println("equals"); else System.out.println("not equals"); } } 程序输出什么呢?如果看客们看过我的《来自 String 的困惑》之一,相信你很快会做出正确的判断: 程序输出:equals
2. 哦,天哪,它又在搅混水了 例程2: class Str { public static void main(String[] args) { String s = "HELLO"; String t = s.toUpperCase(); if (s == t) System.out.println("equals"); else System.out.println("not equals"); } } 那么这个程序有输出什么呢?慎重!再慎重!不要被 String 这个迷乱的家伙所迷惑! 它输出:equals。WHY!!! 把程序简单的更改一下: class Str2 { public static void main(String[] args) { String s = "Hello"; String t = s.toUpperCase(); if (s == t) System.out.println("equals"); else System.out.println("not equals"); } } 你可能会说:不是一样吗?不!千真万确的,不一样!这一次输出:not equals。 Oh MyGOD!!!谁来教训一下这个 String 啊!
3. 你了解你的马吗? “要驯服脱缰的野马,就要了解它的秉性”牛仔们说道。你了解 String 吗? 解读 String 的 API ,可以看到:toUpperCase() 和 toLowerCase() 方法返回一个新的String对象, 它将原字符串表示字符串的大写或小写形势;但是要注意:如果原字符串本身就是大写形式或小写形式,那么返回原始对象。这就是为什么第二个程序中 s 和 t 纠缠不清的缘故 对待这个淘气的、屡教不改的 String ,似乎没有更好的办法了。让我们解剖它,看看它到底有什么结构吧: (1) charAt(int n) 返回字符串内n位置的字符,第一个字符位置为0,最后一个字符的位置为length()-1,访问错误的位置会扔出一块大砖头:StringIndexOutOfBoundsException 真够大的。 (2) concat(String str) 在原对象之后连接一个str,但是返回一个新的String对象。 (3) EqualsIgnoreCase(String str) 忽略大小写的equals方法。这个方法的实质是首先调用静态字符方法toUpperCase()或者toLowerCase()将对比的两个字符转换,然后进行==运算。 (4) trim() 返回一个新的对象,它将原对象的开头和结尾的空白字符切掉。同样的,如果结果与原对象没有差别,则返回原对象。 (5)toString() String类也有toString()方法吗? 真是一个有趣的问题,可是如果没有它,你的String对象说不定真的不能用在System.out.println() 里面啊。小心,它返回对象自己。 String类还有很多其他方法,掌握他们会带来很多方便;也有很多困惑,所以坚持原则,是最关键的。