5.1关于字符串的陷阱
1)JVM对字符串的处理
java对于字符串以及包装类,允许以直接量的方式来创建java对象。
对于这些直接量,jvm会使用一个字符串池来保存它们。字符串池中字符串对象不会被垃圾回收。
如果不是直接量,只要可以在编译时确定下来,结果也一样。
字符串的拼接,如果各部分都是直接量,那么在编译时就可以确定,所以jvm在编译时就计算出值,然后直接放入字符串池中,因此只创建了一个对象。
2)不可变的字符串
String类型,字符串拼接后引用指向新的字符串,原来的字符串失去引用,但是不会被回收,继续存在于字符串池中。这也就是java内存泄漏的原因之一。
StringBuffer线程安全,StringBuilder线程不安全。String没有线程安全和不安全两个版本,因为不可变类总是线程安全的。
3)字符串比较
==,equals(),compareTo()返回0则相等,即字符序列相同。
5.2表达式类型的陷阱
强类型语言的两个基本特征:所有变量必须先声明,然后才能使用;一旦某个变量的数据类型确定下来,那这个变量将永远只能接受该类型的数据。
1)表达式类型的自动提升
*所有byte型,short型和char型将被提升到int型
*整个算术表达式的数据类型自动提升到与表达式中最高等级操作数同样的类型(7个基本类型里double最高)
2)复合赋值运算符的陷阱
如:a=a+5和a+=5其实并不等价
实际上 a+=5等价于a=(a的类型)(a+5)。这就是复合赋值运算符中包含的隐式类型转换。
如果结果值的类型比该变量的类型要大,复合赋值运算符将会执行一次强制类型转换,这有可能导致高位截断,从而导致实际数据丢失的情形。
5.3输入法导致的陷阱(全角字符)
5.4注释的字符必须合法
java程序并没有完全忽略注释部分的内容。编译器会检测到一个非法字符:\u后面的四个字符必须是0-F字符。
5.5转义字符的陷阱
java提供了三种方式来表示字符:直接使用单引号;使用转义字符如'\n';使用Unicode转义字符如'\u0062';
1)慎用字符的unicode转义形式
举例:syso("abc\u000a".length())表面上将输出4,实际上编译报错:未结束的字符串字面值。
原因是java对Unicode转义字符不会进行任何特殊的处理,它只是简单地将Unicode转义字符替换成相应的字符。
对于Unicode而言,它相当于一个换行符,所以实际将会输出3。
2)终止行注释的转义字符
5.6泛型可能引起的错误
1)原始类型变量的赋值
*当程序把一个原始类型的变量赋给一个带泛型信息的变量时,总是可以通过编译,只是提示一些警告信息
*当程序视图访问带泛型声明的集合的集合元素时,编译器总是把集合元素当成泛型类型处理,而并不关心集合里集合元素的实际类型
*当程序视图访问带泛型声明的集合的集合元素时,jvm会遍历每个集合元素执行强制转型,如果不匹配,运行时将引发ClassCastExceptiion异常
2)原始类型带来的擦除
当把一个具有泛型信息的对象赋给另一个没有泛型信息的变量时,所有在尖括号之间的类型信息都将被抛弃。
3)创建泛型数组的陷阱
java虽然支持泛型,但是java不能创建泛型数组。
5.7正则表达式的陷阱
String提供的split(String regex)方法需要的参数是正则表达式
正则表达式中的点号(.)可以匹配任意字符,此时必须对点号进行转义(\\.)
String类支持正则表达式的方法:matches(String regex):判断该字符串是否匹配指定正则表达式
replaceAll(String regex,String replacement):将字符串中所有匹配指定正则表达式的子串替换成replace后返回
类似还有replaceFirst(String regex,String replacement),split(String regex)。*replace(...)不支持正则表达式。