深入学习java源码之StringBuilder.insert()与StringBuilder.replace()
java 字符串初始化=“” 和=null的区别
问题一:
null和""的区别
String s=null;
string.trim()就会抛出为空的exception
String s="";
string.trim()就不会抛,为什么?
答:
NULL代表声明了一个空对象,根本就不是一个字符串。
""代表声明了一个对象实例,这个对象实例的值是一个长度为0的空字符串。
NULL代表声明了一个空对象,对空对象做任何操作都不行的,除了=和==
""是一个字符串了,只是这个字符串里面没有内容了
String s=null;只是定义了一个句柄,也就是说你有了个引用,但是这个引用未指向任何内存空间
String s="";这个引用已经指向了一块是空字符串的内存空间,是一个实际的东东了,所以你可以对它操作,而不用担心什么了
null是空对象 ""是空字符串
String s=null;//null是未分配堆内存空间
String a;//分配了一个内存空间,没存入任何对象
String a="";//分配了一个内存空间,存了一个字符串对象
String abc=null;
String abc="";
一般推荐使用第二种
第一种abc指向null,很多时候要判断字符串是否为空时,容易漏掉这种情况,在调用String的相关方法的时候就会出错
第二种则相对简单,String的方法都可以用,判断的时候也不会出错
问题二:
String s;和String s=null;和String s="a";有什么区别?
针对这三种情况,使用out.println(s);的时候,第一个会出现异常,第二个会输出null.第三个则会输出a.
这是为什么呢?这三句声明语句,各自作了什么呢?
答:
第一个只是定义了一个String类型变量s,并没有给它赋初值,在Java中,默认在使用一个变量的时候必须赋予它初值(降低风险)。
第二个和第三个都定义了String类型变量s,并赋予它初值,只不过第二个赋予的值为null(空)罢了
主要要理解的是String s; s为一个引用~~它不是对象
第一个是没有初始化的引用;
第二个为空引用;
第三个是在字符串池里写入一个字符'a',然后用s指向它。
另外,
String s="a"和String s=new String("a");是有本质上的区别的
前者是在字符串池里写入一个字符'a',然后用s指向它;
后者是在堆上创建一个内容为"a"的字符串对象。
String str="aaa"; //于栈上分配内存
String str=new String("aaa"); //于堆上分配内存
String s; 系统会自动赋值null
String s;只是给s分配一个内存空间
String s=null;是分配的空间中存储的值为空值
String s="a";这句就不用我多说了分配的空间的值为字符a
1) String abc=null;
2) String abc;
3)String a="";
4) String b="";
5) String c=new String("");
6) String d=new String("");
//1)等于2),和C语言不同,JAVA为安全原因不允许一个悬挂引用,没有赋值的引用地址一律自动赋值为NULL,以防止访问到任意内存
//3)和4)中,变量a和b将会指向同一内存地址(""的地址)
//5)和6)中,变量c和d不会指向同一地址,而是两个""内容的地址,并且和a,b不同,实际上,3)和4)相当于new String("").intern().
//String类维护着一个字符串池,对于像3)和4)这样的赋值方法,String会在这个池中查找字符串是否已经在池中,如果在,就直接指向该地址,
如果不在,生成一个实例放入池中再指向那个地址,可见对于同样内容的字符串多次引用时3)4)的方法要比5)6)的方法剩内存,之所以这样做,是
因为String是一个内容不可变的量,运用的是设计模式GOF.FlyWeight
但有个关键的一点,没有人说到,这就是:
String s;在什么情况下可以等同于String s=null;而在什么情况下又不等同?!
考虑下面的代码:
//StringTest.java
public class StringTest {
static String s; //*
public static void main(String[] args) {
//String s; //**
System.out.println(s);
}
}
编译并运行上面的代码,将打印null。
可见标有*号的行是自动初始化了的(s被自动初始化为null)。
而如果把标有**号的行取消注释,代码将不能通过编译,这是因为这行定义的是本地变量,而本地变量是不会自动初始化的。
由此得出结论:
在成员变量的定义中,String s;等同于String s=null;
而在本地变量(方法变量)的定义中,String s;不等同于String s=null;,这时要使用s必须显式地赋值。
这些虽然是小知识点,但在实际应用中很重要,也很容易被一些人忽视,特此提出。
还有一点要说明的是:
只要是在方法在中定义变量都要显示赋初值,main()方法也不例外,而在方法之外编译器回自动赋初值。
关于字符串的拼接,使用String,StringBuffer还是StringBuilder?
有很多不同的选项来连接Java中的String。例如,你可以使用简单的+或+ =,以及StringBuffer或StringBuilder。
那么,你应该选择哪种方法?
答案取决于连接String的代码。如果你是以编程方式添加新内容到String中,例如在for循环中,那么你应该使用StringBuilder。它很容易使用,并提供比StringBuffer更好的性能。但请记住,与StringBuffer相比,StringBuilder不是线程安全的,可能不适合所有用例。
你只需要实例化一个新的StringBuilder并调用append方法来向String中添加一个新的部分。在你添加了所有的部分之后,你就可以调用toString()方法来检索连接的String。
下面的代码片段显示了一个简单的例子。在每次迭代期间,这个循环将i转换为一个String,并将它与一个空格一起添加到StringBuilder sb中。所以,最后,这段代码将在日志文件中写入“This is a test0 1 2 3 4 5 6 7 8 9”。
StringBuilder sb = new StringBuilder(“This is a test”);
for (int i=0; i<10; i++) {
sb.append(i);
sb.append(” “);
}
log.info(sb.toString());
正如在代码片段中看到的那样,你可以将String的第一个元素提供给构造方法。这将创建一个新的StringBuilder,新的StringBuilder包含提供的String和16个额外字符的容量。当你向StringBuilder添加更多字符时,JVM将动态增加StringBuilder的大小。
如果你已经知道你的String将包含多少个字符,则可以将该数字提供给不同的构造方法以实例化具有定义容量的StringBuilder。这进一步提高了效率,因为它不需要动态扩展其容量。
String str="爱我还是他";
str=str+",我已看不到我们的好";
System.out.println(str); //“爱我还是他,我已看不到我们的好”
+在编译器作用下都会转成 StringBuilder 的append方法执行,所以如果抛开运行效率来说,它们其实本质是一样的。
本质一样是否就能说明它们时等价的呢?或者说能否为了方便直接用+来连接字符串,剩下的事就交给编译器了?继续看个例子,在这个例子中有个 for 循环进行字符串连接操作。
public class TestString2 {
public static void main(String[] args) {
String s = "www";
for (int i = 0; i < 10; i++)
s += i;
}
}
编译后的情况如下,不熟悉指令没关系,我们只看重要的部分,if_icmplt 8,这个就是 for 循环的条件判断,小于10则不断跳到8的位置,8后面其实就是创建 StringBuilder 对象,并以本地变量s的值初始化该对象,接着再将本地变量i append到 StringBuilder 对象中,最后调用toString方法将所得值存到本地变量s。
这样来看循环中每次都要创建 StringBuilder 对象,而且要调用toString方法,这样的执行效率显然比较低,而且增加了 GC 的压力。
把事情都丢给编译器是不友好的,为了能让程序执行更加高效,最好是我们自己来控制 StringBuilder 的实例,比如下面,只创建一个 StringBuilder 实例,后面用append
方法连接字符串。
public class TestString3 {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder("www");
for (int i = 0; i < 10; i++)
sb.append(i);
}
}
StringBuffer类型的拼接
StringBuffer sb=new StringBuffer("爱我还是他");
sb.append("。");
System.out.println(sb.toString()); //“爱我还是他。”
sb.insert(0, "你都已看不到我们的好,"); //这里前一个参数是插入的下标,后一个参数是插入的数据
System.out.println(sb.toString()); //“你都已看不到我们的好,爱我还是他。”
可以看到,只要运用的append或insert方法,那么StringBuffer本身的值就会改变,而不用重新赋值。
Modifier and Type | Method and Description |
---|---|
int |
capacity() 返回当前容量。 |
char |
charAt(int index) 返回 |
StringBuilder |
delete(int start, int end) 删除此序列的子字符串中的字符。 |
StringBuilder |
deleteCharAt(int index) 删除 |
void |
ensureCapacity(int minimumCapacity) 确保容量至少等于规定的最小值。 |
void |
getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) 字符从该序列复制到目标字符数组 |
StringBuilder |
insert(int offset, boolean b) 将 |
StringBuilder |
insert(int offset, char c) 在此序列中插入 |
StringBuilder |
insert(int offset, char[] str) 在此序列中插入 |
StringBuilder |
insert(int index, char[] str, int offset, int len) 在此序列中插入 |
StringBuilder |
insert(int dstOffset, CharSequence s) 将指定的 |
StringBuilder |
insert(int dstOffset, CharSequence s, int start, int end) 将指定的CharSequence的子 |
StringBuilder |
insert(int offset, double d) 在此序列中插入 |
StringBuilder |
insert(int offset, float f) 在此序列中插入 |
StringBuilder |
insert(int offset, int i) 将第二个 |
StringBuilder |
insert(int offset, long l) 在此序列中插入 |
StringBuilder |
insert(int offset, Object obj) 将 |
StringBuilder |
insert(int offset, String str) 将字符串插入到此字符序列中。 |
int |
lastIndexOf(String str) 返回指定子字符串最右边出现的字符串内的索引。 |
int |
lastIndexOf(String str, int fromIndex) 返回指定子字符串最后一次出现的字符串中的索引。 |
int |
length() 返回长度(字符数)。 |
int |
offsetByCodePoints(int index, int codePointOffset) 返回此序列中与 |
StringBuilder |
replace(int start, int end, String str) 用指定的String中的字符替换此序列的子字符串中的 |
java源码
package java.lang;
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
static final long serialVersionUID = 4383685877147921099L;
public StringBuilder() {
super(16);
}
public StringBuilder(int capacity) {
super(capacity);
}
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
public StringBuilder(CharSequence seq) {
this(seq.length() + 16);
append(seq);
}
@Override
public StringBuilder delete(int start, int end) {
super.delete(start, end);
return this;
}
public StringBuilder deleteCharAt(int index) {
super.deleteCharAt(index);
return this;
}
@Override
public StringBuilder replace(int start, int end, String str) {
super.replace(start, end, str);
return this;
}
public StringBuilder insert(int index, char[] str, int offset,
int len)
{
super.insert(index, str, offset, len);
return this;
}
@Override
public StringBuilder insert(int offset, Object obj) {
super.insert(offset, obj);
return this;
}
@Override
public StringBuilder insert(int offset, String str) {
super.insert(offset, str);
return this;
}
@Override
public StringBuilder insert(int offset, char[] str) {
super.insert(offset, str);
return this;
}
@Override
public StringBuilder insert(int dstOffset, CharSequence s) {
super.insert(dstOffset, s);
return this;
}
@Override
public StringBuilder insert(int dstOffset, CharSequence s,
int start, int end)
{
super.insert(dstOffset, s, start, end);
return this;
}
@Override
public StringBuilder insert(int offset, boolean b) {
super.insert(offset, b);
return this;
}
@Override
public StringBuilder insert(int offset, char c) {
super.insert(offset, c);
return this;
}
@Override
public StringBuilder insert(int offset, int i) {
super.insert(offset, i);
return this;
}
@Override
public StringBuilder insert(int offset, long l) {
super.insert(offset, l);
return this;
}
@Override
public StringBuilder insert(int offset, float f) {
super.insert(offset, f);
return this;
}
@Override
public StringBuilder insert(int offset, double d) {
super.insert(offset, d);
return this;
}
}
package java.lang;
import sun.misc.FloatingDecimal;
import java.util.Arrays;
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
int count;
AbstractStringBuilder() {
}
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
@Override
public int length() {
return count;
}
public int capacity() {
return value.length;
}
private int newCapacity(int minCapacity) {
// overflow-conscious code
int newCapacity = (value.length << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
{
if (srcBegin < 0)
throw new StringIndexOutOfBoundsException(srcBegin);
if ((srcEnd < 0) || (srcEnd > count))
throw new StringIndexOutOfBoundsException(srcEnd);
if (srcBegin > srcEnd)
throw new StringIndexOutOfBoundsException("srcBegin > srcEnd");
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
public AbstractStringBuilder delete(int start, int end) {
if (start < 0)
throw new StringIndexOutOfBoundsException(start);
if (end > count)
end = count;
if (start > end)
throw new StringIndexOutOfBoundsException();
int len = end - start;
if (len > 0) {
System.arraycopy(value, start+len, value, start, count-end);
count -= len;
}
return this;
}
public AbstractStringBuilder deleteCharAt(int index) {
if ((index < 0) || (index >= count))
throw new StringIndexOutOfBoundsException(index);
System.arraycopy(value, index+1, value, index, count-index-1);
count--;
return this;
}
public AbstractStringBuilder replace(int start, int end, String str) {
if (start < 0)
throw new StringIndexOutOfBoundsException(start);
if (start > count)
throw new StringIndexOutOfBoundsException("start > length()");
if (start > end)
throw new StringIndexOutOfBoundsException("start > end");
if (end > count)
end = count;
int len = str.length();
int newCount = count + len - (end - start);
ensureCapacityInternal(newCount);
System.arraycopy(value, end, value, start + len, count - end);
str.getChars(value, start);
count = newCount;
return this;
}
public AbstractStringBuilder insert(int index, char[] str, int offset,
int len)
{
if ((index < 0) || (index > length()))
throw new StringIndexOutOfBoundsException(index);
if ((offset < 0) || (len < 0) || (offset > str.length - len))
throw new StringIndexOutOfBoundsException(
"offset " + offset + ", len " + len + ", str.length "
+ str.length);
ensureCapacityInternal(count + len);
System.arraycopy(value, index, value, index + len, count - index);
System.arraycopy(str, offset, value, index, len);
count += len;
return this;
}
public AbstractStringBuilder insert(int offset, Object obj) {
return insert(offset, String.valueOf(obj));
}
public AbstractStringBuilder insert(int offset, String str) {
if ((offset < 0) || (offset > length()))
throw new StringIndexOutOfBoundsException(offset);
if (str == null)
str = "null";
int len = str.length();
ensureCapacityInternal(count + len);
System.arraycopy(value, offset, value, offset + len, count - offset);
str.getChars(value, offset);
count += len;
return this;
}
public AbstractStringBuilder insert(int offset, char[] str) {
if ((offset < 0) || (offset > length()))
throw new StringIndexOutOfBoundsException(offset);
int len = str.length;
ensureCapacityInternal(count + len);
System.arraycopy(value, offset, value, offset + len, count - offset);
System.arraycopy(str, 0, value, offset, len);
count += len;
return this;
}
public AbstractStringBuilder insert(int dstOffset, CharSequence s) {
if (s == null)
s = "null";
if (s instanceof String)
return this.insert(dstOffset, (String)s);
return this.insert(dstOffset, s, 0, s.length());
}
public AbstractStringBuilder insert(int dstOffset, CharSequence s,
int start, int end) {
if (s == null)
s = "null";
if ((dstOffset < 0) || (dstOffset > this.length()))
throw new IndexOutOfBoundsException("dstOffset "+dstOffset);
if ((start < 0) || (end < 0) || (start > end) || (end > s.length()))
throw new IndexOutOfBoundsException(
"start " + start + ", end " + end + ", s.length() "
+ s.length());
int len = end - start;
ensureCapacityInternal(count + len);
System.arraycopy(value, dstOffset, value, dstOffset + len,
count - dstOffset);
for (int i=start; i