2019独角兽企业重金招聘Python工程师标准>>>

public class Test {

public static final String MESSAGE="taobao";

public static void main(String[] args) {

String a = "tao"+"bao";

String b = "tao";

String c = "bao";

System.out.println(a==MESSAGE);    System.out.println( (b+c)==MESSAGE);

}

}

对于这道题,考察的是对String类型的认识以及编译器优化。Java中String不是基本类型,但是有些时候和基本类型差不多,如String b = "tao"; 可以对变量直接赋值,而不用 new 一个对象(当然也可以用 new)。所以String这个类型值得好好研究下。

Java中的变量和基本类型的值存放于栈内存,而new出来的对象本身存放于堆内存,指向对象的引用还是存放在栈内存。例如如下的代码:

int i=1;

String s = new String("Hello World");

变量i和s以及1存放在栈内存,而s指向的对象”Hello World”存放于堆内存。

栈内存的一个特点是数据共享,这样设计是为了减小内存消耗,前面定义了i=1,i和1都在栈内存内,如果再定义一个j=1,此时将j放入栈内存,然后查找栈内存中是否有1,如果有则j指向1。如果再给j赋值2,则在栈内存中查找是否有2,如果没有就在栈内存中放一个2,然后j指向2。也就是如果常量在栈内存中,就将变量指向该常量,如果没有就在该栈内存增加一个该常量,并将变量指向该常量。

如果j++,这时指向的变量并不会改变,而是在栈内寻找新的常量(比原来的常量大1),如果栈内存有则指向它,如果没有就在栈内存中加入此常量并将j指向它。这种基本类型之间比较大小和我们逻辑上判断大小是一致的。如定义i和j是都赋值1,则i==j结果为true。==用于判断两个变量指向的地址是否一样。i==j就是判断i指向的1和j指向的1是同一个吗?当然是了。对于直接赋值的字符串常量(如String s=“Hello World”;中的Hello World)也是存放在栈内存中,而new出来的字符串对象(即String对象)是存放在堆内存中。如果定义String s=“Hello World”和String w=“Hello World”,s==w吗?肯定是true,因为他们指向的是同一个Hello World。

堆内存没有数据共享的特点,前面定义的String s = new String("Hello World");后,变量s在栈内存内,Hello World 这个String对象在堆内存内。如果定义String w =new String("Hello World");,则会在堆内存创建一个新的String对象,变量w存放在栈内存,w指向这个新的String对象。堆内存中不同对象(指同一类型的不同对象)的比较如果用==则结果肯定都是false,比如s==w?当然不等,s和w指向堆内存中不同的String对象。如果判断两个String对象相等呢?用equals方法。

说了这么多只是说了这道题的铺垫知识,还没进入主题,下面分析这道题。MESSAGE成员变量及其指向的字符串常量肯定都是在栈内存里的,变量a运算完也是指向一个字符串“taobao”啊?是不是同一个呢?这涉及到编译器优化问题。对于字符串常量的相加,在编译时直接将字符串合并,而不是等到运行时再合并。也就是说

String a = "tao"+"bao";和String a = "taobao";编译出的字节码是一样的。所以等到运行时,根据上面说的栈内存是数据共享原则,a和MESSAGE指向的是同一个字符串。而对于后面的(b+c)又是什么情况呢?b+c只能等到运行时才能判定是什么字符串,编译器不会优化,想想这也是有道理的,编译器怕你对b的值改变,所以编译器不会优化。运行时b+c计算出来的"taobao"和栈内存里已经有的"taobao"是一个吗?不是。b+c计算出来的"taobao"应该是放在堆内存中的String对象。这可以通过System.out.println( (b+c)==MESSAGE);的结果为false来证明这一点。如果计算出来的b+c也是在栈内存,那结果应该是true。Java对String的相加是通过StringBuffer实现的,先构造一个StringBuffer里面存放”tao”,然后调用append()方法追加”bao”,然后将值为”taobao”的StringBuffer转化成String对象。StringBuffer对象在堆内存中,那转换成的String对象理所应当的也是在堆内存中。下面改造一下这个语句System.out.println( (b+c).intern()==MESSAGE);结果是true,intern()方法会先检查String池(或者说成栈内存)中是否存在相同的字符串常量,如果有就返回。所以intern()返回的就是MESSAGE指向的"taobao"。再把变量b和c的定义改一下,

final String b = "tao";

final String c = "bao";

System.out.println( (b+c)==MESSAGE);

现在b和c不可能再次赋值了,所以编译器将b+c编译成了”taobao”。因此,这时的结果是true。

在字符串相加中,只要有一个是非final类型的变量,编译器就不会优化,因为这样的变量可能发生改变,所以编译器不可能将这样的变量替换成常量。例如将变量b的final去掉,结果又变成了false。这也就意味着会用到StringBuffer对象,计算的结果在堆内存中。

如果对指向堆内存中的对象的String变量调用intern()会怎么样呢?实际上这个问题已经说过了,(b+c).intern(),b+c的结果就是在堆内存中。对于指向栈内存中字符串常量的变量调用intern()返回的还是它自己,没有多大意义。它会根据堆内存中对象的值,去查找String池中是否有相同的字符串,如果有就将变量指向这个string池中的变量。

String a = "tao"+"bao";

String b = new String("taobao");

System.out.println(a==MESSAGE); //true

System.out.println(b==MESSAGE);  //false

b = b.intern();

System.out.println(b==MESSAGE); //true

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

转载于:https://my.oschina.net/u/551903/blog/134000

关于String内存分配的深入探讨相关推荐

  1. String内存分配

    Java 把内存划分成两种:一种是栈内存,另一种是堆内存.在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的 栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存 ...

  2. keil 调试 操作系统_调试操作系统:内存分配的经验教训

    keil 调试 操作系统 It began, as so many investigations do, with a bug report. 和许多调查一样,它是从一个错误报告开始的 . The n ...

  3. String 对象内存分配策略

    这个问题可以说是一个高频的面试题目,以前把这个问题弄懂了,最近突然想到这个问题,一时间竟然没有太好的思路了.所以花些时间整理一下其中的知识点. 一.内存分配策略 我们先来看一个题目(这个问题都快看吐了 ...

  4. JVM学习笔记之-StringTable String的基本特性,内存分配,基本操作,拼接操作,intern()的使用,垃圾回收 ,G1中的String去重操作

    String的基本特性 string:字符串,使用一对""引起来表示. String s1 = ""; //字面量的定义方式 String s2 = new S ...

  5. java内存模型 创建类_JVM内存模型及String对象内存分配

    昨天看了一篇关于<Java后端程序员1年工作经验总结>的文章,其中有一段关于String和StringBuffer的描述,对于执行结果仍然把握不准,趁此机会也总结了下JVM内存模型. 1. ...

  6. 《深入理解Java虚拟机》-----第3章 垃圾收集器与内存分配策略

    Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的"高墙",墙外面的人想进去,墙里面的人却想出来. 3.1 概述 说起垃圾收集(Garbage Collection,G ...

  7. Golang 内存分配与逃逸分析

    参考:灵魂拷问:Go 语言这个变量到底分配到哪里了? 来源于 公众号: 脑子进煎鱼了 ,作者陈煎鱼. 我们在写代码的时候,有时候会想这个变量到底分配到哪里了?这时候可能会有人说,在栈上,在堆上.信我准 ...

  8. linux环境内存分配原理

    Linux的虚拟内存管理有几个关键概念: Linux 虚拟地址空间如何分布?malloc和free是如何分配和释放内存?如何查看堆内内存的碎片情况?既然堆内内存brk和sbrk不能直接释放,为什么不全 ...

  9. Java中的内存分配

    Java 程序在运行时,需要在内存中的分配空间.为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式. 栈 存储局部变量 堆 存储new出来的东西 方法区 ...

  10. 【朝花夕拾】Android性能篇之(二)Java内存分配

    前言       原文:[朝花夕拾]Android性能篇之(二)Java内存分配        在内存方面,相比于C/C++程序员,咱们java系程序员算是比较幸运的,因为对于内存的分配和回收,都交给 ...

最新文章

  1. 对Exchange 事件ID 9154 DSACCESS 返回 DS 通知出现的错误“0x80004005”的处理
  2. 2020-2021年度第二届全国大学生算法设计与编程挑战赛 (春季赛)- 天才的操作(线段树+主席树+树上倍增)
  3. python 封装_Python 面向对象三大特性之封装
  4. 理财平台频繁暴雷,羊毛党该要本金还是撸利息?
  5. 95-848-020-源码-AKKA-Akka与Actor 模型
  6. Error creating bean with name ‘tokenBean‘ defined spring找不到Bean
  7. Kubernetes 真的很复杂吗?
  8. SpringMVC→拦截器、SpringMVC拦截器实现、多个拦截器工作原理、拦截器使用场景、拦截器Interceptor与过滤器Filter区别
  9. #define和inline 的区别
  10. [Crypto/CTF]CTF Crypto 包函数和工具总结[ 2021/10/30更新]
  11. 更新一波,特殊福利 !
  12. Helix QAC 2021.1
  13. C语言加油站程序,C语言解决 加油站问题
  14. Linux kernel + busybox自制Linux系统
  15. Android新手入门 FAQ
  16. #35-【刷题】乐乐的方块
  17. [Matlab]脚本实现Excel单元格内容首尾空格删除
  18. 采用SpringBoot发送简单、抄送、密送、带附件邮件
  19. Kubernetes Krew简介
  20. etf动量轮动+大盘择时:年化30%的策略

热门文章

  1. 凸优化第二章凸集 2.1仿射集合和凸集
  2. 【NLP】揭秘马尔可夫模型神秘面纱系列文章(四)
  3. 【生信进阶练习1000days】day7-RSQLite的使用
  4. 【生信进阶练习1000days】day4-Annotation包中mapIds函数的使用
  5. 一文读懂矩阵的特征分解
  6. 【数学题】Multicolored Markers【codeforces-Round #506-div3-F】
  7. 动态链接库dll生成与调用 加密 电脑唯一识别 windows下多个cmd命令输出结果的同时获取 本地时间的处理
  8. python虚拟环境--virtualenv
  9. mysql主从集群搭建;(集群复制数据)
  10. Mysql主从架构的复制原理及配置详解