Java浅谈Java String内幕

 ​

博客主页:     南来_北往

系列专栏:Spring Boot实战


前言

Java中的String类是一个不可变的、用于表示字符串的类。在Java中,字符串是通过字符数组来实现的,而String类则是对这个字符数组进行封装,并提供了一系列操作字符串的方法。

  1. 不可变性:String类的实例是不可变的,这意味着一旦创建了一个String对象,它的值就不能被改变。如果需要修改字符串,实际上是创建了一个新的String对象。这种设计使得字符串在多线程环境下是安全的,因为不需要担心一个线程修改字符串会影响到其他线程。

  2. 字符串常量池:为了提高性能和节省内存,Java引入了字符串常量池(String Pool)。当我们使用双引号创建一个字符串时,Java会首先检查字符串常量池中是否已经存在这个字符串。如果存在,就直接返回这个字符串的引用;如果不存在,就在堆内存中创建一个新的String对象,并将其引用添加到字符串常量池中。这样,相同的字符串只会在内存中存储一份,节省了内存空间。

  3. ==equals()方法:由于字符串的不可变性,我们可以使用==运算符来比较两个字符串对象的引用是否相同。但是,如果我们想要比较两个字符串的内容是否相同,就需要使用equals()方法。equals()方法会比较两个字符串的每个字符,只有当所有字符都相同时,才会返回true

  4. intern()方法:Java提供了一个intern()方法,用于将字符串对象的引用添加到字符串常量池中。如果字符串常量池中已经存在相同的字符串,intern()方法会返回已有字符串的引用;否则,会在字符串常量池中添加新的字符串,并返回其引用。这样,我们可以确保字符串常量池中只有一个字符串的引用。

  5. substring()方法:substring()方法用于截取字符串的一部分,并返回一个新的String对象。需要注意的是,由于字符串的不可变性,substring()方法实际上会创建一个新的String对象,而不是直接修改原字符串。

  6. hashCode()方法:hashCode()方法用于计算字符串的哈希码。在Java中,String类的hashCode()方法是根据字符串的内容计算的,不同的字符串可能会有不同的哈希码。这使得我们可以使用哈希表(如HashMap)来存储和查找字符串。

常量池

Java代码被编译成class文件时,会生成一个常量池(Constant pool)的数据结构,用以保存字面常量和符号引用(类名、方法名、接口名和字段名等)。

package com.ctrip.ttd.whywhy;
public class Test {  public static void main(String[] args) {  String test = "test";  }  
}

很简单的一段代码,通过命令 javap -verbose 查看class文件中 Constant pool 实现: 

Constant pool:#1 = Methodref          #4.#13         // java/lang/Object."<init>":()V#2 = String             #14            // test#3 = Class              #15            // com/ctrip/ttd/whywhy/test#4 = Class              #16            // java/lang/Object#5 = Utf8               <init>#6 = Utf8               ()V#7 = Utf8               Code#8 = Utf8               LineNumberTable#9 = Utf8               main#10 = Utf8               ([Ljava/lang/String;)V#11 = Utf8               SourceFile#12 = Utf8               test.java#13 = NameAndType        #5:#6          // "<init>":()V#14 = Utf8               test#15 = Utf8               com/ctrip/ttd/whywhy/test#16 = Utf8               java/lang/Object

通过反编译出来的字节码可以看出字符串 "test" 在常量池中的定义方式:

# 2 = String             #14            // test
# 14 = Utf8              test

 在main方法字节码指令中,0 ~ 2行对应代码 String test = "test"; 由两部分组成:ldc #2 和 astore_1。

// main方法字节码指令
public static void main(java.lang.String[]);Code:0: ldc           #2                  // String test2: astore_13: return

1、Test类加载到虚拟机时,"test"字符串在Constant pool中使用符号引用symbol表示,当调用 ldc #2 指令时,如果Constant pool中索引 #2 的symbol还未解析,则调用C++底层的 StringTable::intern 方法生成char数组,并将引用保存在StringTable和常量池中,当下次调用 ldc #2 时,可以直接从Constant pool根据索引 #2获取 "test" 字符串的引用,避免再次到StringTable中查找。

2、astore_1指令将"test"字符串的引用保存在局部变量表中。

常量池的内存分配 在 JDK6、7、8中有不同的实现:

  • JDK6及之前版本中,常量池的内存在永久代PermGen进行分配,所以常量池会受到PermGen内存大小的限制。

  • JDK7中,常量池的内存在Java堆上进行分配,意味着常量池不受固定大小的限制了。

  • JDK8中,虚拟机团队移除了永久代PermGen。

字符串初始化

字符串可以通过两种方式进行初始化:字面常量和String对象。

字面常量

public class StringTest {public static void main(String[] args) {String a = "java";String b = "java";String c = "ja" + "va";}
}

 通过 "javap -c" 命令查看字节码指令实现:

其中ldc指令将int、float和String类型的常量值从常量池中推送到栈顶,所以a和b都指向常量池的"java"字符串。通过指令实现可以发现:变量a、b和c都指向常量池的 "java" 字符串,表达式 "ja" + "va" 在编译期间会把结果值"java"直接赋值给c。

String对象

public class StringTest {public static void main(String[] args) {String a = "java";String c = new String("java");}
}

 这种情况下,a == c 成立么?字节码实现如下:

其中3 ~ 9行指令对应代码 String c = new String("java"); 实现:

  • 第3行new指令,在Java堆上为String对象申请内存;

  • 第7行ldc指令,尝试从常量池中获取"java"字符串,如果常量池中不存在,则在常量池中新建"java"字符串,并返回;

  • 第9行invokespecial指令,调用构造方法,初始化String对象。

其中String对象中使用char数组存储字符串,变量a指向常量池的"java"字符串,变量c指向Java堆的String对象,且该对象的char数组指向常量池的"java"字符串,所以很显然 a != c,如下图所示:

 通过 "字面量 + String对象" 进行赋值会发生什么?

public class StringTest {public static void main(String[] args) {String a = "hello ";String b = "world";String c = a + b;String d = "hello world";}
}

 这种情况下,c == d成立么?字节码实现如下:

其中6 ~ 21行指令对应代码 String c = a + b; 实现:

  • 第6行new指令,在Java堆上为StringBuilder对象申请内存;

  • 第10行invokespecial指令,调用构造方法,初始化StringBuilder对象;

  • 第14、18行invokespecial指令,调用append方法,添加a和b字符串;

  • 第21行invokespecial指令,调用toString方法,生成String对象。

通过指令实现可以发现,字符串变量的连接动作,在编译阶段会被转化成StringBuilder的append操作,变量c最终指向Java堆上新建String对象,变量d指向常量池的"hello world"字符串,所以 c != d。

不过有种特殊情况,当final修饰的变量发生连接动作时,虚拟机会进行优化,将表达式结果直接赋值给目标变量:

public class StringTest {public static void main(String[] args) {final String a = "hello ";final String b = "world";String c = a + b;String d = "hello world";}
}

指令实现如下:

 总结

Java中的String类提供了丰富的方法和功能,使得我们可以方便地操作和处理字符串。了解String类的内幕有助于我们更好地理解Java语言,并编写出更高效、更安全的代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/148330.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

c++ 类中特殊成员函数

作业&#xff1a; 仿照string类&#xff0c;自己手动实现 My_string&#xff0c;分文件编译 fun.h代码 #ifndef FUN_H #define FUN_H#include <iostream>using namespace std;class My_string { private:char *ptr; // 指向字符数组的指针int size; // 字符串的最大…

计算机毕业设计推荐-基于python的游戏资讯分享平台

精彩专栏推荐订阅&#xff1a;在下方主页&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f496;&#x1f525;作者主页&#xff1a;计算机毕设木哥&#x1f525; &#x1f496; 文章目录 一、基于python的…

2024重生之回溯数据结构与算法系列学习(4)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】

目录 数据结构王道第2.3章节之线性表精题汇总一 &#xff08;10&#xff09;题目:​编辑 解题思路&#xff1a; 实现代码&#xff1a; &#xff08;11&#xff09;题目&#xff1a; 解题思路&#xff1a; &#xff08;12&#xff09;题目&#xff1a; 解题思路&#xff1a; 实…

Set 和 Map 的模拟实现

1、引言 在数据结构与算法的学习与实践中&#xff0c;关联容器&#xff08;associative containers&#xff09;是不可忽视的重要工具。作为高效管理数据的一类容器&#xff0c;C 标准库中的 set 和 map 在现代软件开发中扮演着关键角色。这两个容器通过平衡二叉搜索树&#x…

软件测试常见面试题

目录 1、什么是测试用例? 2、什么是BUG?(BUG的生命周期) 3、软件开发五大模型 4、软件测试的生命周期 5、测试模型(V模型、W模型) 6、软件开发的生命周期 7、如何描述一个BUG? 8、BUG的级别(线上出现崩溃级别的BUG怎么办) 9、 BUG的生命周期 10、发现一个BUG…

Qt系统相关——QFile和QFileInfo

文章目录 文件操作QFile使用示例QFileInfo 文件操作 C语言&#xff1a; fopen打开文件fread、fwrite读写文件fclose关闭文件 C&#xff1a; fstream打开文件<<、>>流式操作符读写 Linux&#xff1a; open打开文件read、write读写文件close关闭文件 Qt自己也…

把任务管理器里面的vmware usb arbitrition停了,虚拟机一直识别不到手机设备了

在设备管理器--服务 里面找到VMware usb arbitrition服务&#xff0c;点击“启用”就好了。 参考大佬的文章&#xff1a; 吐血经验&#xff01;&#xff01;&#xff01;解决虚拟机连不上USB&#xff01;最全&#xff01;_为什么vmware虚拟机不能连接上usb设备-CSDN博客

地平线静态目标检测 MapTR 参考算法-V1.0

简介 高清地图是自动驾驶系统的重要组件&#xff0c;提供精确的驾驶环境信息和道路语义信息。传统离线地图构建方法成本高&#xff0c;维护复杂&#xff0c;使得依赖车载传感器的实时感知建图成为新趋势。早期实时建图方法存在局限性&#xff0c;如处理复杂地图元素的能力不足…

品牌互鉴,融通中外|AORO遨游创始人受邀参加2024北京国际品牌周

2024年9月21日&#xff0c;第三届北京国际品牌周在北京金融街英蓝国际金融中心举办&#xff0c;本次盛会以“品牌互鉴&#xff0c;融通中外”为主题&#xff0c;由中国国际商会、中国国际公共关系协会、中国文化管理协会、北京国际设计周主办&#xff0c;中国外文局文化传播中心…

LSM-YOLO: A Compact and Effective ROI Detector for Medical Detection

LSM-YOLO: A Compact and Effective ROI Detector for Medical Detection LSM-YOLO: 一种紧凑且有效的医学检测ROI检测器1.介绍2.相关工作2.1医学感兴趣区域&#xff08;ROI&#xff09;检测2.2多尺度特征用于对象检测 3方法3.1LAE(轻量级自适应提取&#xff09;3.2MSFM(多路径…

GESP等级考试C++二级-switch...case的用法

在《GESP等级考试C二级if语句》中提到&#xff0c;可以使用if...else if...else语句来处理多种可能的情况。在C中&#xff0c;使用switch...case语句也可以来处理多种可能。 1 switch...case语句的格式 switch...case语句的格式如图1所示。 图1 switch...case语句的格式 其中…

鸿蒙开发(NEXT/API 12)【基础功能(使用剪贴板进行复制粘贴)】剪贴板服务

场景介绍 [剪贴板]为开发者提供数据的复制粘贴能力。 当需要使用复制粘贴等功能时&#xff0c;例如&#xff1a;复制文字内容到备忘录中粘贴&#xff0c;复制图库照片到文件管理粘贴&#xff0c;就可以通过剪贴板来完成。 约束限制 剪贴板内容大小<128MB。为保证剪贴板数…

【TabBar嵌套Navigation案例-产品推荐页面-UICollectionView-结合xib使用 Objective-C语言】

一、接下来,我们来说这个产品推荐页面 1.首先呢,它是一个CollectionViewController,当我点击这个产品推荐的时候, 这个Cell的时候,我要跳到一个CollectionViewController, 所以呢,我们需要先找到产品推荐,然后给它去添加一个targetVC,然后给它push到一个产品推荐的页面…

AI大模型的前十岗位薪资,谁还说读书没用?零基础入门到精通,收藏这一篇就够了

1. AI系统架构师 薪资范围&#xff1a;100万 - 200万/年 职位要求&#xff1a;需要具备全面的技术背景&#xff0c;精通系统架构设计&#xff0c;能够有效整合AI技术&#xff0c;提升系统性能。要求硕士及以上学历&#xff0c;计算机科学或相关专业背景。 目标院校&#xff1…

使用Conda配置python环境到Pycharm------Window小白版

使用Conda配置python环境到Pycharm 一、Conda安装和环境配置1.1 安装Conda软件1.2 判断是否安装成功1.3 创建Conda虚拟环境 二、 pycharm的安装2.1 Pycharm使用手册2.2 安装pycharm 三、 pycharm导入Conda环境 一、Conda安装和环境配置 anaconda官网 1.1 安装Conda软件 运行…

如何装修阿里巴巴国内1688平台淡入淡出效果首页特效1688店铺装修模板旺铺装修阿里店铺首页怎么装修全阿里

1688运营1688批发首页1688装修模板1688店铺怎么装修模板自定义装修代码1688店铺装修模板旺铺装修阿里店铺首页怎么装修1688店铺装修教程视频全屏通栏代码1688店铺装修模板阿里巴巴店铺装修设计 如何装修阿里巴巴国内1688平台淡入淡出效果首页特效1688店铺装修模板旺铺装修阿里店…

Actions Speak Louder than Words Meta史诗级的端到端推荐大模型落地

发现好久之前整理的推荐系统被遗忘在了草稿箱&#xff0c;让它出来见见世面。。。后续空了持续更新 1.Background 大模型生成用于推荐场景有如下几个难点&#xff1a; 特征缺乏显式结构。存在sparse和dense特征&#xff0c;其中sparse特征指的是一些离散特征&#xff0c;这部…

不再错过任何一个区块!用Node.js + WebSocket轻松实现区块链实时监控

文章目录 前言一、WebSocket是什么&#xff1f;二、项目结构三、代码实现1. 后端实现2. 前端实现 四、启动项目总结 前言 随着区块链技术的发展&#xff0c;实时监控区块链网络中的区块和交易信息变得越来越重要。无论是开发去中心化应用&#xff08;DApp&#xff09;&#xf…

shell脚本(2)

作业&#xff1a; 1.统计家目录下.c文件个数 #!/bin/bash num0 for file in ls ~/*.c do((num)) done echo "家目录中.c文件数:$num" 2定义一个稀疏数组&#xff08;下标不连续&#xff09;&#xff0c;写一个函数求稀疏数组中的和&#xff0c;要求稀疏数组中的数…

p18 docker镜像原理之联合文件系统,p19 docker镜像分层的理解

镜像是什么 镜像其实就是一种轻量级的&#xff0c;可执行的一种软件包&#xff0c;用来打包基于环境开发的软件&#xff0c;里面可以包括代码&#xff0c;环境&#xff0c;数据库&#xff0c;配置文件等信息 如何得到镜像&#xff1f; 可以从镜像仓库下载比方说dockerhub 比…