String不可变性
- String不可变性:指的是字符串对象调用的方法,看起来会改变字符串内容都是返回一个新对象。
String message ="i am not mutable";
String s = message.toUpperCase();
Assert.assertNotEquals(s,message);
String s1 = message.replaceAll("mutable", "chageable");
Assert.assertNotEquals(s1,message);
重载+与StringBuilder
2.重载+与StringBuilder:乍看起来,字符串+叠加实现方式:
String message = "A";
String s = "B" + message+"C";
string方法有一个append方法,并且通过append方法来生成一个新的字符串.大概方式可以看做以下代码
String message = "AAA";
String s = "BBB"+message+"ccc";
Assert.assertEquals(s,"BBBAAAccc");
然而这样的性能会非常差,原因是因为字符串函数,返回新字符串,所以为了生成最终的字符串"BAC",会生成很多的中间字符串变量。造成很多的垃圾回收对象。通过Javap反编译上述代码会得到
0: ldc #8 // String AAA
2: astore_1
3: new #9 // class java/lang/StringBuilder
6: dup
7: invokespecial #10 // Method java/lang/StringBuilder."<init>":()V
10: ldc #11 // String BBB
12: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: aload_1
16: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: ldc #13 // String ccc
21: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: invokevirtual #14 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
通过源码可以看出+号,经过编译器编译以后可以分为以下几步:
1. StringBuilder builder = new StringBuilder("AAA");
2. builder.append("BBB");
3. builder.append("ccc");
4. builder.toString();
编译器优化步骤可以看出,在一个表达式中,如果有字符串+,则第一个+号左边参数调用StringBuilder()构造函数构造StringBuilder对象,然后利用sb.append()方法以此拼接+右边的参数。
虽然编译器为我们优化了+,但是在以下等操作较为复杂的+场景中,我们应该优先使用显式StringBuilder来调用
/**
* 编译器并不能够完全为我们优化
*/
@Test public void testImplictVsExplicti(){
String message ="A";
for (int i = 0; i < 10; i++) {
message+="A";
}
StringBuilder stringBuilder = new StringBuilder("A");
for (int i = 0; i <10; i++) {
stringBuilder.append("A");
}
System.out.println(message);
System.out.println(stringBuilder.toString());
}
public void testImplictVsExplicti();
Code:
0: ldc #17 // String A
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: bipush 10
8: if_icmpge 37
11: new #9 // class java/lang/StringBuilder
14: dup
15: invokespecial #10 // Method java/lang/StringBuilder."<init>":()V
18: aload_1
19: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: ldc #17 // String A
24: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: invokevirtual #14 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
30: astore_1
31: iinc 2, 1
34: goto 5
37: new #9 // class java/lang/StringBuilder
40: dup
41: ldc #17 // String A
43: invokespecial #18 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
46: astore_2
47: iconst_0
48: istore_3
49: iload_3
50: bipush 10
52: if_icmpge 68
55: aload_2
56: ldc #17 // String A
58: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
61: pop
62: iinc 3, 1
65: goto 49
68: getstatic #19 // Field java/lang/System.out:Ljava/io/PrintStream;
71: aload_1
72: invokevirtual #20 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
75: getstatic #19 // Field java/lang/System.out:Ljava/io/PrintStream;
78: aload_2
79: invokevirtual #14 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
82: invokevirtual #20 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
85: return
二者循环内容不一致,编译器只会在每次循环中帮我们新建StringBuilder,这样任然会产生非常多的中间变量对象。也就是简单来说,如果我们在多个编写语句中使用+,仍然会导致非常多的中间对象产生,如以下例子
/**
* 避免由于编译器产生过多垃圾回收对象
*/
@Test public void testApplicate(){
String message ="1";
message = message+"2";
message = message+"3";
System.out.println(message+"此处仍然会产生非常多StringBuilder导致的垃圾变量");
}
StringBuilder是在JavaSE5中引入的,之前编译器优化使用的是StringBuffer,而StringBuffer是线程安全的。因此Java5之后字符串+拼接速度会快一些。这里也可以理解为什么使用StringBuilder来替换StringBuffer.因为StringBuffer是加锁的,但是这里变量只在线程上不用考虑并发。所以使用StringBuilder即可
无意识递归
- 无意识递归,查看以下代码
/**
* toString方法中打印this
*/
@Test public void TestInfiniteRecursion(){
class Temp {
@Override
public String toString() {
return "我的内存地址为"+this;
}
}
Temp temp = new Temp();
System.out.println(temp);
}
java.lang.StackOverflowError
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:449)
at java.lang.StringBuilder.append(StringBuilder.java:136)
at com.thinkinjava.ThinkStringTest$1Temp.toString(ThinkStringTest.java:57)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at com.thinkinjava.ThinkStringTest$1Temp.toString(ThinkStringTest.java:57)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
这个时候我们应该按照如下编写代码
/**
* toString方法中打印this
*/
@Test public void TestInfiniteRecursion(){
class Temp {
@Override
public String toString() {
return "我的内存地址为"+super.toString();
}
}
Temp temp = new Temp();
System.out.println(temp);
}
Java中正则表达式
4.Java中正则表达式
- match():判断字符串是否满足某个正则表达式.正则表达式例子
@Test public void testMatch(){
//前面可能会有一个负号
//-?
//有一个数字\d,
//\\d
//表示一个数值
//-?\\d+
assertTrue("-100".matches("-?\\d+"));
assertFalse("-1d".matches("-?\\d+"));
}
- split():按照匹配正则表达式的字符串处进行分割
/**
* 测试字符串splitAPI
*/
@Test public void testSplit(){
//|表示或,()表示分组
String[] split = "test200split-200is+200ok".split("(-|\\+)?\\d+");
assertEquals(4,split.length);
Arrays.stream(split).forEach(System.out::println);
assertEquals(new String[]{"test","split","is","ok"},split);
}
- replaceAll\replaceFirst:按照匹配正则表达式进行替换,第一次替换以及全部替换
/**
* 测试正则表达式替换
*/
@Test public void testReplace(){
String testMessage = "hello 100 ";
String hello = testMessage.replaceAll("-?\\d+", "hello");
System.out.println(hello);
}