做个string的测试咯。如下:
/**
* Created by lxk on 2016/8/19
*/
public class Test {
public static void main(String[] args) {
String a = "abc";
String b = "abc";
String c = new String("abc");
String d = "ab" + "c";
System.out.println("a == b" + a == b); //false
System.out.println("a == c" + a == c); //false
System.out.println("a == d" + a == d); //false
System.out.println("b == c" + b == c); //false
System.out.println("b == d" + b == d); //false
System.out.println("c == d" + c == d); //false
}
}
代码中警告的原因:
(1)
String c = new String("abc");
可以直接写成如下,上面那么写,就有些啰嗦,脱裤子放屁。哎呀,好不优雅的比喻。咦。。。
String c = "abc";
(2)
意思就是字符串比较不能用 == ,要用equals()方法
注意:
上面的代码和测试结果,都是完全错误的,一失足成千古恨。
要是你一眼就能看到我哪里错了,说明你很还是很牛x的。没一眼看出来,也不要紧。
下面再进行正确的测试。并说明上面错误的地方。
下面是正确的测试代码和测试结果。
测试代码:
package com.lxk.test;
/**
* 字符串测试Class
* <p>
* Created by lxk on 2017/2/8
*/
public class StringTest {
public static void main(String[] args) {
testStringPool();
}
/**
* 测试字符串常量池的问题
*/
private static void testStringPool() {
String a = "abc";//字面量形式
String b = "abc";//字面量形式
String c = new String("abc");//使用new标准的构造对象
/*
注意:这个虽然看起来似乎要在常量池新建三个字符串对象:ab,c,和拼接生成的abc
但是结果是内存中仅有生成的,前面的两个算是过程变量。这反编译得出来的结论,我没测试哟!
这样做实际上是一种优化,避免了创建多余的字符串对象,也没有发生字符串拼接问题
*/
String d = "ab" + "c";//字面量形式
System.out.println("a == b " + (a == b));//true
System.out.println("a == c " + (a == c));//false
System.out.println("a == d " + (a == d));//true
System.out.println("b == c " + (b == c));//false
System.out.println("b == d " + (b == d));//true
System.out.println("c == d " + (c == d));//false
System.out.println("-----------------");
System.out.println("abc" == ("ab" + "c"));//true
System.out.println("-----------------");
String e = c.intern();//将new出来的字符串对象加入字符串常量池
System.out.println(a == e);//true
}
}
对上面结果的解释说明:
首先上面的比较过程是直接拿 == 来比较的,没有用equal方法,那么用 == 来比较的话,比较的是地址。并不是值。
可以看到,a,b,d,三个字符串直接用 == 比较,比较出来的结果都是true。是相等的。
意思就是说这三个变量在空间上都是指向同一个内存地址。这就涉及到一个字符串常量池的问题啦。
但是和c比较的时候,却不相等,因为,c是new出来的,记得当时学习的时候,new都是在堆内存里面的,新开辟的空间。
所以,地址肯定就不相同啦。
我之所以错误了 ,是因为,我在打印结果的时候,没有注意到运算符的优先级问题。
我也没有看到我的打印结果和自己期望的结果是不同的,期望结果起码应该是:a == b false,这个形式的,但是我却没有注意到我当时代码的测试结果仅仅只有:false。
知道问题所在的话,就知道上面的代码怎么错了:前面的字符先是拼接起来,然后再和后面的变量字符串对比,结果当然是false啦。
所以,之前的测试结果一直都是false。
尴尬啦。
之所以有这篇文章,估计当时,看到这个问题,在网上搜索,答案五花八门。所以,我就自己亲自动手,搞了个测试。万万没想到。我还给搞错了。多谢下面那位仁兄的评论,我又回头 再看这个问题,就发现问题所在啦。
关于常量池的简单解释:(后面追加的内容)
Java中字符串对象创建有两种形式。
一种为字面量形式,如String str = "droid";,
另一种就是使用new这种标准的构造对象的方法,如String str = new String("droid");
这两种方式我们在代码编写时都经常使用,尤其是字面量的方式。然而这两种实现其实存在着一些性能和内存占用的差别。
这一切都是源于JVM为了减少字符串对象的重复创建,其维护了一个特殊的内存,这段内存被成为字符串常量池或者字符串字面量池。
工作原理
当代码中出现字面量形式创建字符串对象时,JVM首先会对这个字面量进行检查。
如果字符串常量池中存在相同内容的字符串对象的引用,则将这个引用返回。
否则新的字符串对象被创建,然后将这个引用放入字符串常量池,并返回该引用。
经过上面的这番解释,可以知道,新添加的那行代码的运行结果应该是:true。
以下是更新于(2017/06/23)
就算你仔细看了上面的测试代码,要是再这么一下,稍微加个final,修饰一下字符串。那么结果又有点不同啦。
具体运行结果,如下图。
对上述运行结果的解释:
这里面就是final变量和普通变量的区别了.
当final变量是基本数据类型以及String类型时,如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量使用。
也就是说在用到该final变量的地方,相当于直接访问的这个常量,不需要在运行时确定。
这种和C语言中的宏替换有点像。
因此在上面的一段代码中,由于变量bb被final修饰,因此会被当做编译器常量,所以在使用到bb的地方会直接将变量bb替换为它的值。
而对于变量dd的访问却需要在运行时通过链接来进行。
想必其中的区别大家应该明白了.
不过要注意,只有在编译期间能确切知道final变量值的情况下.编译器才会进行这样的优化.
为什么有这次更新
因为在看到final的用法的时候,又出现了字符串比较,又涉及到了字符串常量池的问题,我就干脆放一起好啦。