作者:禅楼望月(http://www.VEVb.com/yaoyinglong/)
字符串的陷阱java程序创建对象常见的方式有:
此外,基本类型以及基本类的包装类、字符串还可以以直接量的方式来创建Java对象。如:
String str="hello world";Integer in=5;
对于Java程序中的字符直接量,JVM会使用一个字符串池来保护他们:当第一次使用某个字符串直接时,JVM会将它们放入字符串池进行缓存。在一般情况下,字符串缓冲池中字符串对象不会被垃圾回收,当程序再次需要使用该字符串时,无需重新创建一个新的字符串,而是直接让引用变量指向字符串池中已有的字符串:
String str1="hello";String str2="hello";System.out.PRintln(str1==str2);//-->true
虽然String是引用类型。但是由于str1和str2两个字符串的值都是直接量,它们都指向JVM的字符串池里的“hello”字符串,因此判断str1==str2时输出true。
注意:
也可以通过字符串连接表达式来创建字符串对象。如果这个字符串连接表达式的值可以在编译时确定下来,那么JVM会在编译时计算该字符串变量的值,并让它指向字符串池中对应的字符串。但是,如果字符串连接表达式中使用了变量或者调用了方法,那就只能到运行时才可确定该字符串连接表达式的值,也就无法在编译时确定该字符串变量的值,因此无法利用JVM的字符串池。
输出的结果为:
但是也有例外:字符串连接运算中的所有变量都可执行“宏替换”
如果字符串连接运算中的所有变量都可以执行“宏替换”,那么JVM一样可以在编译时就确定字符串连接表达式的值,一样会使字符串变量指向JVM字符串池中的对应字符串。
String str="I love Java";final String str1="I love ";String str2=str1+"Java";System.out.println(str==str2);//-->true
我们用反编译工具看看:
不可变的字符串String str = "I love Java";String str1 = "I love ";String str2 = "I love Java"; //这里执行了宏替换。编译时将str1用相应的值代替了。这是因为str1是final的而final变量的值在初始化之后不会在变化了。所以可以执行宏替换。System.out.println(str == str2);
String在创建之后是不可变的。只能改变字符串变量的引用,不能改变字符串对象。
String str="I love Java";str="I love C#";
第二行代码并没有将“I love Java”字符串对象变为“I love C#”字符串对象,只是改变str的指向,使其指向“I love C#”字符串对象,但是“I love Java”字符串对象仍然存在内存中。但是该“I love Java”字符串对象以后也许永远也不会被使用了,但是它并不会被垃圾回收器所回收,它将一直存在于字符串池中------>这也是Java内存泄漏的一个原因。字符串比较
使用“==”比较的是两个字符串所引用的是否是同一个字符串对象。但是如果要比较两个字符串序列是否相同则须用String的 equals 方法,如下是String类的equals方法源码:
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String) anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
表达式类型的陷阱还有String的compareTo系列方法。
表达式类型的自动提升。
当表达式中出现不同的数据类型是,表达式的结果以表达中最高等级操作数的类型为准。
复合赋值运算符的陷阱
诸如:+=、-=、*=……等复合赋值运算符都隐式包含了类型转换。
如上图所示,a=a+5出现了编译错误,是因为,5为int类型,所以会将整个表达式的值提升为int,而a又是short不能进行类型的自动转换,所以出现了编译错误。而a+=5没有出现编译错误的原因为,我们看看Java底层怎么处理的:
是不是很清楚了,底层默默的进行了隐式的类型转换。怎么转的呢?是以等号左边的类型作为等号后边表达式计算结果的类型。这下可危险了:万一等号右边表达式的计算结果的范围比等号左边的大怎么办,那只能悲催了,如:
所以我们在使用复合赋值运算符是要特别小心,特别是:
不要使用Thread类来创建线程。
不要调用run方法。
当同步一段代码块时,程序必须显式的为它指定同步监视器;对于非静态方法同步方法而言,该方法的同步监视器是this—即调用该方法的Java对象;对于静态同步方法而言,该方法的同步监视器不是this而是当前类本身。
public class Test implements Runnable{ static boolean staticFLag=true; public static synchronized void test0(){ for(int i=0; i<100; i++){ System.out.println("test0:"+Thread.currentThread().getName()+" "+i); } } public void test1(){ synchronized (this) { for(int i=0; i<100; i++){ System.out.println("test1:"+Thread.currentThread().getName()+" "+i); } } } @Override public void run() { if(staticFLag){ staticFLag=false; test0(); }else { staticFLag=true; test1(); } } public static void main(String[] args) throws InterruptedException { Runnable runnable=new Test(); new Thread(runnable).start(); new Thread(runnable).start(); }}
打印的结果为:
test0:Thread-0 0
test1:Thread-1 0
test0:Thread-0 1
test1:Thread-1 1
test0:Thread-0 2
test1:Thread-1 2
test0:Thread-0 3
test1:Thread-1 3
……
我们可以看到,由于两个方法 没有使用相同的同步监视器,所以它们可以同时并发执行,相互之间不会有影响。但是如果我们这样变一下:
public class Test implements Runnable{ static boolean staticFLag=true; public static synchronized void test0(){ for(int i=0; i<100; i++){ System.out.println("test0:"+Thread.currentThread().getName()+" "+i); } } public void test1(){ synchronized (Test.class) { for(int i=0; i<100; i++){ System.out.println("test1:"+Thread.currentThread().getName()+" "+i); } } } @Override public void run() { if(staticFLag){ staticFLag=false; test0(); }else { staticFLag=true; test1(); } } public static void main(String[] args) throws InterruptedException { Runnable runnable=new Test(); new Thread(runnable).start(); new Thread(runnable).start(); }}打印:
test0:Thread-0 0
test0:Thread-0 1
test0:Thread-0 2
……
test0:Thread-0 98
test0:Thread-0 99
test1:Thread-1 0
test1:Thread-1 1
……
test1:Thread-1 99
我们看到,当我们显示的指定test1方法内的代码块的同步监视器为Test.class时,由于test0方法和test1方法是用了相同的同步监视器,所以必须在test0方法释放了该同步监视器的锁定,test1才能使用。
本文链接:http://www.VEVb.com/yaoyinglong/p/java%E8%A1%A8%E8%BE%BE%E5%BC%8F%E4%B8%AD%E7%9A%84%E9%99%B7%E9%98%B1.html
新闻热点
疑难解答