heap 和 stack 分别表示堆和栈。他们是两种计算机领域的数据按序排列的结构。
堆(heap ),队列优先,先进先出(FIFO—first in first out)。
栈(stack ),先进后出(FILO—First-In/Last-Out)。

主要区别如下:

一、空间分配:

1.栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

2.堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由 OS 回收,分配方式倒是类似于链表。PS:java 中都是系统 GC,程序员无法进行 GC。

二、缓存方式:

1.栈是一级缓存,他们通常是被调用时处于存储空间中,调用完后就释放。

2.堆是二级缓存,生命周期由垃圾回收机制决定。所以这些对象的调用速度要慢一些。

三、数据结构区别

1.栈:一种先进后出的数据结构。

2.堆:堆可以看成一棵树,如堆排序。

四、在 Java 中的区别

栈的优势是,存取速度比堆要快,仅次于直接位于 CPU 中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据在多个线程或者多个栈之间是不可以共享的,但是在栈内部多个值相等的变量是可以指向一个地址的。

堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java 的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。

Java的8种基本类型int,short,long,byte,float,double,char,boolean。

这些类型都是通过   int i = 10 ; 来定义的。这些称为自动变量,这些都是实际的值,而非实例对象。

如 int a= 3; 这里的 a 是一个指向 int 类型的引用,指向 3 这个字面值。这些字面值的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存在于栈中。

另外,栈有一个很重要的特殊性,就是存在栈中的数据可以共享

int a = 3 ;
int b = 3 ;

编译器执行第一句,先创建内存空间 a,然后查找是否已经存在值为 3 的地址。若没找到,则开辟一个存放值 3 的地址,然后将 a 指向 3。然后执行第二句相同操作。这样,就出现了 a 与 b 同时均指向 3 的情况。

特别注意的是,这种字面值的引用与类对象的引用不同。假定两个类对象的引用同时指向一个对象,如果一个对象引用变量修改了这个对象的内部状态,那么另一个对象引用变量也即刻反映出这个变化。相反,通过字面值的引用来修改其值,不会导致另一个指向此字面值的引用的值也跟着改变的情况。

如上例,我们定义完 a 与 b 的值后,再令 a=4;那么,b 不会等于 4,还是等于 3。在编译器内部,遇到 a=4;时,它就会重新搜索栈中是否有 4 的字面值,如果没有,重新开辟地址存放 4 的值;如果已经有了,则直接将 a 指向这个地址。因此 a 值的改变不会影响到 b 的值。

Java 的另一种是包装类,如 Integer,String, Double 等将相应的基本数据类型包装起来的类。这些类数据全部存在于【堆】中。Java 用 new()语句来显示地告诉编译器,在运行时才根据需要动态创建,因此比较灵活,但缺点是要占用更多的时间。

需要注意的一点是

Integer i = 127;
Integer j = 127;
System.out.println(i==j);
Integer a = -129;
Integer b = -129;
System.out.println(a==b);

这里的两个打印,分别为 true 和 false。那是因为,Integer 对 -128 到 127 的数做了特殊处理,做了内存共用。在运行之前,堆内存就已经存在了-128 到 127 这 256 个 integer 对象。因此上述例子中的 i,j 都指向同一对象,==操作符对比的其实是引用地址,因此返回 true。

返回 false 就显而易见了,因为地址不同。因此对比 integer 的值推荐使用 equal()方法。

但是,若 integer 的的声明方式是用 new 的话,那就是一个全新对象实例,每个对象实例都唯一。

数据类型包装类的值是不可修改的。不仅仅是 String 类的值不可修改,所有的数据类型包装类都不能更改其内部的值。

有关于 string 字符串的操作。其实与上面大同小异,只需记住,string 字符串是不可修改的,每次有关字符串的操作,必定返回一个新的字符串。