在面试中遇到了一个问题: Java String是线程安全的吗? 我刚开始回答它是不安全的, 因为方法都没有加synchronized
, 后来被面试官引导到字符串常量池才反应过来, String底层是private final char value[];
, 是常量, 所以String一定是线程安全的, 并不能从synchronized
来看是不是安全, 因为这个char数组就没法用方法修改.
从常量池的角度来看, 在JVM-字符创常量池已经总结了. 去理解String
到底在底层创建了多少个对象, intern()
的作用, 字符串拼接底层在干什么.
对于String底层, 我们直接先看构造方法. (JDK8下依然是char[], JDK9及之后就是byte[]了)
//this constructor is unnecessary since Strings are immutable.
public String() {
this.value = "".value;
}
// Unless an explicit copy of {@code original} is needed, use of this constructor is unnecessary since Strings are immutable.
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
// 用char sequence创建String对象, 也有public String(char value[], int offset, int count), 可以限制范围
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
public String(StringBuffer buffer) {
synchronized(buffer) {
this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
}
}
public String(StringBuilder builder) {
this.value = Arrays.copyOf(builder.getValue(), builder.length());
}
还有一些常用方法.
// 返回对应index的char字符
public char charAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
// 比较两个String,
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length; //类的内部可以调用value, 也不需要用length()方法
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) { //如果长度重叠部分有不同的字符, 则相当于把String的每一个char看成一个桶中的数字
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2; //长度重叠部分没有不同的字符, 比较长度
}
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);
}
// 返回一个char数组, 不能用Arrays.copyOf, 我的猜测是初始化阶段当String初始化的时候, Arrays还没有初始化
public char[] toCharArray() {
// Cannot use Arrays.copyOf because of class initialization order issues
char result[] = new char[value.length];
System.arraycopy(value, 0, result, 0, value.length);
return result;
}
// 重写了hashCode方法, 所以即使两个String对象的哈希值相同, 不代表内容相同
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
// 去掉首尾空格
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;
}
参考
- Javadoc
- Doubts about typecast “(String)anObject” and anotherString.value.length
- Why doesn’t String toCharArray use Arrays.copyOf?
comments powered by Disqus