不晓得大家有没有这样得经验,就是无心中点进去得一个业面,而后钻到外面浏览了良久,我就是这样得,明天无心中,ctrl+左键,就点进了string得源码,正好今天下午没啥事,就在外面看一下,没想到,下次缓过去,就是我共事拍我让我去吃饭,哈哈哈哈,不过益处就是,我这边也整顿了一些string类得知识点,也分享给大家,整顿得不好还望海涵
文章首发集体公众号:Java架构师联盟,每日更新技术好文
一、String类
想要理解一个类,最好的方法就是看这个类的实现源代码,来看一下String类的源码:
public final class String
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) {
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操作都会生成新的对象”。
二、字符串常量池
咱们晓得字符串的调配和其余对象调配一样,是须要耗费昂扬的工夫和空间的,而且字符串咱们应用的十分多。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。