详解Java中堆、栈、队列、常量池的区别

先写下Java中堆、栈、方法区大概:

1⃣️堆:存放new出来的对象和数组,可以被所有线程共享,不会存放别的对象引用;
2⃣️栈:存放基本数据类型(包括值),引用对象变量(包括地址);
3⃣️方法区:可以被所有线程共享,存放所有class和static变量;

(一)系统和数据结构的堆、栈

系统中的堆、栈是真实的物理存储区,数据结构中的堆、栈是抽象的数据储存结构。

(二)数据结构中的堆、栈

1.堆
堆是一种完全二叉树或者近似完全二叉树,完全二叉树是效率很高的数据结构,像十分常用的排序算法、Dijkstra算法、Prim算法等都要用堆才能优化。
2.栈(FILO(First In Last Out))
栈满足先进后出的方式,读取顺序限制性强。只能在一端(称为栈顶(top))对数据项进行插入和删除。

堆栈,是栈的抽象表述,定义了栈的基本动作;本身就是栈,是栈的抽象表述。堆栈中定义了基本操作,最重要的是PUSH进栈和POP出栈。

(三)系统中的堆、栈

1、栈区(stack)由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

内存空间:栈是向低地址扩展的数据结构,是一块连续的内存的区域。栈顶的地址和栈的最大容量是系统预先规定好的,如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。

2、堆区(heap)— 是一个可动态申请的内存空间(其记录空闲内存空间的链表由操作系统维护),在java中,所有使用new xxx()构造出来的对象都在堆中存储一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。

内存空间:堆是向高地址扩展的数据结构(链表结构),是不连续的内存空间,堆的内存空间大小受限于计算机系统中有效的虚拟内存。因此堆获得的空间比较灵活,也比较大。

注意:它与数据结构中的堆是两回事,分配方式倒是类似于链表。

(四)队列

队列 (FIFO(First In First Out)),是一种只能先进先出读取的线性表,读取顺序限制性强。
是一种先进先出的线性表,表的前端可以删除,表的后端可以插入。

(五)常量池

1.常量池:存放字符串常量和基本类型常量(public static final),常量池优点是为了避免频繁的创建和销毁对象而影响性能,也实现了对象的共享;

2.字符串常量池:
(1)节约内存空间:常量池中相同的字符串合并为一个;
(2)节约运行时间:比较字符串时,“==” 比 equals() 快;

例如1:

String str1 = "abc"; 
String str2 = "abc"; 

str1 = "bcd"; 

String str3 = str1; 
System.out.println(str3); //bcd 

String str4 = "bcd"; 
System.out.println(str1 == str4); //true

System.out.println(str1 == str2); //false

str3这个对象的引用直接指向str1所指向的对象(注意,str3并没有创建新对象)。当str1改完其值后,再创建一个String的引用 str4,并指向因str1修改值而创建的新的对象。可以发现,这回str4也没有创建新的对象,从而再次实现栈中数据的共享。

例如2:

String str1 = new String("abc"); 
String str2 = "abc"; 
System.out.println(str1==str2); //false 

创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。

另:String s = new String(“abc”);产生几个对象?答:一个或两个,如果常量池中原来没有”abc”,就是两个。

3.扩充常量池:
String的intern()方法就是扩充常量池的一个方法;当一个String实例str调用intern()方法时,Java查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其的引用,如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用;

String str1 = "abc";  
String str2 = new String("abc"); 
String str3 = str2.intern();

System.out.println( str1==str2 );  //false
System.out.println( str1==str3 );  //true

4.结论与建议:
(1) 使用诸如String str = “abc”;的格式定义类时,对象可能并没有被创建,只能肯定表明指向 String类的引用被创建了。至于这个引用是否指向了一个新的对象,必须根据上下文来考虑;

因此,更为准确的说法是,我们创建了一个指向String类的对象的引用变量str,这个对象引用变量指向了某个值为"abc"的String类。清醒地认识到这一点对排除程序中难以发现的bug是很有帮助的。

(2) 使用String str = “abc”;的方式,可以在一定程度上提高程序的运行速度,因为JVM会自动根据常量池中数据的实际情况来决定是否有必要创建新对象。

而对于String str = new String(“abc”);的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。这个思想应该是享元模式的思想,但JDK的内部在这里实现是否应用了这个模式,不得而知。

(3) 当比较包装类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==。

(4) 由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值