java作为一门面向对象的编程语言,在里面运用了类的概念,程序的处理又有编译阶段和运行阶段之分,这导致很多刚开始学习java的人都不够理解,从而犯下很多错误。但今天说的这些,不只是初学者,很多有一点工作经验但不丰富的,也未必知道,平时几乎用不上,但在面试时,却最容易被用来做考题。下面就是两个典型的例子。
先看下面的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
publicstaticvoidmain(String[]args){
Stringa1="123456789";
Stringa2="12345";
Stringa3="6789";
Stringa4=a2+a3;
Stringa5="12345"+a3;
Stringa6="123456789";
Stringa7="12345"+a3;
Stringa8=a2+a3;
System.out.println(a1==a4);//true or false?
System.out.println(a1==a5);//true or false?
System.out.println(a1==a6);//true or false?
System.out.println(a4==a5);//true or false?
System.out.println(a7==a5);//true or false?
System.out.println(a8==a4);//true or false?
}
在看文章时,请先不要看答案,你能很确定的说出上面的所有结果吗?
正确的答案是:
false
false
true
false
false
false
对于没答对,或者靠蒙对的人,也许你会在想,这是为什么呢?
答案就在于,Java为了节约内存,防止多余的内存开销,所以内存中已经存在的,它不会再重新创建,而是直接引用。比如:String a1 = “123456789”,会生成一个String类型的变量a1,再去看内存中存不存在”123456789″这个字符串常量,如果有,则直接引用,没有,则创建,可惜的是,并没有找到,所以会创建一个字符串常量 “123456789”,同时让a1指向这个字符串常量。a2,a3也是如此,但对于a4和a5,就不一样了。因为a2,a3已经存在,再对于已有的部分,他们只会引用,而不会重新再创建。只有没有的部分才创建。所以对于a5,它会自己创建一个”12345″,再引用一个a3,而a4,直接引用a2和a3。故a1不等于a4和a5,a4也不等于a5。对于a6,因为”123456789″已经存在,则会直接对它进行引用,所以a1和a6指向的是同一个字符串常量,它们相等。
但如果换成下面这样,结果又如何呢?
1
2
3
4
5
6
System.out.println(a1.equals(a4));//true or false?
System.out.println(a1.equals(a5));//true or false?
System.out.println(a1.equals(a6));//true or false?
System.out.println(a4.equals(a5));//true or false?
System.out.println(a7.equals(a5));//true or false?
System.out.println(a8.equals(a4));//true or false?
答案是结果全部为true。因为字符串的equals()方法在比较的时候,比较的是字面值。而上面比较的所有变量,字面值都为123456789,结果自然都为true了。
可如果换成下面这样,结果你又能否都明确的说出来呢?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Stringa1="123456789";
finalStringa2="12345";
finalStringa3="6789";
finalStringa4=a2+a3;
finalStringa5="12345"+a3;
Stringa6="123456789";
finalStringa7="12345"+a3;
finalStringa8=a2+a3;
System.out.println(a1==a4);//true or false?
System.out.println(a1==a5);//true or false?
System.out.println(a1==a6);//true or false?
System.out.println(a4==a5);//true or false?
System.out.println(a7==a5);//true or false?
System.out.println(a8==a4);//true or false?
答案也是全部为true。这里面就是final变量和普通变量的区别了,当final变量是基本数据类型以及String类型时,如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量使用。也就是说在用到该final变量的地方,相当于直接访问这个确切的常量,不需要在运行时确定。因此在上面的一段代码中,由于变量a2,a3,a4,a5,a7,a8被final修饰,因此会被当做编译器常量,所以在使用到b的地方会直接将它们 替换为它们的值。所以结果为true。
不过要注意,只有在编译期间能确切知道final变量值的情况下,编译器才会进行这样的优化,对于不知道的,就不会了。比如下面的这段代码就不会进行优化:
1
2
3
4
5
6
7
8
9
10
11
12
13
publicclassTest{
publicstaticvoidmain(String[]args){
Stringa="hello2";
finalStringb=getHello();
Stringc=b+2;
System.out.println((a==c));
}
publicstaticStringgetHello(){
return"hello";
}
}
好了,说完了上面的,我们再来看下面的这个例子。
1
2
3
4
5
publicstaticvoidmain(String[]args){
Integera=10;
Integerb=10;
System.out.println(a==b);//true or false?
}
你知道答案是什么吗,我相信大家都会很明确的回答true。嗯,不错,就是true。接着再来看下面:
1
2
3
4
5
publicstaticvoidmain(String[]args){
Integera=128;
Integerb=128;
System.out.println(a==b);//true or false?
}
也许你会好不犹豫的回答,这不跟上面一样吗,同样是true啊。可也有的人会想,既然我文章标题都说了java的陷阱,怕是没那么简单吧。不管怎样,答案就是false,你是不是很吃惊啊,怎么会这样!
答案在于,对于java里面的封装数据类型,它的范围是有限制的,对于Integer,它的范围就是-128-127,任何大于127或小于-128的两个Integer相比较,都为false。这下你明白了吧。对于其它的几个,我也没一一测试,就留给读者了,毕竟写代码这东西,要多动手,如果看到这里,你也不亲自动手试试的话,未免太懒了。