黑马程序员技术交流社区

标题: final与字符串结合的问题 [打印本页]

作者: dev    时间: 2012-7-3 18:44
标题: final与字符串结合的问题
本帖最后由 翁游龙 于 2012-7-4 16:04 编辑

第一次见到这样奇怪的问题,以下有两个类StringJoinTest和StringJoinTest02,
程序如下:


类StringJoinTest:


public class StringJoinTest{
        public static void main(String[] args){
                String s1 = "HeimaJava";
                String s2 = "Heima" + "Java";
                System.out.println(s1 == s2); //true


                //定义两个字符串
                String str1 = "Heima";
                String str2 = "Java";
                //将str1和str2进行连接运算
                String s3 = str1 + str2;
                System.out.println(s1 == s3);  //false
        }
}


类StringJoinTest02:


public class StringJoinTest02{
        public static void main(String[] args){
                String s1 = "HeimaJava";
                String s2 = "Heima" + "Java";
                System.out.println(s1 == s2); //true


                //定义两个字符串
                final String str1 = "Heima";
                final String str2 = "Java";
                //将str1和str2进行连接运算
                String s3 = str1 + str2;
                System.out.println(s1 == s3);  //true
        }
}


问题:两个类中同样是比较s1和s3的地址池,为什么使用了final修饰后的字符串
判断打印的是结果为true呢?



作者: 孙飞    时间: 2012-7-3 20:25
因为第一个类中的str1和str2是一个引用指向字符串常量池里,它是在编译时期就这么做的,但是String s3 = str1 + str2;中的s3是要在运行时才能知道结果,所以在s3得到结果之前str1和str2什么时候赋值是不知道的,就相当于二个变量,所以str1+str2是在堆内存中创建的,那么s3是不可能把向字符串常量池的,所以s1==s2是false。第二个类中str1和str2被final修饰就是说,他们不可再变,再说因为"Heima"和"java"都是常量,所以str1和str2的值在编译时期就确定了,并存在于字符串常量 池里,在常量池里虚拟机会自动找到它们连接后的值,并把引用指向s3,所以s1和s3引用指向的地址是相同的,所以s1==s3是true
作者: 韦念欣    时间: 2012-7-3 21:14
feigecal 发表于 2012-7-3 20:25
因为第一个类中的str1和str2是一个引用指向字符串常量池里,它是在编译时期就这么做的,但是String s3 = st ...

说的很不错!学习了!
作者: 孙飞    时间: 2012-7-3 21:17
韦念欣 发表于 2012-7-3 21:14
说的很不错!学习了!

一起努力吧,呵呵
作者: 周朋飞    时间: 2012-7-3 21:31
其实你i想理解的话我给你讲个final的作用你就知道了,首先你前一段String s1 = "HeimaJava";
                 String s2 = "Heima" + "Java";
                 System.out.println(s1 == s2); //true 这个打印为true的原因很明显,因为heima 和java都是字符串常量池当中的数据,你这样写的效果跟直接这样写String strs2="HeimaJava"是一样的效果,所以打印结果为true,但是第二个因为+运算符会在内存重新创建一个对象,然后将引用赋给s2所以两个的地址不相同 所以打印为false
final的作用指的是引用变量不可变,而不是引用变量的内容不可变,就好像你定义一个Stringbuffer对象为final类型 final StringBuffer str = new StringBuffer("123"); 执行以下语句会报错  str = new StringBuffer(""); 但是a.append("aff");就不会报错,这就说明这个final修饰变量只是引用不能变,但这个题目有一点不一样,String 类型本身就是不可变的,就好像你定义一个变量String  s1 = "abc"这里面s1指向的内容是不会改变的 你只能改变s1的引用地址,但是这道题你将String定义成了fianl类型 两者都不能变,所以相加就是两个常量再相加,所以会返回true

作者: 汤密奕    时间: 2012-7-3 23:25
我的理解是这样的,不知道对不对:
查看String文档有这么一句:Java 语言提供对字符串串联符号("+")以及将其他对象转换为字符串的特殊支持。字符串串联是通过 StringBuilder(或 StringBuffer)类及其 append 方法实现的。字符串转换是通过 toString 方法实现的,该方法由 Object 类定义,并可被 Java 中的所有类继承。
所以很好理解第一个类中的s3实际是在堆内存中开辟了新的对象,而s1的引用地址是指向字符串常量池的,结果为(s1==s3)false

第二个类的str1和str2被final修饰",在常量池就会开辟两块空间存放“Heima”和“Java”,那么串联时因为被final修饰了(引用地址不可改变),所以不会在堆内存中开辟新的空间,而是在常量池中查找,找到“HeimaJava”后把常量池存“HeimaJava”的地址(这个地址指向s1)仍给s3,结果(s1==s3)true
作者: dev    时间: 2012-7-4 16:03
feigecal 发表于 2012-7-3 20:25
因为第一个类中的str1和str2是一个引用指向字符串常量池里,它是在编译时期就这么做的,但是String s3 = st ...

嗯。我明白了。谢谢你。




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2