Java问题解读系列之String相关---String、StringBuffer、StringBuilder的区别

今天的题目是String、StringBuffer和StringBuilder的区别:

首先还是去官方的API看看对这三种类型的介绍吧,Go......

一、继承类和实现接口情况

1、String类

public final class String
extends Object
implements Serializable, Comparable<String>, CharSequence

2、StringBuffer类

public final class StringBuffer
extend Object
implements Serializable, CharSequence

3、StringBuilder类

public final class StringBuilder
extends Object
implements Serializable, CharSequence

由以上三段代码可以看出:

它们都继承自Object类,并且实现了Serializable, CharSequence这两个接口,而String比另外两个多实现了一个用来比较字符串是否相等的接口Comparable

二、官方解释

1、String类

2、StringBuffer类

3、StringBuilder类

其实从以上官方解释中,我只能理解以下几点:

(1)String定义的字符串是不能被改变的;而StringBuffer和StringBuilder定义的字符串本身不能被改变,但可以通过调用一些方法来改变(其实不太理解)

(2)String和StringBuffer操作字符串是线程安全的;而StringBuilder线程不安全;

(3)它们的效率从高到底依次为StringBuilder>StringBuffer>String

但是还有一个根本的问题需要弄清楚-----不能被改变到底是什么意思?比如下面这段代码:

String str1 = "Hello";
str1+=" World";
System.out.println(str1);

答案是:Hello World

运行完上面这段代码,我就很疑惑,不是说不能被改变吗,为什么改变之后却没有报错?经过一番调研之后发现,这里就存在一个java内存分配机制的问题,这里面所说的改变指的是对同一个字符串内容的改变,虽然上面的代码看上去是改变了str的内容,其实却不然,理解了内存分配就知道是怎么回事了。于是我又去了解了一下java内存分配机制问题:

 

Java栈、堆、常量池、方法区

1、  栈。栈中存放的是一些基本类型的变量、变量值和一些引用变量。当在一段代码中定义一个变量时,java就在栈中为这个变量分配内存空间,在该变量退出作用域后会自动释放变量所占的内存空间供其他变量使用。

2、  堆。堆内存放一些由new关键字创建的对象和数组。堆中分配的内存,由java虚拟机的垃圾回收机制管理。我们使用new创建变量和对象时,都会给他们起一个名字,而这些名字是存放在栈中的。所以当堆中生成一个变量或数组时,它们的名字会被存放在栈中,名字所在的栈内存中存放的是指向变量或数组在堆内存中的首地址,这样我们就可以通过栈来找到堆中的数组或变量。存放在栈中的变量相当于堆内存中对象或数组的引用,称之为对象引用,即java中的指针。

Java中的堆内存是在程序运行过程中动态分配的,而且由java垃圾回收机制管理,当堆中的数据不被使用的时候回收内存。因为是动态分配所以读取速度比较慢,而且整个内存中只有一个堆被所有线程共享。

3、  方法区。方法区跟堆一样,被所有线程共享。用来存放一些常量、类信息、方法名和即时编译后的代码

4、  常量池。常量池指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据顾名思义,常量池中用来存放常量,如基本类型的值,对象型的常量值等。对于String类型,它的值就存放在常量池中,而jvm中的常量池是以表的形式存储的,对于string类型,有一张固定长度的表存储字符串的值,在程序执行的时候,常量池是存放在方法区,而不是堆中的。

看完内存分配机制,其实可以这样来理解前面那段代码的内存分配问题:

可以看出,我们定义的str是hello,虽然最后输出的是hello world,但是常量str="hello"并没有被改变,而是对新的字符串重新分配了一块内存空间。其实字符串不能被改变可以理解为变量的内存地址没有被改变,再来看下面的代码:

String string1 = "name";
String string2 = "name";
System.out.println(string1==string2);//true
    
String string3 = new String("name");
String string4 = new String("name");
System.out.println(string3==string4);//false

string1和string2是存放在常量池中的,当创建string2时,常量池中已经有了name,所以string1指向name,不会重新创建一个一模一样的;

string3和string4是对象的引用,存放在栈中,实际的对象存在堆中,栈中的内存地址是不一样的,所以string3==string4为false

 

综上所述,String、StringBuffer和StringBuilder的区别如下(引自其它文章):

关于内存分配,目前觉得最好的一篇文章是:http://blog.csdn.net/yangyuankp/article/details/7651251

里面有四个链接,都很有价值,认真阅读完,相信会对java内存分配机制不再困惑。

以上是今天的所有内容......

posted @ 2017-03-13 15:24  bug改了我  阅读(410)  评论(0编辑  收藏  举报