想要相识一个类,最好的办法就是看这个类的实现源代码,来看一下String类的源码:
public final class String implements java.io.Serializable, Comparable, CharSequence{ /** The value is used for character storage. */ private final char value[]; /** The offset is the first index of the storage that is used. */ private final int offset; /** The count is the number of characters in the String. */ private final int count; /** Cache the hash code for the string */ private int hash; // Default to 0 /** use serialVersionUID from JDK 1.0.2 for interoperability */ private static final long serialVersionUID = -6849794470754667710L; ........}从上面可以看出几点:
1)String类是final类,也即意味着String类不能被继续,并且它的成员方法都默认为final方法。在Java中,被final修饰的类是不答应被继续的,并且该类中的成员方法都默认为final方法。
2)上面列举出了String类中全部的成员属性,从上面可以看出String类实在是通过char数组来保存字符串的。
下面再继续看String类的一些方法实现:
public String substring(int beginIndex, int endIndex) { if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } if (endIndex > count) { throw new StringIndexOutOfBoundsException(endIndex); } if (beginIndex > endIndex) { throw new StringIndexOutOfBoundsException(endIndex - beginIndex); } return ((beginIndex == 0) && (endIndex == count)) ? this : new String(offset + beginIndex, endIndex - beginIndex, value);}public String concat(String str) { int otherLen = str.length(); if (otherLen == 0) { return this; } char buf[] = new char[count + otherLen]; getChars(0, count, buf, 0); str.getChars(0, otherLen, buf, count); return new String(0, count + otherLen, buf);}public String replace(char oldChar, char newChar) { if (oldChar != newChar) { int len = count; int i = -1; char[] val = value; /* avoid getfield opcode */ int off = offset; /* avoid getfield opcode */ while (++i < len) { if (val[off + i] == oldChar) { break; } } if (i < len) { char buf[] = new char[len]; for (int j = 0 ; j < i ; j++) { buf[j] = val[off+j]; } while (i < len) { char c = val[off + i]; buf = (c == oldChar) ? newChar : c; i++; } return new String(0, len, buf); } } return this;}从上面的三个方法可以看出,无论是sub操、concat照旧replace操作都不是在原有的字符串上进行的,而是重新生成了一个新的字符串对象。也就是说进行这些操作后,最原始的字符串并没有被改变。
在这里要永久记取一点:“String对象一旦被创建就是固定不变的了,对String对象的任何改变都不影响到原对象,相干的任何change操作都会生成新的对象”。 二、字符串常量池
我们知道字符串的分配和其他对象分配一样,是需要消耗高昂的时间和空间的,而且字符串我们使用的非常多。JVM为了提高性能和镌汰内存的开销,在实例化字符串的时间进行了一些优化:使用字符串常量池。每当我们创建字符串常量时,JVM会首先查抄字符串常量池,假如该字符串已经存在常量池中,那么就直接返回常量池中的实例引用。假如字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。由于String字符串的不可变性我们可以十分肯定常量池中一定不存在两个相同的字符串(这点对理解上面至关重要)。
Java中的常量池,现实上分为两种形态:静态常量池和运行时常量池。
所谓静态常量池,即*.class文件中的常量池,class文件中的常量池不但仅包含字符串(数字)字面量,还包含类、方法的信息,占用class文件绝大部分空间。
而运行时常量池,则是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池。
来看下面的程序:
String a = "chenssy";String b = "chenssy";a、b和字面上的chenssy都是指向JVM字符串常量池中的"chenssy"对象,他们指向同一个对象。
String c = new String("chenssy");new关键字一定会产生一个对象chenssy(注意这个chenssy和上面的chenssy不同),同时这个对象是存储在堆中。以是上面应该产生了两个对象:保存在栈中的c和保存堆中chenssy。但是在Java中根本就不存在两个完全一模一样的字符串对象。故堆中的chenssy应该是引用字符串常量池中chenssy。以是c、chenssy、池chenssy的关系应该是:c--->chenssy--->池chenssy。整个关系如下: