黑马程序员技术交流社区
标题:
String主要特点归纳
[打印本页]
作者:
黄成超
时间:
2013-2-8 10:28
标题:
String主要特点归纳
本帖最后由 黄成超 于 2013-2-8 10:30 编辑
String主要特点归纳:
(1)不可变String
String对象是不可变的,每一次修改String的值,都会创建一个全新的String,以包含修改后的内容,比如:
public class Test1 {
public static void main(String[] args) {
String q = "howdy";
System.out.println(q);//howdy
String qq = upcase(q);
System.out.println(qq);//HOWDY,也许这里的返回值不能说明q是否改变,因为返回值确实也可以先创建一个对象,用于保存临时String对象
System.out.println(q);//howdy,但是通过打印q的值,就你能了解到q的值确实并未改变
}
/**
* 写一个自定义的upcase方法,用于返回s转化为大写的结果
*
*/
public static String upcase(String s)
{
System.out.println(s.hashCode());
return s.toUpperCase();
}
复制代码
关于字符串的不可改变,jdk开发文档也有说明,同时因为String对象不可改变,所以可以共享,如:
String str = "abc";
等效于:
char[] data = {'a','b','c'};
String str = new String(data);
需要强调的是,字符串是常量,他们的值在创建之后不可变(这个jdk开发文档有说明),并不是说创建多个String对象,内存中只有一个,
不可改变,指的是原来的String的值不能改变。
因此:
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(str1==str2); //false
结果显然是false。
而字符串是常量,所以
String str3 = "abc";
String str4 = "abc";
System.out.println(str3==str4);//true
常量存放在常量池中,两个"abc"存放位置是相同的。这点要区别。
(2)String、StringBuffer、StringBuilder比较
String是java中的常用字符串对象,而StringBuffer,StringBuilder则是类似于String的字符串缓冲区。
String对象创建后不能修改,上面已有说明;
StringBuffer不能直接修改,但可以通过某些方法改变内容与长度,可安全的用于多线程
StringBuilder为可变字符序列,StringBuilder则用于多线程不安全,但相对更高效,所以除非在多线程,否则建议使用StringBuilder
StringBuffer,
(关于String,StringBuffer,StringBuilder以及类似单个类或对象方法的比较,建议查看jdk开发文档!)
(3)String的"+"、"+="
原来一直不是很清楚下面的到底创建了多少对象:
String s = "abc" + "addf" +"def" +47;
相信看完下面这个例题分析,了解String的底层创建过程,就能够清楚知道:
class StringTest
{
public static void main(String[] args)
{
String mango = "mango";
String s = "abc" + mango +"def" +47;
System.out.println(s);
}
}
复制代码
这段代码是怎么工作的呢?通过jdk自带的工具javap反编译这段代码,如下:
javap -c -StringTest
注意,反编译前一定要先编译哦!
反编译图片.png
(17.33 KB, 下载次数: 150)
下载附件
2013-2-8 10:20 上传
通过反编译的图片可以知道,在运行的过程中编译器创建了一个StringBuilder对象,用以构造最终的String(使用StringBuilder,是因为StringBuilder更高效),并
为每个字符串调用一次append()方法,首先是将"abc"通过append方法放在StringBuilder中,然后使用append()用于包含"abc"与mango连接后的字符串
,然后再次调用append()方法,把def加入StringBuilder中,以此类推,总共4次。最后用toString方法,生成结果String对象(这个在jdk中也有说明)。
对于一些容易产生误解的知识点,可能还不够完整,希望大家都补充一下。。。。
参考《java编程思想》及jdk1.6开发文档以及自己总结及代码验证
作者:
刘凯
时间:
2013-2-8 11:18
受教 顶一个
作者:
王昕
时间:
2013-2-8 14:26
常量池是栈还是堆呢?
作者:
黄成超
时间:
2013-2-8 15:46
王昕 发表于 2013-2-8 14:26
常量池是栈还是堆呢?
常量池在java用于保存在编译期已确定的,已编译的class文件中的一份数据。它包括了关于类,方法,接口等中的常量,也包括字符串常量,如String s = "java"这种申明方式;当然也可扩充,执行器产生的常量也会放入常量池,故认为常量池是JVM的一块特殊的内存空间。关于常量池,比较复杂,有兴趣是话,可以自己了解一下。
作者:
pzfpang449
时间:
2013-2-8 17:18
看了楼主的总结,我也加上一些关于String其他的内容
1. 首先String不属于8种基本数据类型,String是一个对象。
因为对象的默认值是null,所以String的默认值也是null;但它又是一种特殊的对象,有其它对象没有的一些特性。
2. new String()和new String(“”)都是申明一个新的空字符串,是空串不是null;
3. String str=”kvill”;
String str=new String (“kvill”);的区别:
Ps:
在这里,我们不谈堆,也不谈栈,只先简单引入常量池这个简单的概念。
常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。
看例1:
String s0=”kvill”;
String s1=”kvill”;
String s2=”kv” + “ill”;
System.out.println( s0==s1 );
System.out.println( s0==s2 );
结果为:
true
true
首先,我们要知结果为道Java会确保一个字符串常量只有一个拷贝。
因为例子中的s0和s1中的”kvill”都是字符串常量,它们在编译期就被确定了,所以s0==s1为true;而”kv”和”ill”也都是字符串常量,
当一个字符串由多个字符串常量连接而成时,它自己肯定也是字符串常量,所以s2也同样在编译期就被解析为一个字符串常量,
所以s2也是常量池中”kvill”的一个引用。
所以我们得出s0==s1==s2;
用new String() 创建的字符串不是常量,不能在编译期就确定,所以new String() 创建的字符串不放入常量池中,它们有自己的地址空间。
看例2:
String s0=”kvill”;
String s1=new String(”kvill”);
String s2=”kv” + new String(“ill”);
System.out.println( s0==s1 );
System.out.println( s0==s2 );
System.out.println( s1==s2 );
结果为:
false
false
false
例2中s0还是常量池中”kvill”的应用,s1因为无法在编译期确定,所以是运行时创建的新对象”kvill”的引用,
s2因为有后半部分new String(“ill”)所以也无法在编译期确定,所以也是一个新创建对象”kvill”的应用;明白了这些也就知道为何得出此结果了。
希望对楼主有所帮助
作者:
黄玉昆
时间:
2013-2-8 19:02
不错的总结,我也会好好总结总结关于String这个特殊的类型的。
作者:
朱玉玺
时间:
2013-2-16 12:53
王昕 发表于 2013-2-8 14:26
常量池是栈还是堆呢?
每个类都用自己的常量池,并且存在于方法区中;而方法区呢,根据jvm规范中说是堆的逻辑分区,只不过垃圾回收期不对它进行管理~
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/)
黑马程序员IT技术论坛 X3.2