字符串常量池StringTable 你真的了解么
本篇目录
- 1、 String的基本特性
- 2、 String的内存分配
- 3、 字符串拼接操作
- 3.1 字符串拼接
- 3.2 拼接操作与append的效率对比
- 4、 intern()的使用
- 4.1 new String("ab")会创建几个对象,new String("a")+new String("b")呢
- 4.2 关于String.intern()的问题
- 4.3 总结String的intern()的使用
1、 String的基本特性
• String:字符串,使用一对""引起来表示。
• String sl = "hello";//字面量的定义方式
• String s2 = new String("hello") ;
• String声明为final的, 不可被继承
• String实现了Serializable接口:表示字符串是支持序列化的。 实现了Comparable接口:表示String可以比较大小
• String在jdk8及以前内部定义了final char[] value用于存储字符串数据。jdk9时改为byte[]
public final class String implements java.io.Serializable, Comparable<String>,CharSequence {@Stable
private final byte[] value;
}
结论: String再也不用char[] 来存储啦,改成了byte[] 加上编码标记,节约了一些空间。StringBuffer和StringBuilder也做了一些修改。
• String:代表不可变的字符序列。简称:不可变性。
- 当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。
- 当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
- 当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
• 通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
/*** String的基本使用:体现String的不可变性*/
public class StringTest1 {@Testpublic void test1() {String s1 = "abc";//字面量定义的方式,"abc"存储在字符串常量池中String s2 = "abc";s1 = "hello";System.out.println(s1 == s2);//判断地址:true System.out.println(s1);// helloSystem.out.println(s2);//abc}@Testpublic void test2() {String s1 = "abc";String s2 = "abc";s2 += "def";System.out.println(s2);//abcdefSystem.out.println(s1);//abc}@Testpublic void test3() {String s1 = "abc";String s2 = s1.replace('a', 'm');System.out.println(s1);//abcSystem.out.println(s2);//mbc}
}
• 字符串常量池中是不会存储相同内容的字符串的。
- String的String Pool 是一个固定大小的Hashtable,默认值大小长度是1009。如果放进StringPool的String非常多, 就会造成Hash冲突严重,从而导致链表会很长,而链表长了后直接会造成的影响就是当调用String. intern时性能会大幅下降。
- 使用一XX: StringTableSize可设置StringTable的长度
- 在jdk6中StringTable是固定的,就是1009的长度,所以如果常量池中的字符串过多就会导致效率下降很快。StringTableSize设 置没有要求
- 在jdk7中,StringTable的长度默认值是60013
- jdk8开始,1009是StringTable长度可设置的最小值
2、 String的内存分配
在Java语言中有8种基本数据类型和一种比较特殊的类型String。这些 类型为了使它们在运行过程中速度更快、更节省内存,都提供了一种常量池的概念。
常量池就类似一.个Java系统级别提供的缓存。8种基本数据类型的常量 池都是系统协调的,String类 型的常量池比较特殊。它的主要使用方法有两种。
- 直接使用双引号声明出来的String对象会直接存储在常量池中。 比如: String info = “abc” ;
- 如果不是用双引号声明的String对象,可以使用String提供的intern()方法。这个后面重点聊。
Java 6及以前,字符串常量池存放在永久代。
Java 7中Oracle的工程师对字符串池的逻辑做了很大的改变,即将字符串常量池的位置调整到Java堆内。
- 所有的字符串都保存在堆(Heap)中,和其他普通对象一样,这样可以让你在进行调优应用时仅需要调整堆大小就可以了。
- 字符串常量池概念原本使用得比较多,但是这个改动使得我们有足够的理由让我们重新考虑在Java 7中使用String. intern()。
Java8元空间,字符串常量在堆
StringTable为什么要调整?
①永久代permSize默认比较小;
②永久代的垃圾回收频率低;
3、 字符串拼接操作
- 常量与常量的拼接结果在常量池,原理是编译期优化
- 常量池中不会存在相同内容的常量。
- 只要其中有一个是变量,结果就在堆中。变量拼接的原理是StringBuilder
- 如果拼接的结果调用intern()方法,则主动将常量池中还没有的字符串对象放入池中,并返回此对象地址。
@Testpublic void test1(){String s1 = "a" + "b" + "c";//编译期优化:等同于"abc"String s2 = "abc"; //"abc"一定是放在字符串常量池中,将此地址赋给s2/** 最终.java编译成.class,再执行.class* String s1 = "abc";* String s2 = "abc"*/System.out.println(s1 == s2); //trueSystem.out.println(s1.equals(s2)); //true}@Testpublic void test2(){String s1 = "javaEE";String s2 = "hadoop";String s3 = "javaEEhadoop";String s4 = "javaEE" + "hadoop";//编译期优化//如果拼接符号的前后出现了变量,则相当于在堆空间中new String(),具体的内容为拼接的结果:javaEEhadoopString s5 = s1 + "hadoop";String s6 = "javaEE" + s2;String s7 = s1 + s2;System.out.println(s3 == s4);//trueSystem.out.println(s3 == s5);//falseSystem.out.println(s3 == s6);//falseSystem.out.println(s3 == s7);//falseSystem.out.println(s5 == s6);//falseSystem.out.println(s5 == s7);//falseSystem.out.println(s6 == s7);//false//intern():判断字符串常量池中是否存在javaEEhadoop值,如果存在,则返回常量池中javaEEhadoop的地址;//如果字符串常量池中不存在javaEEhadoop,则在常量池中加载一份javaEEhadoop,并返回次对象的地址。String s8 = s6.intern();System.out.println(s3 == s8);//true}
3.1 字符串拼接
@Testpublic void test3(){String s1 = "a";String s2 = "b";String s3 = "ab";/*如下的s1 + s2 的执行细节:(变量s是我临时定义的)① StringBuilder s = new StringBuilder();② s.append("a")③ s.append("b")④ s.toString() --> 约等于 new String("ab")补充:在jdk5.0之后使用的是StringBuilder,在jdk5.0之前使用的是StringBuffer*/String s4 = s1 + s2;//System.out.println(s3 == s4);//false}/*1. 字符串拼接操作不一定使用的是StringBuilder!如果拼接符号左右两边都是字符串常量或常量引用,则仍然使用编译期优化,即非StringBuilder的方式。2. 针对于final修饰类、方法、基本数据类型、引用数据类型变量的结构时,能使用上final的时候建议使用上。*/@Testpublic void test4(){final String s1 = "a";final String s2 = "b";String s3 = "ab";String s4 = s1 + s2;System.out.println(s3 == s4);//true}@Testpublic void test5(){String s1 = "javaEEhadoop";String s2 = "javaEE";String s3 = s2 + "hadoop";System.out.println(s1 == s3);//falsefinal String s4 = "javaEE";//s4:常量String s5 = s4 + "hadoop";System.out.println(s1 == s5);//true}
3.2 拼接操作与append的效率对比
append效率要比字符串拼接高很多!
/*体会执行效率:通过StringBuilder的append()的方式添加字符串的效率要远高于使用String的字符串拼接方式!详情:1、 StringBuilder的append()的方式:自始至终中只创建过一个StringBuilder的对象使用String的字符串拼接方式:创建过多个StringBuilder和String的对象2、 使用String的字符串拼接方式:内存中由于创建了较多的StringBuilder和String的对象,内存占用更大;如果进行GC,需要花费额外的时间。改进的空间:在实际开发中,如果基本确定要前前后后添加的字符串长度不高于某个限定值highLevel的情况下,建议使用构造器实例化:StringBuilder s = new StringBuilder(highLevel);//new char[highLevel]*/@Testpublic void test6(){long start = System.currentTimeMillis();
// method1(100000);//4014method2(100000);//7long end = System.currentTimeMillis();System.out.println("花费的时间为:" + (end - start));}public void method1(int highLevel){String src = "";for(int i = 0;i < highLevel;i++){src = src + "a";//每次循环都会创建一个StringBuilder、String}
// System.out.println(src);}public void method2(int highLevel){//只需要创建一个StringBuilderStringBuilder src = new StringBuilder();for (int i = 0; i < highLevel; i++) {src.append("a");}
// System.out.println(src);}
4、 intern()的使用
如果不是用双引号声明的String对象,可以使用String提供的intern方法: intern方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中。
比如:
String myInfo = new String("I love u").intern();
也就是说,如果在任意字符串上调用String. intern方法,那么其返回结果所指向的那个类实例,必须和直接以常量形式出现的字符串实例完全相同。因此,下 列表达式的值必定是true:
("a" + "b" + "c").intern()== "abc";
通俗点讲,Intern() String就是确保字符串在内存里只有一份拷贝,这样可以节约内存空间,加快字符串操作任务的执行速度。注意,这个值会被存放在字符串内部池(String Intern Pool)。
4.1 new String(“ab”)会创建几个对象,new String(“a”)+new String(“b”)呢
public class StringNewTest {public static void main(String[] args) {// String str = new String("ab");String str = new String("a") + new String("b");}
}
new String(“ab”)会创建几个对象?看字节码,就知道是两个。
- 一个对象是:new关键字在堆空间创建的
- 另一个对象是:字符串常量池中的对象"ab"。 字节码指令:ldc
new String(“a”) + new String(“b”)呢?
- 对象1:new StringBuilder()
- 对象2: new String(“a”)
- 对象3: 常量池中的"a"
- 对象4: new String(“b”)
- 对象5: 常量池中的"b"
- 4.22
4.2 关于String.intern()的问题
/*** 如何保证变量s指向的是字符串常量池中的数据呢?* 有两种方式:* 方式一: String s = "shkstart";//字面量定义的方式* 方式二: 调用intern()* String s = new String("shkstart").intern();* String s = new StringBuilder("shkstart").toString().intern();**/
public class StringIntern {public static void main(String[] args) {String s = new String("1");String s1 = s.intern();//调用此方法之前,字符串常量池中已经存在了"1"String s2 = "1";//s 指向堆空间"1"的内存地址//s1 指向字符串常量池中"1"的内存地址//s2 指向字符串常量池已存在的"1"的内存地址 所以 s1==s2System.out.println(s == s2);//jdk6:false jdk7/8:falseSystem.out.println(s1 == s2);//jdk6: true jdk7/8:trueSystem.out.println(System.identityHashCode(s));//491044090System.out.println(System.identityHashCode(s1));//644117698System.out.println(System.identityHashCode(s2));//644117698//s3变量记录的地址为:new String("11")String s3 = new String("1") + new String("1");//执行完上一行代码以后,字符串常量池中,是否存在"11"呢?答案:不存在!!//在字符串常量池中生成"11"。如何理解:jdk6:创建了一个新的对象"11",也就有新的地址。// jdk7:此时常量中并没有创建"11",而是创建一个指向堆空间中new String("11")的地址s3.intern();//s4变量记录的地址:使用的是上一行代码代码执行时,在常量池中生成的"11"的地址String s4 = "11";System.out.println(s3 == s4);//jdk6:false jdk7/8:true}
}
4.3 总结String的intern()的使用
- jdk1.6中,将这个字符串对象尝试放入串池。
- ➢如果字符串常量池中有,则并不会放入。返回已有的串池中的对象的地址
- ➢如果没有,会把此对象复制一份,放入串池,并返回串池中的对象地址
- Jdk1.7起,将这个字符串对象尝试放入串池。
- ➢如果字符串常量池中有,则并不会放入。返回已有的串池中的对象的地址
- ➢如果没有,则会把对象的引用地址复制一份,放入串池,并返回串池中的引用地址
字符串常量池StringTable 你真的了解么相关推荐
- 字符串常量池(StringTable)
1,String的基本特性 String:字符串,使用一对""引起来表示 String a = "avc";//字面量定义 String b = new Str ...
- JVM---StringTable(字符串常量池)
StringTable(字符串常量池) 字符串常量池 StringTable(字符串常量池) String的基本特性 String 的底层结构 String 的内存分配 String 的基本操作 字符 ...
- JVM之(执行引擎、字符串常量池、垃圾回收)-总结
JVM之(执行引擎.性能监控.垃圾回收)-总结 如想了解更多更全面的Java必备内容可以阅读:所有JAVA必备知识点面试题文章目录: JAVA必备知识点面试题 注意: 本篇主要以HotSpot虚拟机为 ...
- JVM学习-StringTable字符串常量池
StringTable 1.StringTable的特性 1.1.面试题 1.2.常量池与串池的关系 1.3.字符串的拼接 1.4.编译器优化 1.5.intern方法 1.5.1.intern方法( ...
- java string 常量池_用了这么久Java String,你真的懂字符串常量池吗?
字符串问题可谓是 Java 中经久不衰的问题,尤其是字符串常量池经常作为面试题出现.可即便是看似简单而又经常被提起的问题,还是有好多同学一知半解,看上去懂了,仔细分析起来却又发现不太明白. 本文以 J ...
- Java字符串常量池详解(StringTable)
前言:在介绍字符串常量池之前,我们先来简单了解下Java中字符串的概念以及常见的一些问题. 参考书籍: <Java核心技术> 参考网站:牛客 作者水平很有限,如果发现错误,麻烦及时告知作者 ...
- 字符串常量池,看这篇就够了(一)
哈喽,我是子牙.十余年技术生涯,一路披荆斩棘从技术小白到技术总监到JVM专家到创业.技术栈如汇编.C语言.C++.Windows内核.Linux内核.特别喜欢研究虚拟机底层实现,对JVM有深入研究.分 ...
- Java中,那些关于String和字符串常量池你不得不知道的东西
老套的笔试题 在一些老套的笔试题中,会要你判断s1==s2为false还是true,s1.equals(s2)为false还是true. String s1 = new String("xy ...
- 02.字符串常量池 ? class常量池? 运行时常量池?
java对象创建流程: 简介: 这几天在看Java虚拟机方面的知识时,看到了有几种不同常量池的说法,然后我就去CSDN.博客园等上找资料,里面说的内容真是百花齐放,各自争艳,因此,我好好整理了一下,将 ...
最新文章
- weex componet 简单扩展
- hdu4496并查集的删边操作
- 基于遗传算法优化的BP神经网络的 非线性函数拟合
- oracle在linux上使用裸设备,在linux下构建基于LVM的裸设备数据库
- VB一个可以改变箭头方向的气泡提示
- jbpm与OA项目开发过程实录 (二)
- js基础-11-相等和全等的区别
- [OpenBMC] 从代码谈 Redfish 的Log 机制 (LogService/EventLog)
- 机器学习笔记最大熵之NER
- 项目管理第四章项目整合管理
- 命令查看计算机出厂时间,Check cosmetics or perfume production date and shelf life by the batch code....
- 数组unshift_数组unshift()方法以及JavaScript中的示例
- 冰蝎Behinder_v4.0
- obs linux编译,教你如何在 Ubuntu 18.04/16.04 中安装 OBS Studio 21.1
- 怎么更改计算机用户为管理员账户,Win10如何更改为管理员账户,教您如何更改
- 看完这些在HW面试官面前横着走 HW面试常见问题大合集(适合第一次参加)
- 一个普通程序员和他的猫
- SQLZOO selfjoin/zh——爱丁堡巴士
- 3.MyBatis源码解析-CRUD执行流程--阿呆中二
- html5页面打不开原因,有网但是网页打不开是什么原因(原因及2种解决方法)