本篇目录

  • 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、 字符串拼接操作

  1. 常量与常量的拼接结果在常量池,原理是编译期优化
  2. 常量池中不会存在相同内容的常量。
  3. 只要其中有一个是变量,结果就在堆中。变量拼接的原理是StringBuilder
  4. 如果拼接的结果调用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 你真的了解么相关推荐

  1. 字符串常量池(StringTable)

    1,String的基本特性 String:字符串,使用一对""引起来表示 String a = "avc";//字面量定义 String b = new Str ...

  2. JVM---StringTable(字符串常量池)

    StringTable(字符串常量池) 字符串常量池 StringTable(字符串常量池) String的基本特性 String 的底层结构 String 的内存分配 String 的基本操作 字符 ...

  3. JVM之(执行引擎、字符串常量池、垃圾回收)-总结

    JVM之(执行引擎.性能监控.垃圾回收)-总结 如想了解更多更全面的Java必备内容可以阅读:所有JAVA必备知识点面试题文章目录: JAVA必备知识点面试题 注意: 本篇主要以HotSpot虚拟机为 ...

  4. JVM学习-StringTable字符串常量池

    StringTable 1.StringTable的特性 1.1.面试题 1.2.常量池与串池的关系 1.3.字符串的拼接 1.4.编译器优化 1.5.intern方法 1.5.1.intern方法( ...

  5. java string 常量池_用了这么久Java String,你真的懂字符串常量池吗?

    字符串问题可谓是 Java 中经久不衰的问题,尤其是字符串常量池经常作为面试题出现.可即便是看似简单而又经常被提起的问题,还是有好多同学一知半解,看上去懂了,仔细分析起来却又发现不太明白. 本文以 J ...

  6. Java字符串常量池详解(StringTable)

    前言:在介绍字符串常量池之前,我们先来简单了解下Java中字符串的概念以及常见的一些问题. 参考书籍: <Java核心技术> 参考网站:牛客 作者水平很有限,如果发现错误,麻烦及时告知作者 ...

  7. 字符串常量池,看这篇就够了(一)

    哈喽,我是子牙.十余年技术生涯,一路披荆斩棘从技术小白到技术总监到JVM专家到创业.技术栈如汇编.C语言.C++.Windows内核.Linux内核.特别喜欢研究虚拟机底层实现,对JVM有深入研究.分 ...

  8. Java中,那些关于String和字符串常量池你不得不知道的东西

    老套的笔试题 在一些老套的笔试题中,会要你判断s1==s2为false还是true,s1.equals(s2)为false还是true. String s1 = new String("xy ...

  9. 02.字符串常量池 ? class常量池? 运行时常量池?

    java对象创建流程: 简介: 这几天在看Java虚拟机方面的知识时,看到了有几种不同常量池的说法,然后我就去CSDN.博客园等上找资料,里面说的内容真是百花齐放,各自争艳,因此,我好好整理了一下,将 ...

最新文章

  1. weex componet 简单扩展
  2. hdu4496并查集的删边操作
  3. 基于遗传算法优化的BP神经网络的 非线性函数拟合
  4. oracle在linux上使用裸设备,在linux下构建基于LVM的裸设备数据库
  5. VB一个可以改变箭头方向的气泡提示
  6. jbpm与OA项目开发过程实录 (二)
  7. js基础-11-相等和全等的区别
  8. [OpenBMC] 从代码谈 Redfish 的Log 机制 (LogService/EventLog)
  9. 机器学习笔记最大熵之NER
  10. 项目管理第四章项目整合管理
  11. 命令查看计算机出厂时间,Check cosmetics or perfume production date and shelf life by the batch code....
  12. 数组unshift_数组unshift()方法以及JavaScript中的示例
  13. 冰蝎Behinder_v4.0
  14. obs linux编译,教你如何在 Ubuntu 18.04/16.04 中安装 OBS Studio 21.1
  15. 怎么更改计算机用户为管理员账户,Win10如何更改为管理员账户,教您如何更改
  16. 看完这些在HW面试官面前横着走 HW面试常见问题大合集(适合第一次参加)
  17. 一个普通程序员和他的猫
  18. SQLZOO selfjoin/zh——爱丁堡巴士
  19. 3.MyBatis源码解析-CRUD执行流程--阿呆中二
  20. html5页面打不开原因,有网但是网页打不开是什么原因(原因及2种解决方法)

热门文章

  1. Oracle EBS R12.1.1 应用克隆 - 2
  2. 函数声明、函数表达式、匿名函数、立即执行函数
  3. PHP 简单计算器代码实现
  4. 类型转换异常处理java.lang.RuntimeException
  5. open-falcon
  6. 答网友问题:职业化代码设计原则讨论
  7. (摘)Excel 2007查询操作中的函数应用
  8. 余敖的实验整理(还没完成)
  9. sqoop把mysql数据导入hive
  10. Spark的三种伪分布式方式