黑马程序员技术交流社区
标题:
初学String后的三个问题
[打印本页]
作者:
创造命运
时间:
2014-4-1 20:30
标题:
初学String后的三个问题
一、对于“字符串对象一旦创建不可再改变”这句话的理解。
是字符串对象不可再改变,但是指向该字符串对象的引用去是可以改变的。如下代码:
public class StringTest{
public static void main(String[] args){
//s1是一个引用,s1指向"abc"对象
String s1 = "abc";
//可以让s1重新指向吗?s1是局部变量,s1前边没有final,所以s1是可以重新指向的
//但是"abc"与"def"对象本身却不可变
s1 = "def";
}
}
二、关于“字符串常量池”
在看视频时,有这样两句话:(1).为了提升字符串的访问效率,在程序中使用了“缓存”技术。所以
在java中所有使用双引号括起来的字符串都会在字符串常量池中创建一份,字符串常量池在方法区
中被存储;(2).在程序执行过程中,如果程序用到某个字符串,例如"abc",那么程序会在字符串常
量池中去搜索该字符串,如果没有找到则在字符串常量池中新建一个"abc"字符串,如果找到就直接
拿过来使用。
如下代码:
public class StringTest{
public static void main(String[] args){
String s1 = "Hello"; //在字符串常量池中新建一个"Hello"字符串对象,该对象不可变
String s2 = "Hello"; //从字符串常量池中直接拿来使用
}
}
问题来了:
1.字符串常量池是在JVM中一直“静态”存在的吗?还是在每一段程序运行时才有一个字符串常量池?
2.字符串常量池的最初状态是空的吗?也就是说最初字符串常量池没有字符常量呢,还是在程序运行之前已经
有一些字符串常量保存在字符串常量池中了?
3.假设在字符串常量池没有"Hello"字符串常量,在运行String s1 = "Hello";后便有了。那么在该程序结束后,再次运行时,字符串常量池里还有"Hello"该字符串常量吗?
4.我自己的理解是:在StringTest这个程序运行时,CPU会给这个程序分配一段内存(栈区,堆区,方法区等),而字符串常量池就存储在方法区。每一个程序运行时都会有一个字符串常量池,但这时字符串常量池里还没有任何字符串常量。在运行String s1 = "Hello";后便有了"Hello"字符串常量。程序结束后,字符串常量池也就不存在了。不知道我的理解是否正确。
5.使用字符串常量池有一个好处,就是在有多个引用都指向“Hello”字符串常量时,可以直接拿来使用,不用再另外给这些引用创建多个“Hello”字符串对象。这样节约了内存的使用。感觉这样子的话,使用String s1 = "Hello";完全可以实现String s1 = new String("Hello");的功能。既然Java有String s1 = new String("Hello");这样的语法,那么它肯定有什么作用是用String s1 = "Hello";实现不了的。那么是什么呢?
三、关于比较两个字符串是否相等
public class StringTest{
public static void main(String[] args){
String s1 = "Hello"; //在字符串常量池中新建一个"Hello"字符串对象,该对象不可变
string s2 = "Hello"; //从字符串常量池中直接拿来使用
//s1与s2是两个不同的引用,却是指向同一个字符串对象"Hello"
System.out.println(s1 == s2); //结果是true
//但是正确的做法却是比较两个字符串是否相等,不能使用"=="
//以下两个引用s3与s4所指向的对象的内容虽然都是"Hello",可是却是指向两个不同的对象??
String s3 = new String("Hello");
String s4 = new String("Hello");
//以上两个Hello是两个不同的对象,他们的地址不一样。
System.out.println(s3 == s4); //结果是false
//比较两个字符串的内容是否相等,必须使用String类提供的equals方法
System.out.println(s3.equals(s4)); //结果是true
}
}
作者:
linweiwen
时间:
2014-4-1 21:23
本帖最后由 linweiwen 于 2014-4-1 23:00 编辑
问题一:
你的理解没错。s1是String类创建的对象引用变量,它指向"abc"字符串。而String是final的,不能被重新赋值。
问题二:
首先,字符串常量池存在于方法区,方法区其实也是在内存中开辟了一个空间,用来放类被加载后的信息。
1.字符串常量池是在JVM中一直“静态”存在的吗?还是在每一段程序运行时才有一个字符串常量池?
首先,常量池的信息是在编译期被确定的,并被保存在编译后的.class文件(字符串常量池是常量池中的一种)。
也就是你运行javac命令后,.class文件就有了你程序中字符串常量池的信息。
因此,不能说是在JVM中一直静态存在的。
在运行java命令启动JVM前,该类的字符串常量池信息已经确定了,只是JVM启动后常量池信息被加载到内存而已。
也不是每段程序运行有一个,而是每个类有相应的常量池。JVM启动后要去维护这些被加载类的常量池。
2.字符串常量池的最初状态是空的吗?也就是说最初字符串常量池没有字符常量呢,
还是在程序运行之前已经有一些字符串常量保存在字符串常量池中了?
既然常量池是加载对应类的信息存储区,那么它的最初状态,就是根据你的代码怎么写就怎么定,你没写字符串,当然就没有东西了。
3.假设在字符串常量池没有"Hello"字符串常量,在运行String s1 = "Hello";后便有了。
那么在该程序结束后,再次运行时,字符串常量池里还有"Hello"该字符串常量吗?
这个问题,看了上面的回到就应该知道,
在编译程序后,就已经保存有Hello字符串的信息,
运行的时候,只是加载到常量池的内存空间。
程序结束后,内存中的
"Hello"
字符串是没有了,.class文件还保留有信息。
再次运行时,有根据这个信息创建字符串常量池,里面就有了"Hello"字符串。
4.我自己的理解是:在StringTest这个程序运行时,CPU会给这个程序分配一段内存(栈区,堆区,方法区等),而字符串常量池就存储在方法区。
每一个程序运行时都会有一个字符串常量池,但这时字符串常量池里还没有任何字符串常量。
在运行String s1 = "Hello";后便有了"Hello"字符串常量。程序结束后,字符串常量池也就不存在了。
不知道我的理解是否正确。
基本看上面,补充一下:方法区(内含常量池)在JVM启动时被创建,每个类有它自己的常量池,
它在编译完成后里面就保存有你代码中字符串常量的信息,
运行时s1便指向"Hello",而不是运行时才有,是s1被创建后指向已经被加载到常量池中的"Hello"。
5.使用字符串常量池有一个好处,就是在有多个引用都指向“Hello”字符串常量时,可以直接拿来使用,不用再另外给这些引用创建多个“Hello”字符串对象。这样节约了内存的使用。感觉这样子的话,使用String s1 = "Hello";完全可以实现String s1 = new String("Hello");的功能。既然Java有String s1 = new String("Hello");这样的语法,那么它肯定有什么作用是用String s1 = "Hello";实现不了的。那么是什么呢?
这个问题实在回答不了,个人感觉没这么复杂,两种方式其实要根据实际需要决定用那个,
看你第三个问题就有这种情况了。
三、关于比较两个字符串是否相等
s3、s4的地址不一样,所以不能用"==",但其实它们都是指向同一个“Hello”,内容一样。
Java就提供了equals这个方法解决了。
作者:
创造命运
时间:
2014-4-2 05:01
好的,之前我还不是很懂得怎么发帖。以后我会做得越来越好的。谢谢!
作者:
相见欢
时间:
2014-4-2 13:16
好难理解。。。
作者:
创造命运
时间:
2014-4-2 20:44
linweiwen 发表于 2014-4-1 21:23
问题一:
你的理解没错。s1是String类创建的对象引用变量,它指向"abc"字符串。而String是final的,不能被 ...
刚下班回来洗完澡,看到了这个。个人觉得我已经基本理解了一些。非常感谢你的解答。
作者:
探花
时间:
2014-4-2 20:47
分析的很好,学习了
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/)
黑马程序员IT技术论坛 X3.2