zl程序教程

您现在的位置是:首页 >  后端

当前栏目

JAVA堆、栈、方法区、StringBuffer与String的区别

JAVA方法 string 区别 stringbuffer
2023-09-11 14:20:00 时间

前言:

上课提到的这么一个问题:
String, StringBuffer StringBuilder 的区别。

答:String 的长度是不可变的
StringBuffer 的长度是可变的,如果你对字符串中的内容经常进行操作,
特别是内容要修改时,那么使用 StringBuffer,如果最后需要 String,那么使
用 StringBuffer 的 toString()方法;线程安全
StringBuilder 是从 JDK 5 开始,为 StringBuffer 该类补充了一个单个线
程使用的等价类;通常应该优先使用 StringBuilder 类,因为它支持所有相同
的操作,但由于它不执行同步,所以速度更快

助记

其实由于随着电脑性能的提神以及业务需要,我们通常不怎么考虑线程安全问题而是速度问题,所以StringBuffer用的少为线程安全得那种!

对于可不可变联想到了 需要区分好堆、栈、方法区问题才能明白

字符串比较的问题:

补充知识: 

1. 对于字符串:其对象的引用都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期(new出来的)才能确定的就存储在堆中。对于equals相等的字符串,在常量池中永远只有一份,在堆中有多份

 

String s1 = "china";  
String s2 = "china";  
String s3 = "china";  
String ss1 = new String("china");  
String ss2 = new String("china");  
String ss3 = new String("china");  

 

 

面试题:

String s = new String(“xyz”);产生几个对象?一

个或两个,如果常量池中原来没有”xyz”,就是两个。

(先在常量池一个"xyz", 再在堆里面创建一个 new String("xyz"), 其中xyz 是由常量池拷贝过来

 

 

2. 对于基础类型的变量和常量 : 变量和引用存储在栈中,常量存储在常量池中。 ( 搞清楚什么是常量什么是变量。 常量就是final 的 不会变的, string 类型 其实也是 不可变类 所以在常量区。 )  

int i1 = 9;  
int i2 = 9;  
int i3 = 9;   
public static final int INT1 = 9;  
public static final int INT2 = 9;  
public static final int INT3 = 9; 

3.对于成员变量和局部变量:(成员变量就是方法外部,类的内部定义的变量;局部变量就是方法内部定义的变量。局部变量必须初始化,成员变量jvm自动初始化, 形式参数也是局部变量)

局部变量的数据存在于栈内存中。栈内存中的局部变量随着方法的消失而消失。
成员变量存储在堆中的对象里面,由垃圾回收器负责回收。

class BirthDate {  
    private int day;   //成员变量
    private int month;  
    private int year;      
    public BirthDate(int d, int m, int y) {  //d, m,y 也是局部变量
        day = d;   
        month = m;   
        year = y;  
    }  
    省略get,set方法………  
}  
  
public class Test{  
    public static void main(String args[]){  
        int date = 9;   //局部变量
        Test test = new Test();        
        test.change(date);   
        BirthDate d1= new BirthDate(7,7,1970);         
    }    
  
    public void change1(int i){  //i 是局部变量
        i = 1234;  
    }

解释:

1. date 是局部变量,基础类型, 所以值跟引用都放在栈里 int date = 9;

2. Test test = new Test(); test 为 new Test()对象的引用, 存放在栈里

3. test.change(date); i是局部变量,引用和值存在栈中。当方法change执行完成后,i就会从栈中消失。

4.4. BirthDate d1= new BirthDate(7,7,1970);    d1 为对象的引用 所以存放在栈中, 其所指的对象 存放在堆中, ,其中d,m,y为局部变量存储在栈中,且它们的类型为基础类型,因此它们的数据也存储在栈中。day,month,year为成员变量,它们存储在堆中(new BirthDate()里面)。当BirthDate构造方法执行完之后,d,m,y将从栈中消失。

5.main方法执行完之后,date变量,test,d1引用将从栈中消失,new Test(),new BirthDate()将等待垃圾回收。

 

Java中数据的存储分为以上5种方式,但在实际中最常谈起的是:堆内存存储 与 栈内存存储。

我们可以联系着二者来分析这两种不同的存储方式,更利于我们理解:

首先,它们有一定的相同之处:

堆与栈都是用于程序中的数据在RAM(内存)上的存储区域。并且Java会自动地管理堆和栈,不能人为去直接设置。

其次,更关键的在于它们的不同之处:

1.存储数据类型:栈内存中存放局部变量基本数据类型对象引用),而堆内存用于存放对象(实体)。

2.存储速度:就存储速度而言,栈内存的存储分配与清理速度更快于堆,并且栈内存的存储速度仅次于直接位于处理器当中的寄存器。

3.灵活性:就灵活性而言,由于栈内存与堆内存存储机制的不同,堆内存灵活性更优于栈内存。

这样两种存储方式的不同之处,也是由于它们自身的存储机制所造成的。所以为了理解它们,首先我们应该弄清楚它们分别的存储原理和机制,在Java中:

— 栈内存被要求存放在其中的数据的大小、生命周期必须是已经确定的;

— 堆内存可以被虚拟机动态的分配内存大小,无需事先告诉编译器的数据的大小、生命周期等相关信息。

接下来便可以进行分析:

栈内存和堆内存的存储数据类型为何不同?

我们知道在Java中,变量的类型通常分为:基本数据类型变量和对象引用变量。

首先,8种基本数据类型中的数字类型实际上都是存储的一组位数(所占bit位)不同的二进制数据;除此之外,布尔型只有true和false两种可能值。

其次,对象引用变量存储的,实际是其所关联(指向)对象在内存中的内存地址,而内存地址实际上也是一串二进制的数据。

所以,局部变量的大小是可以被确定的;

接下来,java中,局部变量会在其自身所属方法(或代码块)执行完毕后,被自动释放。

所以局部变量的生命周期也是可以被确定的。

那么,既然局部变量的大小和生命周期都可以被确定,完全符合栈内存的存储特点。自然,局部变量被存放在栈内存中。

 

参考文献:

https://blog.csdn.net/DamonUp/article/details/88304467