1. 创建字符串
1.1 String s1 = "hello world" 与 String s3 = new String("hello world") 的区别
1. String s1 = "hello world"; //String直接创建 2. String s2 = "hello world"; //String直接创建 3. String s3 = s1; //相同引用 4. String s4 = new String("hello world"); //String对象创建 5. String s5 = new String("hello world"); //String对象创建
String创建的字符串存储在class常量池中,new创建的字符串对象在堆上。
String str1 = "hello world";和String str3 = "hello world"; 都在编译期间生成了字面常量和符号引用,运行期间字面常量"hello world"被存储在运行时常量池(当然只保存了一份)。JVM在运行时会先查找常量池是否存在相同的字面常量,如果存在,则直接引用指向已经存在的字面常量;否则在运行时常量池开辟一个空间来存储该字面常量,并将引用指向该字面常量。
new关键字来生成对象是在堆区进行的,而在堆区进行对象生成的过程是不会去检测该对象是否已经存在的。因此通过new来创建对象,创建出的一定是不同的对象,即使字符串的内容是相同的。
1.2 String对象一旦创建就无法改变
\jdk1.6.0_14\src\java\lang\String.java 文件中。
打开这个类文件就会发现String类是被final修饰的:
1. public final class String 2. implements java.io.Serializable, Comparable<String>, CharSequence 3. { 4. /** The value is used for character storage. */ 5. private final char value[]; 6. 7. /** The offset is the first index of the storage that is used. */ 8. private final int offset; 9. 10. /** The count is the number of characters in the String. */ 11. private final int count; 12. 13. /** Cache the hash code for the string */ 14. private int hash; // Default to 0 15. 16. /** use serialVersionUID from JDK 1.0.2 for interoperability */ 17. private static final long serialVersionUID = -6849794470754667710L; 18. 19. ...... 20. 21. }
从上面的源码可以看出:
1)String类是final类,也就是说String类不能被继承,并且它的成员方法默认都是final方法。【在java中,被final修饰的类是不允许被继承的,并且该类中的成员方法默认都是final方法】
2)上面的源码列举了String类中所有的成员属性,从上面可以看出String类其实是通过char数组来保存字符串的。
下面继续看String类的一些方法实现:
1. public String substring(int beginIndex, int endIndex) { 2. if (beginIndex < 0) { 3. throw new StringIndexOutOfBoundsException(beginIndex); 4. } 5. if (endIndex > count) { 6. throw new StringIndexOutOfBoundsException(endIndex); 7. } 8. if (beginIndex > endIndex) { 9. throw new StringIndexOutOfBoundsException(endIndex - beginIndex); 10. } 11. return ((beginIndex == 0) && (endIndex == count)) ? this : 12. new String(offset + beginIndex, endIndex - beginIndex, value); 13. } 14. 15. public String concat(String str) { 16. int otherLen = str.length(); 17. if (otherLen == 0) { 18. return this; 19. } 20. char buf[] = new char[count + otherLen]; 21. getChars(0, count, buf, 0); 22. str.getChars(0, otherLen, buf, count); 23. return new String(0, count + otherLen, buf); 24. } 25. 26. public String replace(char oldChar, char newChar) { 27. if (oldChar != newChar) { 28. int len = count; 29. int i = -1; 30. char[] val = value; /* avoid getfield opcode */ 31. int off = offset; /* avoid getfield opcode */ 32. 33. while (++i < len) { 34. if (val[off + i] == oldChar) { 35. break; 36. } 37. } 38. if (i < len) { 39. char buf[] = new char[len]; 40. for (int j = 0 ; j < i ; j++) { 41. buf[j] = val[off+j]; 42. } 43. while (i < len) { 44. char c = val[off + i]; 45. buf[i] = (c == oldChar) ? newChar : c; 46. i++; 47. } 48. return new String(0, len, buf); 49. } 50. } 51. return this;
从上面可以看到,String的方法包括sub, concat, replace操作都是重新生成一个新的字符串,原始的字符串并没有被改变。对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新对象。
String类是不可改变的,所以一旦创建String对象,那么它的值就无法改变了。如果需要对字符串做很多修改,可以选择使用StringBuffer和StringBuilder类。
2. 字符串长度
用于获取有关对象的信息的方法称为访问器方法。String类的一个访问器方法是length()方法,它返回字符串对象包含的字符数。
1. public class StaticTest{ 2. public static void main(String args[]){ 3. String site = "www.baidu.com"; 4. int len = site.length; 5. System.out.println(+len); 6. } 7. }
输出结果是:13
3. 连接字符串
String类提供了两个连接字符串的方法
1)string1.concat(string2);
2) string1 + string2
4. StringBuffer和StringBuilder类
当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
在使用 StringBuffer 类时,每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,所以如果需要对字符串进行修改推荐使用 StringBuffer。
StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。下面摘了2段代码分别来自StringBuffer和StringBuilder,insert方法的具体实现:
StringBuilder的insert方法:
1. public StringBuilder insert(int index, char str[], int offset, 2. int len) 3. { 4. super.insert(index, str, offset, len); 5. return this; 6. }
StringBuffer的insert方法:
1. public synchronized StringBuffer insert(int index, char str[], int offset, 2. int len) 3. { 4. super.insert(index, str, offset, len); 5. return this; 6. }
由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。