1. 表达式中的陷阱
1.1 关于字符串的陷阱
1. 在创建一个String s = new String("java");的时候,JVM会到常量池中去检查看是否有一个"java"对象了,如果没有则在常量池中创建一个。之后会在堆内存中分配了一个空间,放置这个new出来的String对象,形式如下:java.lang.String@123b, 常量池是在编译期生成的,而new一个对象是在运行时进行的,有一个先后顺序。
2. Java程序中创建对象的常规方式有如下四种:
ü 通过new调用构造器创建java对象。
ü 通过Class对象的newInstance()方法调用构造器创建java对象。
ü 通过java的反序列化机制从IO流中恢复java对象。
ü 通过java对象提供的clone()方法复制一个新的java对象。
3. 对于java程序中的字符直接量,JVM会使用一个字符串池来保存它们:当第一次使用某个字符串直接量时,JVM会将它放入字符串池进行缓存。在一般情况下,字符串池中字符串对象不会被垃圾回收,当程序再次需要使用字符串时,无需重新创建一个新的字符串,而是直接让引用变量指向字符串池中已有的字符串。
4. 关于以直接量的方式来创建java对象的一些问题:
String str1 = "liyafang"; String str2 = "liyafang"; System.out.println(str1==str2);//true //虽然str4的值不是直接量,但因为它的值可以在编译时确定, //所以str4也会直接引用字符串池中对应的字符串。 String str3 = "liyafang2"; String str4 = "li"+"yafang"+2;//只创建了一个字符串对象 System.out.println(str3==str4);//true //str6的值包含了方法调用,不能再编译时确定,所以无法利用JVM的字符串池 String str5 = "liyafang8"; String str6 = "liyafang"+"liyafang".length(); System.out.println(str5==str6);//false //str8的值使用了变量,不能再编译时确定,所以无法利用JVM的字符串池 int len = 1; String str7 = "liyafang1"; String str8 = "liyafang"+len; System.out.println(str7==str8);//false //字符串连接运算中的所有变量都可执行"宏替换", //那么JVM一样可以在编译时就确定字符串连接表达式的值 final String temp = "li"; String str9 = "liyafang"; String str10 = temp+"yafang"; System.out.println(str9==str10);//true final int leng = 8; String str11 = "liyafang8"; String str12 = "liyafang"+leng; System.out.println(str11==str12);//true
5. 不可变的字符串:
String str = "I"; System.out.println(System.identityHashCode(str)); //该静态方法用于获取某个对象唯一的hashCode值,这个方法的返回值与该类是否重写 //hashCode方法无关。只有当两个对象相同时,它们的identityHashCode值才会相等。 str = str+" love "; System.out.println(System.identityHashCode(str)); str = str+"java!"; System.out.println(System.identityHashCode(str));
运行结果:31843011
25860399
5184781
看起来str对应的字符串序列可以发生改变。但是要记住,str只是一个引用类型变量,它并不是真正的String对象,只是指向String对象而已,真正发生改变的是str变量本身,它改变了指向,指向了一个新的String对象。以上程序总共会创建3个字符串对象,“I”,“I love ”,“I love java!”,其中前两个将一直存在于字符串常量池中-这就是java内存泄漏的原因之一。
6.优先考虑使用StringBuilder:
其与StringBuffer唯一的区别在于StringBuffer是线程安全的,也就说StringBuffer类里绝大部分方法都增加了synchronized修饰符。在单线程环境下,优先考虑StringBuilder,因其效率高。
7.String类实现了Comparable接口:
可以通过compareTo()方法比较两个字符串之间的大小,当两个字符串所包含的字符序列相等时,返回值为0。比较规则:先将两个字符串左对齐,然后从左向右依次比较两个字符串所包含的每个字符,包含较大字符的字符串的值比较大。
1.2 表达式类型的陷阱
1.表达式类型的自动提升:
byte->short;
short,char->int->long->float->double
eg: short a = 5; a = a -2;此时将会发生错误,因为int赋给short类型的变量将发生错误。
2.复合赋值运算符的陷阱:
short a = 5; a -=2; 没有错误,根据java语言规范,复合赋值运算符包含了一个隐式的类型转换,所以a-=2等价于a = (a的类型)(a-2);与此类似的有+=,*=,/=,%=,<<=,>>=,>>>=,&=,^=和|=等。
再看下面这个例子:
short st = 15;
st += 90000;
System.out.println(st);//输出结果不是900015,而是24479
因为short类型的变量只能接受-32768~32767之间的整数,因此上面程序会将高位“截断”。
复合赋值运算符简单,方便,而且具有性能上的优势,但复合赋值运算符可能有一定的风险:它潜在的隐式类型转换可能不知不觉中导致计算结果的高位被截断,为了避免潜在的危险,如下情况需要额外注意:
将复合赋值运算符应用于byte,short,char等类型的变量;应用于int类型的变量,而表达式右边是long,float,double类的值;应用于float类型的变量,而表达式右侧是double类型的值。
当+用于字符串连接符时,则+=的变量只能是String类型的,而不可是String的父类型(如Object或CharSequence)。
1.3 输入法导致的陷阱
基本上如果在编译java程序时提示形如:“非法字符:\xxxxx”的错误提示,那么就可断定该java程序中包含“全角字符”,逐个删除他们即可。
1.4 注释的字符必须合法
Java要求注释中所有的字符必须是合法的字符。Java程序允许直接使用\uXXXX的形式代表字符,它要求\u后面的4个字符必须是0~F字符,如果注释中出现“\unit5”,这不符合java对Unicode转义字符的要求。
1.5 转义字符的陷阱
Java程序提供三种方式表示字符:
直接使用单引号括起来的字符值,如’a’;
使用转义字符,如’\n’;
使用Unicode转义字符,如’\u0062’。
不过转移字符得慎用,如下:
System.out.println(“abc\u000a”.length());这句话无法通过编译,提示:未结束的字符串字面值。引起这个原因是java对Unicode转移字符不会进行任何特殊的处理,只是简单的将其替换成相应的字符。对于\u000a而言,相当于一个换行符(\n),因此上边的程序相当于:
System.out.println(“abc
”.length());
这样就不难理解出现的编译错误了。
类似的情况如下边的注释将通不过编译:
//\u000a代表一个换行符。
因为对于java编译器来说相当于
//
代表一个换行符。
1.5 正则表达式的陷阱
以下程序不会输出任何东西,想想为什么?
String str = "www.baidu.com";
String[] str1 = str.split(".");
for(String s:str1){
System.out.println(s);
}
从JDK1.4开始,java的String类提供了split()方法进行字符串的分割。JDK1.0原来提供的StringTokenizer基本上已经成为“历史遗物”了。
对于上面的程序,需要注意两点:
1.String提供的split(String regex)方法需要的参数是正则表达式;
2.正则表达式中的点号(“.”)可以匹配任意字符。
所以上面程序实际上不是以“.”作为分隔符,而是以任意字符作为分隔符。为了实现以“.”作为分隔符的目的,必须对“.”号进行转义,将上面的程序改为
String[] str1 = str.split("\\.");即可得到想要的结果。
从JDK1.4开始,Java加入了对正则表达式的支持,String类也增加了一些方法用于支持正则表达式,具体方法如下:
matches(String regex):判断该字符串是否匹配指定正则表达式。
String replaceAll(String regex,Stringreplacement):将字符串中所有匹配指定正则表达式的子串替换成replacement后返回。
String replaceFirst(String regex,Stringreplacement):将字符串中第一个匹配正则表达式的子串替换成replacement后返回。
String[] split(String regex):以regex正则表达式匹配的子串作为分割符来分割该字符串。
replace(CharSequence target,CharSequencereplacement):将字符串中所有target子串替换成replacement后返回。这个普通的replace()方法不支持正则表达式,开发中必须区别对待replaceAll和replace两个方法。
例如:
String str = "www.baidu.com";
String str1 = str.replace(".", "\\");
String str2 = str.replaceAll("\\.", "\\\\");
System.out.println(str1);//输出结果:www\baidu\com
System.out.println(str2);//输出结果:www\baidu\com
表达式陷阱(字符串,表达式类型,输入法,注释字符,转义字符,正则表达式)
最新推荐文章于 2024-04-12 10:06:29 发布