表达式陷阱(字符串,表达式类型,输入法,注释字符,转义字符,正则表达式)


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











  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值