字符串在JVM中如何存放 及常量池技术
字符串对象在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中如何存放 及常量池技术相关推荐
- jvm中方法区和常量池详解_Java常量池(静态常量池与运行时常量池)
1.什么是常量 用final修饰的成员变量表示常量,值一旦给定就无法改变! final修饰的变量有三种:静态变量.实例变量和局部变量,分别表示三种类型的常量. Java中的常量池,实际上分为两种形态: ...
- jvm中方法区和常量池详解_JVM——内存区域:运行时数据区域详解
关注微信公众号:CodingTechWork,一起学习进步. 引言 我们经常会被问到一个问题是Java和C++有何区别?我们除了能回答一个是面向对象.一个是面向过程编程以外,我们还会从底层内存管理和垃 ...
- 蚂蚁面试:字符串在JVM中如何存放?
字符串对象在JVM中可能有两个存放的位置:字符串常量池或堆内存. 使用常量字符串初始化的字符串对象,它的值存放在字符串常量池中: 使用字符串构造方法创建的字符串对象,它的值存放在堆内存中: Strin ...
- 欧尼酱讲JVM(11)——动态链接和常量池
因为动态链接和运行时常量池有联系,所以先解释一下运行时常量池: 运行时常量池位于方法区(注意: JDK1.7 及之后版本的 JVM 已经将运行时常量池从方法区中移了出来,在 Java 堆(Heap)中 ...
- Integer关于equals与==的比较(自动拆装箱技术和常量池技术)
首先介绍下 equals方法遵循的规则:自反性,一致性,传递性,对称性,与null相比,返回false: 1.JAVA当中所有的类都是继承于Object这个基类的,在Object中的基类中定义了一个e ...
- JVM虚拟机-Class文件之常量池
一.常量池的作用 在class文件中的魔数.副版本号.主版本之后,紧接着就是常量池的数据区域了,如下图用红线包括的位置: 常量池可以比喻为Class文件里的资源仓库,它是Class 文件结构中与其他项 ...
- 读取字符串字符时出错_JVM | 运行时常量池和字符串常量池及intern()
本文知识点 这一块的知识点,一直都是最为混乱的,网上很多博客有的都自相矛盾,因此,这一块需要深入学习,本文以JDK11为基础,尽量参考官方文档 静态常量池 运行时常量池及字符串的引用 各种String ...
- java 常量区存放 new_java常量池与对象存储
一 数据存储位置 我们先来谈谈数据的存储位置,有五个地方可以存储数据 (1)寄存器:这是最快的存储区,因为它位于不同于其他存储区的地方-处理器内部.对于程序员来说是透明的不能直接控制,并且数量有限. ...
- Java中栈、堆和常量池
2019独角兽企业重金招聘Python工程师标准>>> Java内存分配主要包括以下几个区域: 寄存器 最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制.. 栈 存放基 ...
最新文章
- Android 中 include的使用
- linux yum配置文件 yum.conf 简介
- java 线程 wait 一定要同步_Java 线程中调用wait为什么一定要在同步代码块中?
- Swing中常用的方法
- 硬链接与软链接的区别
- 宝塔面板 mongodb 允许外网访问
- Solr router 路由介绍
- const与指针变量
- linux自学笔记--vim和文本三剑客基础
- 动态加载js文件是异步的
- 苹果6发布时间_iPhone12promax11月6日几点预售 11.6苹果12mini预售时间
- nginx: [emerg] mkdir() /var/temp/nginx/client failed (2: No such file or directory)
- 【VBA】excel多功能数据处理插件
- 保存为UTF8的1byte文字,2byte文字,3byte文字,4byte文字大全
- hp 816、817 墨盒计数器清零图文教程
- python能做什么工作知乎-python能做什么知乎
- java公司年会抽奖流程图文件流_年会抽奖程序的一些总结
- JEECG3.8 全套实战视频全部开放,免费下载!
- 完美融入云原生的无代码平台 iVX编辑器实战
- Web与排版学上的字体问题【转】