字符串对象在JVM中可能有两个存放的位置:字符串常量池或堆内存

  • 使用常量字符串初始化的字符串对象,它的值存放在字符串常量池中;

  • 使用字符串构造方法创建的字符串对象,它的值存放在堆内存中;

String提供了一个API, java.lang.String.intern(),这个API可以手动将一个字符串对象的值转移到字符串常量池中。

在1.7之前,字符串常量池是在PermGen区域,这个区域的大小是固定的——不能在运行时根据需要扩大,也不能被垃圾收集器回收,因此如果程序中有太多的字符串调用了intern方法的话,就可能造成OOM。

在1.7以后,字符串常量池移到了堆内存中,并且可以被垃圾收集器回收,这个改动降低了字符串常量池OOM的风险。

案例分析

1. String对象的两种创建方式:

           String s1="javaadu";

           String s2="javaadu";

           String s3=new String("javaadu");

           System.out.println(s1==s2);          //true     

           System.out.println(s1==s3);          //false

           

           String s4=s3.intern();

           System.out.println(s1==s4);          //true

           System.out.println(s3==s4);          //false

  • 直接使用双引号声明出来的String对象会直接存储在常量池中。

  • 如果不是用双引号声明的String对象,可以使用String提供的intern方法。String.intern() 是一个Native方法,它的作用是:如果常量池中已经包含一个等于此String对象内容的字符串,则返回常量池中该字符串的引用;如果没有,则在常量池中创建与此 String 内容相同的字符串,并返回常量池中创建的字符串的引用。

2. String字符串拼接

           String str1 = "str";

           String str2 = "ing";

           String str3 ="str"+"ing";  //常量池中的对象

           String str4 = str1 + str2;  //在堆上创建的新对象       

           String str5 = "string";        

           System.out.println(str3 == str4);          //flase

           System.out.println(str3 == str5);          //true

           System.out.println(str4 == str5);          //false

String s1 = new String("abc");这句话创建了几个对象?

创建了两个对象。或者一个

如果如果常量池没有,就是两个;如果常量池已经有,就是一个。

           String str1=new String("abc");

           String str2="abc";

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

解释:

先有字符串"abc"放入常量池,然后 new 了一份字符串"abc"放入Java堆(字符串常量"abc"在编译期就已经确定放入常量池,而 Java 堆上的"abc"是在运行期初始化阶段才确定),然后 Java 栈的 str1 指向Java堆上的"abc"。

3. 8种基本类型的包装类和常量池

  • Java 基本类型的包装类的大部分都实现了常量池技术,即Byte,Short,Integer,Long,Character,Boolean;这5种包装类默认创建了数值[-128,127]的相应类型的缓存数据,但是超出此范围仍然会去创建新的对象。

  • 两种浮点数类型的包装类 Float,Double 并没有实现常量池技术。

           Integer aInteger=1;

           Integer bInteger=2;

           

           Integer cInteger=3;

           Integer dInteger=3;

           

           Integer eInteger=321;

           Integer fInteger=321;

           

           Long gLong=3L;

           Long hLong=2L;

           

           System.out.println(cInteger==dInteger);         //true

           System.out.println(eInteger==fInteger);         //false

           System.out.println(cInteger==(aInteger+bInteger));        //true

           System.out.println(cInteger.equals(aInteger+bInteger)); //true

           System.out.println(gLong==(aInteger+bInteger));           //true

           System.out.println(gLong.equals(aInteger+bInteger)); //false

           System.out.println(gLong.equals(aInteger+hLong));         //true

使用==:

①如果比较Integer变量,默认比较的是地址值。

②Java的Integer维护了从-128~127的缓存池。

③如果比较的某一边有操作表达式(如a+b),那么比较的是具体数值。

使用equals():

①无论Integer还是Long中的equals默认比较的是数值。

②Long的equals()方法,JDK的默认实现:会判断是否是Long类型。

           int a=59;       //基本类型,存储在栈中。

           Integer bInteger=59; //会调用Integer的valueOf方法

           /*

            * valueOf这个方法就是返回一个Integer对象,返回之前,需要判断当前值是否在[-128,127]区间。

            * 如果在此区间,先看IntegerCache中是否存在此对象,若存在,直接返回引用,否则创建一个新的对象。

            * 若不在此区间,return new Integer(i);

            */

           Integer cInteger=Integer.valueOf(59); //因为IntegerCache中存在此对象,所以直接返回引用。

           Integer dInteger=new Integer(59);    //直接创建一个新的对象。

           

           System.out.println(a==bInteger);           //true    Integer会自动拆箱成int,然后进行值比较

           System.out.println(bInteger==cInteger);         //true

           System.out.println(cInteger==dInteger);         //false

           System.out.println(a==dInteger);           //true  dInteger会自动拆箱,进行值比较

注意:

==比较的是地址。

但当为基本类型时,比较的是值。

如果两边有包装类型,则先将包装类型转换为基本类型再比较值是否相等。

字符串在JVM中如何存放 及常量池技术相关推荐

  1. jvm中方法区和常量池详解_Java常量池(静态常量池与运行时常量池)

    1.什么是常量 用final修饰的成员变量表示常量,值一旦给定就无法改变! final修饰的变量有三种:静态变量.实例变量和局部变量,分别表示三种类型的常量. Java中的常量池,实际上分为两种形态: ...

  2. jvm中方法区和常量池详解_JVM——内存区域:运行时数据区域详解

    关注微信公众号:CodingTechWork,一起学习进步. 引言 我们经常会被问到一个问题是Java和C++有何区别?我们除了能回答一个是面向对象.一个是面向过程编程以外,我们还会从底层内存管理和垃 ...

  3. 蚂蚁面试:字符串在JVM中如何存放?

    字符串对象在JVM中可能有两个存放的位置:字符串常量池或堆内存. 使用常量字符串初始化的字符串对象,它的值存放在字符串常量池中: 使用字符串构造方法创建的字符串对象,它的值存放在堆内存中: Strin ...

  4. 欧尼酱讲JVM(11)——动态链接和常量池

    因为动态链接和运行时常量池有联系,所以先解释一下运行时常量池: 运行时常量池位于方法区(注意: JDK1.7 及之后版本的 JVM 已经将运行时常量池从方法区中移了出来,在 Java 堆(Heap)中 ...

  5. Integer关于equals与==的比较(自动拆装箱技术和常量池技术)

    首先介绍下 equals方法遵循的规则:自反性,一致性,传递性,对称性,与null相比,返回false: 1.JAVA当中所有的类都是继承于Object这个基类的,在Object中的基类中定义了一个e ...

  6. JVM虚拟机-Class文件之常量池

    一.常量池的作用 在class文件中的魔数.副版本号.主版本之后,紧接着就是常量池的数据区域了,如下图用红线包括的位置: 常量池可以比喻为Class文件里的资源仓库,它是Class 文件结构中与其他项 ...

  7. 读取字符串字符时出错_JVM | 运行时常量池和字符串常量池及intern()

    本文知识点 这一块的知识点,一直都是最为混乱的,网上很多博客有的都自相矛盾,因此,这一块需要深入学习,本文以JDK11为基础,尽量参考官方文档 静态常量池 运行时常量池及字符串的引用 各种String ...

  8. java 常量区存放 new_java常量池与对象存储

    一 数据存储位置 我们先来谈谈数据的存储位置,有五个地方可以存储数据 (1)寄存器:这是最快的存储区,因为它位于不同于其他存储区的地方-处理器内部.对于程序员来说是透明的不能直接控制,并且数量有限. ...

  9. Java中栈、堆和常量池

    2019独角兽企业重金招聘Python工程师标准>>> Java内存分配主要包括以下几个区域: 寄存器 最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制.. 栈 存放基 ...

最新文章

  1. Android 中 include的使用
  2. linux yum配置文件 yum.conf 简介
  3. java 线程 wait 一定要同步_Java 线程中调用wait为什么一定要在同步代码块中?
  4. Swing中常用的方法
  5. 硬链接与软链接的区别
  6. 宝塔面板 mongodb 允许外网访问
  7. Solr router 路由介绍
  8. const与指针变量
  9. linux自学笔记--vim和文本三剑客基础
  10. 动态加载js文件是异步的
  11. 苹果6发布时间_iPhone12promax11月6日几点预售 11.6苹果12mini预售时间
  12. nginx: [emerg] mkdir() /var/temp/nginx/client failed (2: No such file or directory)
  13. 【VBA】excel多功能数据处理插件
  14. 保存为UTF8的1byte文字,2byte文字,3byte文字,4byte文字大全
  15. hp 816、817 墨盒计数器清零图文教程
  16. python能做什么工作知乎-python能做什么知乎
  17. java公司年会抽奖流程图文件流_年会抽奖程序的一些总结
  18. JEECG3.8 全套实战视频全部开放,免费下载!
  19. 完美融入云原生的无代码平台 iVX编辑器实战
  20. Web与排版学上的字体问题【转】

热门文章

  1. 海量数据处理简要说明(一)
  2. 真的不再使用*狗输入法了,改七七拼音
  3. 详细介绍MPLS路由协议的相关概念知识
  4. 一个maven错误:org/apache/maven/shared/filtering/MavenFilteringException
  5. 对于新安装的MySQL如何提升MySQL的安全级别
  6. window7 已经分好区的硬盘如何再次分区?
  7. 2019最新k8s集群搭建教程 (centos k8s 搭建)
  8. 金三银四,那浏览器兼容你知多少?
  9. 宝塔Linux 8888 进不去
  10. 奇异值分解与低秩矩阵近似