什么是字符串常量池_常量池中的字符串是对象吗

什么是字符串常量池_常量池中的字符串是对象吗关于字符串与字符串常量池JDK1.8-1.9,String底层从char数组变成了byte数组,原因是部分字符仅占一个byte,而堆中含有大量的String字符串,该优化能节省较多空间。StringTable为什么要调整(移入堆内)(JDK1.6-1.7)permSize默认比较小永久代垃圾回收频率低字符串拼接操作常量与常量的拼接结果在常量池,原理是编译器优化常量池中不会存在相同内容的常量只要其中一个是变量,结果就在堆中。变量拼接的原理是StringBuilder(final不算变量)

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

JDK1.8-1.9,String底层从char数组变成了byte数组,原因是部分字符仅占一个byte,而堆中含有大量的String字符串,该优化能节省较多空间。

关于常量池

常量池/Class常量池(Constant pool)

常量池,也叫 Class 常量池(常量池==Class常量池)。Java文件被编译成 Class文件,Class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项就是常量池,常量池是当Class文件被Java虚拟机加载进来后存放在方法区 各种字面量 (Literal)和 符号引用 。
在这里插入图片描述

运行时常量池(Runtime constant pool)

运行时常量池是方法区的一部分。运行时常量池是当Class文件被加载到内存后,Java虚拟机会 将Class文件常量池里的内容转移到运行时常量池里(运行时常量池也是每个类都有一个)。运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中

只存放字符串引用

在这里插入图片描述

字符串常量池(String pool/String table)

字符串常量池又称为:字符串池,全局字符串池,英文也叫String Pool。 在工作中,String类是我们使用频率非常高的一种对象类型。JVM为了提升性能和减少内存开销,避免字符串的重复创建,其维护了一块特殊的内存空间:字符串常量池。字符串常量池由String类私有的维护。

堆里边的字符串常量池存放的是字符串的引用或者字符串(两者都有)

比如new String(“test”)就会先在常量池中检查是否存在,不存在则在常量池中创建,然后堆中创建其引用。

常量池和字符串常量池的版本变化

  • 在JDK1.7之前运行时常量池逻辑包含字符串常量池存放在方法区, 此时hotspot虚拟机对方法区的实现为永久代
  • 在JDK1.7 字符串常量池、静态变量等被从方法区拿到了堆中, 这里没有提到运行时常量池,也就是说 字符串常量池被单独拿到堆,运行时常量池剩下的东西还在方法区, 也就是hotspot中的永久代
  • 在JDK1.8 hotspot移除了永久代用元空间(Metaspace)取而代之, 这时候字符串常量池还在堆, 运行时常量池还在方法区, 只不过方法区的实现从永久代变成了元空间(Metaspace)
  • 补充:
    • 对象只能存在于堆中(虚拟机规范的定义),所以String实体只能存在于堆中,
    • 运行时常量池存放的是字面量引用
    • 使用双引号方式显式声明的字符串,则直接放入字符串常量池中(final修饰的“变量”可以直接看作双引号字面量)

StringTable为什么要调整(1.6-1.7)

  • permSize默认比较小
  • 永久代垃圾回收频率低

字符串拼接操作

  • 常量与常量的拼接结果在常量池,原理是编译器优化
  • 常量池中不会存在相同内容的常量
  • 只要其中一个是变量,结果就在堆中。变量拼接的原理是StringBuilder(final不算变量),返回String对象
  • 如果拼接的结果调用intern()方法,则注定将常量池中还没有的字符串对象放入池中,并返回此对象地址

所以建议多使用final定义字符串,并且不使用new,对于new String()声明final不会优化

示例

    public static boolean IsInPool(String str){ 
   
        return str == str.intern();
    }
    public static void main(String[] args) { 
   
        final String a1 = "a";
        final String a2 = "b";
        String a3 = a1 + a2;
        String limit1 = "ab";

        String a4 = "c";
        String a5 = "d";
        String a6 = a4 + a5;
        String limit2 = "cd";

        String b1 = new String("e");
        String b2 = new String("f");
        String b3 = b1 + b2;
        String limit3 = "ef";

        final String b4 = new String("g");
        final String b5 = new String("h");
        String b6 = b4 + b5;
        final String limit4 = "gh";

        System.out.println(IsInPool(a3));//true
        System.out.println(IsInPool(a6));//false
        System.out.println(IsInPool(b3));//false
        System.out.println(IsInPool(b6));//false

image-20211008214506729

字符串的创建与常量池

String两种创建方式

  • 方式一(str值和字符串常量池中字面量地址相等)
String str = "abc"
  1. 检查字符串常量池是否存在该字符串,存在则不创建并且返回该字符串的引用
  2. 不存在则在字符串常量池中创建该字符串常量并返回其常量池中地址
  • 方式二(str值和字符串常量池中字面量地址不相等)
String str = new String("abc")
  1. 检查字符串常量池是否存在该字符串,存在则不创建,不存在则创建该字符串常量
  2. 在堆中创建该对象
  3. 返回堆中该对象的引用

普遍地

使用双引号方式显式存在的字符串,则直接放入字符串常量池中(final修饰的“变量”可以直接看作双引号字面量)

一些测试(JDK1.8)

情况一:

    public static void main(String[] args) { 
   
        String a1 = "a";
        String a2 = new String("b");
        String a3 = "c";
        String a4 = a1 + a2;
        String a5 = a1 + a3;
// String at = "a"+"c";
// String at1 = "a"+"b";

        System.out.println(IsInPool(a1));//true
        System.out.println(IsInPool("b"));//true
        System.out.println(IsInPool(a2));//false
        System.out.println(IsInPool(a3));//true

        System.out.println(IsInPool(a4));//true
        System.out.println(IsInPool("ab"));//true
        System.out.println(IsInPool(a5));//true
        System.out.println(IsInPool("ac"));//true
    }

情况二:

    public static void main(String[] args) { 
   
        String a1 = "a";
        String a2 = new String("b");
        String a3 = "c";
        String a4 = a1 + a2;
        String a5 = a1 + a3;
        String at = "a"+"c";
        String at1 = "a"+"b";

        System.out.println(IsInPool(a1));//true
        System.out.println(IsInPool("b"));//true
        System.out.println(IsInPool(a2));//false
        System.out.println(IsInPool(a3));//true

        System.out.println(IsInPool(a4));//false
        System.out.println(IsInPool("ab"));//true
        System.out.println(IsInPool(a5));//false
        System.out.println(IsInPool("ac"));//t
    }

IsInpool函数

    //使用这个函数需要在这个函数前使用双引号字面量使得字符串直接被加入常量池中
	//使用顺序应为待测字符串->字面量->IsInPool

	public static boolean IsInPool(String str){ 
   
        return str == str.intern();
    }

对于intern函数的理解

调用这个方法之后就是去看当前字符串是否在字符串常量池中已经存在引用

(1)存 在:那就直接返回该字符串在字符串常量池中所对应的地址给栈中要引用这个字符串的变量。

(2)不存在:
① jdk 1.6:先在字符串常量池中创建该字符串,地址与堆中字符串地址不相同。然后再返回刚创建的字符串在字符串常量池中所对应的地址给栈中要引用这个字符串的变量。

② jdk 1.7及以后:直接将堆中(不是字符串常量池中)该字符串的地址复制到字符串常量池中,这样字符串常量池就有了该字符串的地址引用,也可以说此时字符串常量池中的字符串只是一个对 堆中字符串对象的引用,它们两个的地址相同,然后再把这个地址返回给栈中要引用这个字符串的变量。

对测试的解释

  • 第一次两个拼接测试为true,因为intern函数将堆中字符串对象引用复制到字符串常量池中,所以二者自然相等
  • 第二次两个拼接测试为false,因为intern检查到已经存在该字符常量,且堆常量池中保存的是字符串的值,二者自然不相等
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/164773.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 关于java的垃圾回收机制,下面哪些结论_java垃圾回收算法有哪些

    关于java的垃圾回收机制,下面哪些结论_java垃圾回收算法有哪些本篇文章介绍了Java的垃圾回收机制、引用类型、JVM一次完整的GC流程、垃圾回收算法以及经典的垃圾回收器

    2022年8月31日
  • 探秘X86架构CPU流水线[通俗易懂]

    探秘X86架构CPU流水线[通俗易懂]http://ee.ofweek.com/2013-07/ART-11001-2805-28704745.html导读:CPU是如何工作的呢?一条指令执行需要多长时间?当我们讨论某个新款处理器拥有12级流水线还是18级流水线,甚至是更深的31级流水线时,这到些都意味着什么呢?作为程序员,CPU在我们的工作中扮演了核心角色,因此了解处理器内部的工作方式对程序员来说不无裨益。  CPU…

    2022年8月20日
  • redis过期时间实现原理_redis过期时间实现原理

    redis过期时间实现原理_redis过期时间实现原理一、有效时间设置:redis对存储值的过期处理实际上是针对该值的键(key)处理的,即时间的设置也是设置key的有效时间。Expires字典保存了所有键的过期时间,Expires也被称为过期字段。 四种处理策略EXPIRE将key的生存时间设置为ttl秒PEXPIRE将key的生成时间设置为ttl毫秒EXPIREAT将key的过期时间设置为timestamp所代表的的秒数的时间戳PEXPIR…

    2022年9月26日
  • linux下tomcat安装及配置教程

    linux下tomcat安装及配置教程tomcat是一款java语言servlet规范的服务器软件,如下的经验讲述如何使用linux环境下tomcat的安装和配置。工具/原料Linuxtomcat方法/步骤1下载tomcat压缩包如第一张图如果你是服务器linux,可以通过软件本地上传文件导linux服务器如图二也可也通过下面linux命令下载:2新建目录,并解压Tomcat软件的压缩包命令:tar…

    2022年5月31日
  • JAVA API中文在线帮助文档

    JAVA API中文在线帮助文档https://www.w3cschool.cn/java/dicthttp://www.matools.com/api/java8http://tool.oschina.net/apidocs/apihttp://www.javaweb.cc/help/JavaAPI1.6/overview-summary.html

    2022年7月26日
  • iPhone使用教程_iphone基础使用

    iPhone使用教程_iphone基础使用iPhone史上最全的使用教程iPhone的解锁、越狱、激活、固件等等是什么意思,有什么分别这几天看见好多新人问这几个词的含义及区别。我在这儿说说我的看法,不是官方解释,不懂的学习一下,懂的绕道,如有错误,敬请指正!第一次买来时或恢复官方固件后,iPhone会处于那种只能拨打紧急电话状态,不能使用其它功能,如果要使用其它功能,就必须进行一项操作,那就是“激活”。一般有锁版的只有使…

    2022年9月15日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号