作者:NiceCui
String 类被 final 所修饰,也就是说 String 对象是不可变量,并发程序最喜欢不可变量了。String 类实现了 Serializable, Comparable
从一段代码说起:
- public void stringTest() {
- String a = "a" + "b" + 1;
- String b = "ab1";
- System.out.println(a == b);
- }
大家猜一猜结果如何?如果你的结论是 true。好吧,再来一段代码:
- public void stringTest() {
- String a = new String("ab1");
- String b = "ab1";
- System.out.println(a == b);
- }
结果如何呢?正确答案是 false。
让我们看看经过编译器编译后的代码如何
- //第一段代码
- public void stringTest() {
- String a = "ab1";
- String b = "ab1";
- System.out.println(a == b);
- }
- //第二段代码
- public void stringTest() {
- String a1 = new String("ab1");
- String b = "ab1";
- System.out.println(a1 == b);
- }
也就是说第一段代码经过了编译期优化,原因是编译器发现 "a"+"b"+1 和 "ab1" 的效果是一样的,都是不可变量组成。但是为什么他们的内存地址会相同呢?如果你对此还有兴趣,那就一起看看 String 类的一些重要源码吧。
一、 String 属性
String 类中包含一个不可变的 char 数组用来存放字符串,一个 int 型的变量 hash 用来存放计算后的哈希值。
- /** The value is used for character storage. */
- private final char value[];
- /** 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;
二、 String 构造函数
- //不含参数的构造函数,一般没什么用,因为value是不可变量
- public String() {
- this.value = new char[0];
- }
- //参数为String类型
- public String(String original) {
- this.value = original.value;
- this.hash = original.hash;
- }
- //参数为char数组,使用java.utils包中的Arrays类复制
- public String(char value[]) {
- this.value = Arrays.copyOf(value, value.length);
- }
- //从bytes数组中的offset位置开始,将长度为length的字节,以charsetName格式编码,拷贝到value
- public String(byte bytes[], int offset, int length, String charsetName)
- throws UnsupportedEncodingException {
- if (charsetName == null)
- throw new NullPointerException("charsetName");
- checkBounds(bytes, offset, length);
- this.value = StringCoding.decode(charsetName, bytes, offset, length);
- }
- //调用public String(byte bytes[], int offset, int length, String charsetName)构造函数
- public String(byte bytes[], String charsetName)
- throws UnsupportedEncodingException {
- this(bytes, 0, bytes.length, charsetName);
- }
三、 String 常用方法
- boolean equals(Object anObject)
- public boolean equals(Object anObject) {
- //如果引用的是同一个对象,返回真
- if (this == anObject) {
- return true;
- }
- //如果不是String类型的数据,返回假
- if (anObject instanceof String) {
- String anotherString = (String) anObject;
- int n = value.length;
- //如果char数组长度不相等,返回假
- if (n == anotherString.value.length) {
- char v1[] = value;
- char v2[] = anotherString.value;
- int i = 0;
- //从后往前单个字符判断,如果有不相等,返回假
- while (n--!=0) {
- if (v1[i] != v2[i]) return false;
- i++;
- }
- //每个字符都相等,返回真
- return true;
- }
- }
- return false;
- }
- String e1 = "good";
- String e2 = "good everyDay";
- e1.equals(e2); // 返回 false
- int compareTo(String anotherString)
- public int compareTo(String anotherString) {
- //自身对象字符串长度len1
- int len1 = value.length;
- //被比较对象字符串长度len2
- int len2 = anotherString.value.length;
- //取两个字符串长度的最小值lim
- int lim = Math.min(len1, len2);
- char v1[] = value;
- char v2[] = anotherString.value;
- int k = 0;
- //从value的第一个字符开始到最小长度lim处为止,如果字符不相等,返回自身(对象不相等处字符-被比较对象不相等字符)
- while (k < lim) {
- char c1 = v1[k];
- char c2 = v2[k];
- if (c1 != c2) {
- return c1 - c2;
- }
- k++;
- }
- //如果前面都相等,则返回(自身长度-被比较对象长度)
- return len1 - len2;
- }
- String co1 = "hello" ;
- String co2 = "hello";
- String co3 = "hello you";
- System.out.println(co1.compareTo(co2)); // 0
- System.out.println(co1.compareTo(co3)); // -4
- int hashCode()
- public int hashCode() {
- int h = hash;
- //如果hash没有被计算过,并且字符串不为空,则进行hashCode计算
- if (h == 0 && value.length > 0) {
- char val[] = value;
- //计算过程
- //s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
- for (int i = 0; i < value.length; i++) {
- h = 31 * h + val[i];
- }
- //hash赋值
- hash = h;
- }
- return h;
- }
- String a = "toyou";
- char val[] = a.toCharArray();
- char c1 = 't';
- char c2 = 'a';
- int f = c1;
- int e = c2;
- System.out.println(e); // 97 a
- System.out.println(f); // 116 t
- System.out.println(31 * val[0]); // 3596
- System.out.println(31 * c1); // 3596
- // hashCode 计算中 因为char 字符可以自动转换成对应的 int 整形
- boolean startsWith(String prefix, int toffset)
- public boolean startsWith(String prefix, int toffset) {
- char ta[] = value;
- int to = toffset;
- char pa[] = prefix.value;
- int po = 0;
- int pc = prefix.value.length;
- // Note: toffset might be near -1>>>1.
- //如果起始地址小于0或者(起始地址+所比较对象长度)大于自身对象长度,返回假
- if ((toffset < 0) || (toffset > value.length - pc)) {
- return false;
- }
- //从所比较对象的末尾开始比较
- while (--pc >= 0) {
- if (ta[to++] != pa[po++]) {
- return false;
- }
- }
- return true;
- }
- public boolean startsWith(String prefix) {
- return startsWith(prefix, 0);
- }
- public boolean endsWith(String suffix) {
- return startsWith(suffix, value.length - suffix.value.length);
- }
- String d = "www.58fxp.com";
- System.out.println(d.startsWith("www")); // true
- System.out.println(d.endsWith("com")); // true
- String concat(String str)
- public String concat(String str) {
- int otherLen = str.length();
- //如果被添加的字符串为空,返回对象本身
- if (otherLen == 0) {
- return this;
- }
- int len = value.length;
- char buf[] = Arrays.copyOf(value, len + otherLen);
- str.getChars(buf, len);
- return new String(buf, true);
- }
- String cat = "much";
- String newcat = cat.concat(" yes"); // much yes
- String replace(char oldChar, char newChar)
- public String replace(char oldChar, char newChar) {
- //新旧值先对比
- if (oldChar != newChar) {
- int len = value.length;
- int i = -1;
- char[] val = value;
- /* avoid getfield opcode */
- //找到旧值最开始出现的位置
- while (++i < len) {
- if (val[i] == oldChar) {
- break;
- }
- }
- //从那个位置开始,直到末尾,用新值代替出现的旧值
- if (i < len) {
- char buf[] = new char[len];
- for (int j = 0; j < i; j++) {
- buf[j] = val[j];
- }
- while (i < len) {
- char c = val[i];
- buf[i] = (c == oldChar) ? newChar: c;
- i++;
- }
- return new String(buf, true);
- }
- }
- return this;
- }
- String r1 = "how do you do";
- String r2 = r1.replace("do","is");
- System.out.println(r2); // how is you is
- String trim()
- public String trim() {
- int len = value.length;
- int st = 0;
- char[] val = value;
- /* avoid getfield opcode */
- //找到字符串前段没有空格的位置
- while ((st < len) && (val[st] <= ' ')) {
- st++;
- }
- //找到字符串末尾没有空格的位置
- while ((st < len) && (val[len - 1] <= ' ')) {
- len--;
- }
- //如果前后都没有出现空格,返回字符串本身
- return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
- }
- String t1 = " public void "; // 前后各一个空格
- System.out.println("t1:"+t1.length()); // 13 带空格长度
- String t2 = t1.trim();
- System.out.println("t2:"+t2.length()); // 11 去掉空格
- System.out.println(t2);
- String intern()
- public native String intern();
- String dd = new String("bb").intern();
将引言中第二段代码
- //String a = new String("ab1");
- //改为
- String a = new String("ab1").intern();
- int hash32()
- private transient int hash32 = 0;
- int hash32() {
- int h = hash32;
- if (0 == h) {
- // harmless data race on hash32 here.
- h = sun.misc.Hashing.murmur3_32(HASHING_SEED, value, 0, value.length);
- // ensure result is not zero to avoid recalcing
- h = (0 != h) ? h: 1;
- hash32 = h;
- }
- return h;
- }
- public int length() {
- return value.length;
- }
- public String toString() {
- return this;
- }
- public boolean isEmpty() {
- return value.length == 0;
- }
- public char charAt(int index) {
- if ((index < 0) || (index >= value.length)) {
- throw new StringIndexOutOfBoundsException(index);
- }
- return value[index];
- }
以上是一些简单的常用方法。
总结
每个字符串都是一个 String 对象,系统开发中将会频繁使用字符串,如果像其他对像那样创建销毁将极大影响程序的性能。
了解字符串常量池,首先看一下 堆栈方法区
字符串常量池存在于方法区
代码:堆栈方法区存储字符串
- String str1 = "abc";
- String str2 = "abc";
- String str3 = "abc";
- String str4 = new String("abc");
- String str5 = new String("abc");
来源: http://www.cnblogs.com/NiceCui/p/8046564.html