不知道你们有没有这样得经历,就是无心中点进去得一个业面,而后钻到里面浏览了很久,我就是这样得,今天无心中,ctrl+左键,就点进了string得源码,正好今天下午没啥事,就在里面看一下,没想到,下次缓过来,就是我同事拍我让我去吃饭,哈哈哈哈,不过好处就是,我这边也整理了一些string类得知识点,也分享给你们,整理得很差还望海涵
文章首发我的公众号:Java架构师联盟,每日更新技术好文
1、String类
想要了解一个类,最好的办法就是看这个类的实现源代码,来看一下String类的源码:
public final class Stringjava
implements java.io.Serializable, Comparable<String>, 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) {jvm
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[i] = (c == oldChar) ? newChar : c; i++; } return new String(0, len, buf); } } return this;
}
从上面的三个方法能够看出,不管是sub操、concat仍是replace操做都不是在原有的字符串上进行的,而是从新生成了一个新的字符串对象。也就是说进行这些操做后,最原始的字符串并无被改变。
在这里要永远记住一点:“String对象一旦被建立就是固定不变的了,对String对象的任何改变都不影响到原对象,相关的任何change操做都会生成新的对象”。
2、字符串常量池
咱们知道字符串的分配和其余对象分配同样,是须要消耗高昂的时间和空间的,并且字符串咱们使用的很是多。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。性能