堆栈常量池详解+例子
转自:http://www.iteye.com/topic/634530
一:概述
-
寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制.
-
栈(stack):存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(字符串常量对象存放在常量池中。
-
堆(heap):存放所有new出来的对象。
-
静态域:存放静态成员(static定义的)
-
常量池:存放字符串常量(String)和基本类型常量(public static final)。
-
非RAM存储:硬盘等永久存储空间
栈
存放基本数据类型变量数据和对象的 “引用”,对象本身不存在这个里面。
常量池:
像int i=1中的1就不放在常量池中,因为这个1不是常量属于变量。
像public static final int i=1中的1就放在这个常量池中。
二:初步理解(可以先看最下面的几个性质)
String类型
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产生六个名字在栈中。
堆:只要是new出来的都在栈中存储,前三个没有new只有后面三个在堆中存在
常量池:所有的字符串类型和基本数据类型都在这个里面存着,并且相同的不会重复存储。
String ss1 = new String(“china”); 整体介绍
对于通过new产生一个字符串(假设为”china”)时,会先去常量池中查找是否已经有了”china”对象,如果没有则在常量池中创建一个此字符串对象,然后堆中再创建一个常量池中此”china”对象的拷贝对象。这也就是有道面试题:String s = new String(“xyz”);产生几个对象?一个或两个,如果常量池中原来没有”xyz”,就是两个。
int 类型
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;
对于基础类型的变量和常量:变量和引用存储在栈中,常量存储在常量池中。
如int a = 3; 这里的a是一个指向int类型的引用,指向3这个字面值。这些字面值的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存在于栈中。
##### 隆重介绍–引用类型######
class BirthDate {
private int day;
private int month;
private int year;
public BirthDate(int d, int m, int 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 = 1234;
}
}
对于以上这段代码,date为局部变量,i,d,m,y都是形参为局部变量,day,month,year为成员变量。下面分析一下代码执行时候的变化:
- main方法开始执行:int date = 9;
date局部变量,基础类型,引用和值都存在栈中。- Test test = new Test();
test为对象引用,存在栈中,对象(new Test())存在堆中- test.change(date);
i为局部变量,引用和值存在栈中。当方法change执行完成后,i就会从栈中消失。- BirthDate d1= new BirthDate(7,7,1970);
d1为对象引用,存在栈中,对象(new BirthDate())存在堆中,其中d,m,y为局部变量存储在栈中,且它们的类型为基础类型,因此它们的数据也存储在栈中。day,month,year为成员变量,它们存储在堆中(new BirthDate()里面)。当BirthDate构造方法执行完之后,d,m,y将从栈中消失。- main方法执行完之后,date变量,test,d1引用将从栈中消失,new Test(),new BirthDate()将等待垃圾回收.
三:性质介绍
堆和栈的性质
-
栈:
- 由编译器自动分配释放 ,存放函数的参数值,局部变量的值
- 优点是存取速度很快,仅次于CPU中的寄存器
- 缺点是存在栈中的数据大小与生存期必须是确定的、
- 栈中的数据可以共享(见下面详细介绍)
-
堆:
- Java的垃圾回收器自动收走这些不再使用的数据
- 优点是动态的分配内存大小(因为是存储new 出来的数据的)
- 缺点是运行时需要动态分配内存,所以存取速度慢
栈的共享性
存在栈中的数据可以共享
int a = 3;
int b = 3;
-
编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找栈中有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处理int b = 3;在创建完b的引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。这样,就出现了a与b同时均指向3的情况。
-
接下来的话,再令a=4;那么,b不会等于4,还是等于3。在编译器内部,遇到a=4;时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。
上面这个例子很容易与引用类型的混淆:
假定两个类对象的引用同时指向一个对象,如果一个对象引用变量修改了这个对象的内部状态,那么另 一个对象引用变量也即刻反映出这个变化。
成员变量和局部变量
记住一点:栈里面是用完就删!而堆是全局的!
- 上面的局部变量放在栈中
- 成员变量中的实例变量放在堆中
类对象的引用同时指向一个对象,如果一个对象引用变量修改了这个对象的内部状态,那么另 一个对象引用变量也即刻反映出这个变化。
成员变量和局部变量
[外链图片转存中…(img-JtiCFBij-1640593655375)]
记住一点:栈里面是用完就删!而堆是全局的!
- 上面的局部变量放在栈中
- 成员变量中的实例变量放在堆中