下面说一些平时可能不太注意的知识点:
String 对象具有只读特性,所以指向它的任何引用都不可能改变它的值,因此不会其它的引用有什么影响。String的"+"与"+="是java中仅有的两年重载过的操作符,而java并不允许程序员重载任何操作符。
String的优化
由于String的不可改变的特性,每次改变都会生成一个新的String对象。那如果我们会使用String的重载运算符"+"拼接字符串时不是会生成大量的中间对象?java设计师一开始也是这样做的(这也是软件设计中的一个教训:除非你用代码将系统实现,并将它动起来,否则你无法真正了解它会有什么问题),然后他们发现其性能相当糟糕。
public class Concatenation {
public static void main(String[] args){
String mango="mango";
String s="abc"+mango+"def"+45;
System.out.println(s);
}
}
然后在class生成目录下使用javap -c Concatenation
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String mango
2: astore_1
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<
init>":()V
10: ldc #5 // String abc
12: invokevirtual #6 // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: aload_1
16: invokevirtual #6 // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: ldc #7 // String def
21: invokevirtual #6 // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: bipush 45
26: invokevirtual #8 // Method java/lang/StringBuilder.ap
pend:(I)Ljava/lang/StringBuilder;
29: invokevirtual #9 // Method java/lang/StringBuilder.to
String:()Ljava/lang/String;
32: astore_2
33: getstatic #10 // Field java/lang/System.out:Ljava/
io/PrintStream;
36: aload_2
37: invokevirtual #11 // Method java/io/PrintStream.printl
n:(Ljava/lang/String;)V
40: return
}
其中dup与invokevirtural语句相当于java虚拟机上的汇编语言。从上可以看出编译器自动引用了StringBuilder类,使得那句代码更高效。但是编译器为我们优化的程序有限。随时可以用javap来分析你的程序。
当你为一个类重写toString时如果字符串比较简单那么可以依赖编译器,否则使用StringBuilder更高效。
3.无意识的递归:当重写一个类的的toString的时候,如果你希望打印对象的内存地址不要使用this关键字,比如:
@Override
public String toString() {
return "*** address:"+this;
}
当调用this的时候又会调用这个方法的toString方法,所以在这里我们应该使用super.toString()的方法。
- String的matches方法可以进行正则表达式判断:
System.out.println("-3478".matches("-?\\d+"));
输出结果:
true
一般说来,比起功能有限的String类,我们更愿意使用Pattern和Matcher对象。
- Scanner定界符
在默认情况下,Scanner根据空白字符对输入进行分词,但是你可以用正则表达式指定自己所需的定界符。
public class ScannerDelimiter {
public static void main(String[] args) {
Scanner scanner = new Scanner("23,5,6,78,23");
scanner.useDelimiter("\\s*,\\s*");
while (scanner.hasNext()) {
System.out.println(scanner.nextInt());
}
}
}
运行结果:
23
5
6
78
23
在java引入正则表达式和Scanner类之前,分割字符串的唯一方法是使用StringTokenier来分词。不过,现在有了正则表式和Scanner,我们可以使用更加简单,更加简洁的方式来完成同样的工作了。基本上StringTokenizer已经弃用了。