一、String的构造方法
可不只是 String a = "abc";
和 String a = new String("abc");
阅读 String 源码,发现 String 提供了很多种构造方法,这里列出常用的几种:
public String(String original){}
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
public String(char value[], int offset, int count) {
...
Arrays.copyOfRange(value, offset, offset + count);
}
// unicode数组构造
public String(int[] codePoints, int offset, int count) {};
String str_03 = new String(new int[]{0x61, 0x62, 0x63}, 0, 3);
// Constructs a new String by decoding the specified array of bytes using the platform's default charset.
public String(byte bytes[]) {
this(bytes, 0, bytes.length);
}
String str_04 = new String(new byte[]{0x61, 0x62, 0x63});
除了构造方法,还提供了很多静态工厂方法供用户直接将 char、int、long、float、double、boolean、char[]、Object 转化为 String 。
二、字符串常量池
对于语句 String s1 = new String("abc");
- new String(“abc”) 明确要求在 堆内存 创建一个新的对象,即便 “abc” 已存在于常量池中。
- 如果直接用字面值 “abc”,则引用的是 常量池中的对象。
- 常量池中的字符串和堆中的字符串是两个独立的对象,设计上是为了兼顾性能优化(常量池)和明确的实例化需求(new)。
所以不管常量池中有没有保存 “abc”,new String() 的语义都强制命令在堆中创建新的对象。
想要复用常量池中的对象,就必须依赖于 String xxx = "abc"
。 而不能 new String()
。
在该语句下,如果常量池中有 “abc” 的对象,就直接复用引用,创建了0次对象。
因此:对于创建了几次对象的问题:
String s1 = new String(“abc”);
- 如果常量池中没有”abc”,创建两次对象(常量池”abc”字面量 + 堆内存对象);
- 如果常量池中已经存在”abc”,创建一次对象 (堆内存);
String xxx = “abc”
- 如果常量池中没有”abc”,创建一次对象(常量池”abc”字面量);
- 如果常量池中已经存在”abc”,创建零次对象,直接指向常量池中的对象。
创建了几次对象?
String str_01 = "abc";
String str_02 = "abc" + "def";
String str_03 = str_01 + "def";
假设这句之前常量池中没有”abc” “def” 以及 “abcdef”
- 常量池中没有”abc”,在常量池创建一个对象;
- 表达式 “abc” + “def” 是一个常量表达式,得益于JVM编译器的优化(常量表达式会在编译器优化),直接将其计算为”abcdef”,在常量池中不存在,创建一个对象;
- str_01 是变量引用,虽然其值是常量 “abc”,但在编译期 无法确定 表达式的值。先创建字面量对象 “def” 在常量池中,创建 StringBuilder 对象,append 后 toString,直接存储在堆中。创建3个对象(一个在常量池,两个在堆内存)
三、intern()
/**
* Returns a canonical representation for the string object. * <p>
* A pool of strings, initially empty, is maintained privately by the * class {@code String}. * <p>
* When the intern method is invoked, if the pool already contains a * string equal to this {@code String} object as determined by * the {@link #equals(Object)} method, then the string from the pool is
* returned. Otherwise, this {@code String} object is added to the * pool and a reference to this {@code String} object is returned. * <p>
* It follows that for any two strings {@code s} and {@code t}, * {@code s.intern() == t.intern()} is {@code true} * if and only if {@code s.equals(t)} is {@code true}. * <p>
* All literal strings and string-valued constant expressions are * interned. String literals are defined in section 3.10.5 of the * <cite>The Java™ Language Specification</cite>.
* * @return a string that has the same contents as this string, but is * guaranteed to be from a pool of unique strings. */
public native String intern();
这是一个本地方法。通过C++实现。
作用是将堆中String的值直接推进常量池中,返回的是常量池中对应值的对象。
- intern() 方法的功能 是将字符串的值(即其内容)尝试存入 字符串常量池,并返回 常量池中该字符串的引用。
- 如果 常量池 中已经有一个与当前字符串对象内容相同的字符串(通过 equals 比较),则直接返回常量池中的引用;
- 如果 常量池 中还没有,则将当前字符串的内容加入常量池,并返回常量池中的引用。
四、String StringBuilder StringBuffer
阅读StringBuilder源码发现,StringBuilder实际上就是维护一个char[]数组。
append(String str)
append(String str) 的操作基于arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
,就是将str拼接到维护的char[]的尾部。
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
StringBuilder的自动扩容
扩容操作和ArrayList的扩容原理一致,超过容量后,扩容至 原容量 * 2 + 2,另外用 Arrays.copyOf(value, newCapacity)
把原有元素拷贝到新数组中。
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
private int newCapacity(int minCapacity) {
// overflow-conscious code
int newCapacity = (value.length << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
// 确保不会超过最大容量或int的最大值
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
StringBuffer
StringBuffer 的API和底层实现基本一致,不同之处在于StringBuffer的方法加了synchronized
锁,保证了线程安全性。