String常量池问题的几个例子

下面是几个常见例子的比较分析和理解:

  1. String a = "a1";

  2. String b = "a" + 1;

  3. System.out.println((a == b)); //result = true

  4. String a = "atrue";

  5. String b = "a" + "true";

  6. System.out.println((a == b)); //result = true

  7. String a = "a3.4";

  8. String b = "a" + 3.4;

  9. System.out.println((a == b)); //result = true

分析:JVM对于字符串常量的"+"号连接,将程序编译期,JVM就将常量字符串的"+"连接优化为连接后的值,拿"a" + 1来说,经编译器优化后在class中就已经是a1。在编译期其字符串常量的值就确定下来,故上面程序最终的结果都为true。

  1. String a = "ab";

  2. String bb = "b";

  3. String b = "a" + bb;

  4. System.out.println((a == b)); //result = false

分析:JVM对于字符串引用,由于在字符串的"+"连接中,有字符串引用存在,而引用的值在程序编译期是无法确定的,即"a" + bb无法被编译器优化,只有在程序运行期来动态分配并将连接后的新地址赋给b。所以上面程序的结果也就为false。

  1. String a = "ab";

  2. final String bb = "b";

  3. String b = "a" + bb;

  4. System.out.println((a == b)); //result = true

分析:和[3]中唯一不同的是bb字符串加了final修饰,对于final修饰的变量,它在编译时被解析为常量值的一个本地拷贝存储到自己的常量 池中或嵌入到它的字节码流中。所以此时的"a" + bb和"a" + "b"效果是一样的。故上面程序的结果为true。

  1. String a = "ab";

  2. final String bb = getBB();

  3. String b = "a" + bb;

  4. System.out.println((a == b)); //result = false

  5. private static String getBB() {

  6. return "b";

  7. }

分析:JVM对于字符串引用bb,它的值在编译期无法确定,只有在程序运行期调用方法后,将方法的返回值和"a"来动态连接并分配地址为b,故上面 程序的结果为false。

通过上面4个例子可以得出得知:

String  s  =  "a" + "b" + "c"; 
  
就等价于String s = "abc";

String  a  =  "a";   
String  b  =  "b";   
String  c  =  "c";   
String  s  =   a  +  b  +  c;

这个就不一样了,最终结果等于:

  1. StringBuffer temp = new StringBuffer();

  2. temp.append(a).append(b).append(c);

  3. String s = temp.toString();

由上面的分析结果,可就不难推断出String 采用连接运算符(+)效率低下原因分析,形如这样的代码:

  1. public class Test {

  2. public static void main(String args[]) {

  3. String s = null;

  4. for(int i = 0; i < 100; i++) {

  5. s += "a";

  6. }

  7. }

  8. }

每做一次 + 就产生个StringBuilder对象,然后append后就扔掉。下次循环再到达时重新产生个StringBuilder对象,然后 append 字符串,如此循环直至结束。如果我们直接采用 StringBuilder 对象进行 append 的话,我们可以节省 N - 1 次创建和销毁对象的时间。所以对于在循环中要进行字符串连接的应用,一般都是用StringBuffer或StringBulider对象来进行 append操作。

String对象的intern方法理解和分析:

  1. public class Test4 {

  2. private static String a = "ab";

  3. public static void main(String[] args){

  4. String s1 = "a";

  5. String s2 = "b";

  6. String s = s1 + s2;

  7. System.out.println(s == a);//false

  8. System.out.println(s.intern() == a);//true

  9. }

  10. }

这里用到Java里面是一个常量池的问题。对于s1+s2操作,其实是在堆里面重新创建了一个新的对象,s保存的是这个新对象在堆空间的的内容,所 以s与a的值是不相等的。而当调用s.intern()方法,却可以返回s在常量池中的地址值,因为a的值存储在常量池中,故s.intern和a的值相等。

总结

栈中用来存放一些原始数据类型的局部变量数据和对象的引用(String,数组.对象等等)但不存放对象内容

堆中存放使用new关键字创建的对象.

字符串是一个特殊包装类,其引用是存放在栈里的,而对象内容必须根据创建方式不同定(常量池和堆).有的是编译期就已经创建好,存放在字符串常 量池中,而有的是运行时才被创建.使用new关键字,存放在堆中。

转载于:https://blog.51cto.com/wuqinglong/1431774

深入Java核心 Java内存分配原理精讲(3)相关推荐

  1. 深入Java核心 Java内存分配原理精讲

    深入Java核心 Java内存分配原理精讲 Java内存分配与管理是Java的核心技术之一,之前我们曾介绍过Java的内存管理与内存泄露以及Java垃圾回收方面的知识,今天我们再次深入Java核心,详 ...

  2. java 检测硬盘原理_深入Java核心 Java内存分配原理精讲

    Java内存分配与管理是Java的核心技术之一,一般Java在内存分配时会涉及到以下区域: ◆寄存器:我们在程序中无法控制 ◆栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中 ...

  3. JAVA中堆栈和内存分配原理

    JAVA中堆栈和内存分配原理 1.栈.堆 1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制. 2. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在 ...

  4. 独占设备的分配与回收_灵魂拷问:Java对象的内存分配过程是如何保证线程安全的?...

    点击上方"linkoffer", 选择关注公众号高薪职位第一时间送达 作者 l Hollis JVM内存结构,是很重要的知识,相信每一个静心准备过面试的程序员都可以清楚的把堆.栈. ...

  5. Java基础-Java中的内存分配与回收机制

    Java基础-Java中的内存分配与回收机制 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一. 二. 转载于:https://www.cnblogs.com/yinzhengji ...

  6. java 线程 内存分配内存_漫谈JAVA语言的内存分配

    在说JAVA语言的内存分配之前,我们先聊聊OS的Memory Management,这是学习操作系统课程的一个重要内容.从这里开始理解内存分配有利于理解的更深入. 我们知道物理内存是由page 和se ...

  7. 原创 | 灵魂拷问:Java对象的内存分配过程是如何保证线程安全的?

    △Hollis, 一个对Coding有着独特追求的人△ 这是Hollis的第 222 篇原创分享 作者 l Hollis 来源 l Hollis(ID:hollischuang) JVM内存结构,是很 ...

  8. java中堆栈内存_Java堆空间与堆栈– Java中的内存分配

    java中堆栈内存 Sometime back I wrote a couple of posts about Java Garbage Collection and Java is Pass by ...

  9. java对象的内存分配流程

    了解对象的内存分配流程对常见内存溢出问题.jvm优化有很大作用. 内存分配原则 对象栈内分配 通常理解new对象都在堆中分配存储空间,但是当(通过逃逸分析 确定)对象仅在方法内使用而未被外部访问的时候 ...

最新文章

  1. Android之HTTP预备知识
  2. Python列表和元祖
  3. 在envi做随机森林_随机森林原理介绍与适用情况(综述篇)
  4. xml突然变成空白_“侏罗纪中期”出现了型增转变填补食肉性恐龙体型发展当中的空白...
  5. Touch - 全能名片王
  6. es6 --- 使用proxy对数据进行劫持
  7. 抓取html中用到的css_如何使用HTML和CSS制作像《星球大战》一样的抓取文字
  8. iOS9适配(包括APNs)的改变
  9. mac系统如何在当前目录下打开终端
  10. JAVA抽象类和接口的深入探讨
  11. OpenGL ES 中的模板测试
  12. 扫清盲点,如何正确的从HttpClient 3.x系统升级到HttpClient 4.x
  13. 发生在我们身边的灵异事件 - 发生在台湾奇萊山的一些灵异事件
  14. TriCore 与 RT-Thread(TC264 移植)
  15. 灰度图片及彩色图片像素点统计及显示
  16. ua-parser-js实现获取浏览器信息和操作系统信息
  17. 基础 | 并发编程 - [LongAdder Striped64]
  18. pandas实现列转行
  19. keras.metrics有五种accuracy
  20. ANativeWindow

热门文章

  1. Python-多进程
  2. date、sleep和usleep命令
  3. 使用python+ffmpeg批量转换格式
  4. 第一冲刺阶段工作总结02
  5. 00020970-0000-0000-C000-000000000046
  6. POJ 2942 Knights of the Round Table ★(点双连通分量+二分图判定)
  7. c#中如何实现拷贝对象
  8. dotNetCharting使用总结
  9. 2022.4.9 mac os M1 芯片 12.3.1 Monterey 安装cocoapods
  10. linux查看修改环境变量日志,linux查看和修改PATH环境变量的方法