揭秘:Java字符串对象的内存分布原理(二)

接上篇揭秘:Java字符串对象的内存分布原理

再看看下面几道关于String的真实面试题,看看你废不废?

在这里插入图片描述

String str1 = "Hello";
String str2 = "He" + "llo";
String str3 = "He";
String str4 = "llo";
String str5 = str3 + str4;
String str6 = str3 + "llo";System.out.println(str1 == str2); // true or false?
System.out.println(str1.equals(str5)); // true or false?
System.out.println(str1 == str5); // true or false?
System.out.println(str1.equals(str5)); // true or false?
System.out.println(str1 == str6); // true or false?

public class StringExercise07 {public static void main(String[] args) {Test1 ex = new Test1();ex.change(ex.str, ex.ch);// 输出结果是?System.out.println(ex.str + " and ");System.out.println(ex.ch);}
}class Test1 {String str = new String("ly");final char[] ch = {'j', 'a', 'v', 'a'};public void change(String str, char ch[]) {str = "java";ch[0] = 'h';}
}

④比较下面两种字符串拼接的差异

// 第一种拼接:使用+操作符
String strPlus = "";
for (int i = 0; i < 1000; i++) {strPlus += "Hello, World!";
}// 第二种拼接:使用StringBuilder
StringBuilder strBuilder = new StringBuilder();
for (int i = 0; i < 1000; i++) {strBuilder.append("Hello, World!");
}
String result = strBuilder.toString();

如果要正确回答上面几个面试题,不深入理解String类型的内存原理是不行的,本篇的唯一目的就是言简意赅、深入浅出的梳理String类型的底层原理。

一,字符串拼接

下面这道面试题考察的是字符串拼接的底层原理,字符串拼接可以分为两种:

  • ①字面量拼接,如String str2 = "He" + "llo";,拼接的所有部分都是字面量,对于这种拼接,Java在编译时就会优化,将其合并为一个字符串:
// 源码:
String str2 = "He" + "llo";// 编译优化后:
String str2 = "Hello";
  • ②变量拼接,String str5 = str3 + str4;String str6 = str3 + "llo";,只要有一个变量参与,就是变量拼接。对于这种拼接,JVM底层会调用StringBuilderappend方法进行拼接,拼接完成后调用toString方法,toString方法中使用构造函数new String()创建新的字符串对象

小结:

  • 纯字面量拼接相当于用字面量声明一个字符串对象,字符串对象会保存到字符串常量池
  • 变量拼接的相当于通过构造函数声明一个字符串对象,不会存入字符串常量池

所以,对于如下面试题:

String str1 = "Hello";
String str2 = "He" + "llo";
String str3 = "He";
String str4 = "llo";
String str5 = str3 + str4;
String str6 = str3 + "llo";System.out.println(str1 == str2); // true or false?
System.out.println(str1.equals(str5)); // true or false?
System.out.println(str1 == str5); // true or false?
System.out.println(str1.equals(str5)); // true or false?
System.out.println(str1 == str6); // true or false?
字面量拼接:
String str1 = "Hello";
String str2 = "He" + "llo";
System.out.println(str1 == str2); // true or false?
System.out.println(str1.equals(str5)); // true or false?

因为编译器的优化,代码String str2 = "He" + "llo";相当于String str2 = "Hello";,类似下图,在字符串常量池的作用下,变量str1str2会指向同一个字符串对象:

在这里插入图片描述
所以其输出结果是:

在这里插入图片描述

变量拼接:
String str1 = "Hello";
String str2 = "He" + "llo";
String str3 = "He";
String str4 = "llo";
String str5 = str3 + str4;
String str6 = str3 + "llo";System.out.println(str1 == str5); // true or false?
System.out.println(str1.equals(str5)); // true or false?
System.out.println(str1 == str6); // true or false?

变量str5str6是通过字符串变量拼接得到,底层是通过字符串构造函数创建的,所以不会通过常量池复用已经创建的对象,所以str1str5两个变量指向不同地址的对象,用运算符==比较的结果是false;但两个对象存储的字符串内容是相同的,用equals比较的结果是true

在这里插入图片描述

二,为什么要使用StringBuilder进行字符串拼接

我们运行下面的代码,统计一下两个循环的耗时:

④比较下面两种字符串拼接的差异

long s = System.currentTimeMillis();String strPlus = "";for (int i = 0; i < 100000; i++) {strPlus += "Hello, World!";}System.out.println("字符串变量拼接耗时:" + (System.currentTimeMillis() - s));s = System.currentTimeMillis();// 第二种拼接:使用StringBuilderStringBuilder strBuilder = new StringBuilder();for (int i = 0; i < 100000; i++) {strBuilder.append("Hello, World!");}String result = strBuilder.toString();System.out.println("StringBuilder拼接耗时:" + (System.currentTimeMillis() - s));

发现第一种拼接方式耗时超过7秒,但是第二种拼接方式耗时不到3毫秒,差距惊人。
在这里插入图片描述
原因在于:

  • ①使用+号进行字符串变量循环拼接时,每次拼接都会创建一个StringBulider对象,并调用字符串构造函数创建一个新的字符串对象,循环次数很大时,创建大量对象耗时很长
  • ②使用StringBuilder拼接对象时,是把字符串通过append方法插入到StringBuilder类的一个字符数组中,数组的插入是非常快的,尽管循环次数多,但是效率非常高,耗时极短

最佳实践

  • ①如果存在大量的字符串拼接,使用StringBuilder是更高效的方式
  • ②如果拼接次数很少,比如少于10次,使用+进行拼接会使代码更简洁

所以,不必所有的字符串都用StringBuilder进行拼接。

三,扩展:就近原则

public class StringExercise07 {public static void main(String[] args) {Test1 ex = new Test1();ex.change(ex.str, ex.ch);// 输出结果是?System.out.println(ex.str + " and ");System.out.println(ex.ch);}
}class Test1 {String str = new String("ly");final char[] ch = {'j', 'a', 'v', 'a'};public void change(String str, char ch[]) {str = "java";ch[0] = 'h';}
}

先看下运行结果:

在这里插入图片描述
对于初学者,很难理解这个输出,调用了Test1change方法为什么成员变量str的值没有改变,但是变量ch的值却改变了?

如果仔细看change方法的代码:

public void change(String str, char ch[]) {str = "java";ch[0] = 'h';
}

change方法的第一个参数和Test1的成员变量str同名,JVM在执行这个方法时,会优先从change方法栈帧的局部变量表中查找str变量,如果有,就不会使用Test1类的成员变量,所以Test1的成员变量str的值并没有被修改。

如果像下面这样修改change的代码:

```cpp
public void change(String str, char ch[]) {this.str = "java";ch[0] = 'h';}

因为有了this关键字的作用,JVM会查找成员变量str,然后修改其值,打印结果如下:![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/028b9778c2d7422ebf5092f25a4b4f3b.png)

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Github上一款开源、简洁、强大的任务管理工具:Condution

Condution 是一款开源任务管理工具&#xff0c;它以简洁易用、功能强大著称。它旨在为用户提供一个简单高效的平台&#xff0c;帮助他们管理日常任务、提高工作效率。 1. Condution 的诞生背景 现如今&#xff0c;市面上存在着许多任务管理软件&#xff0c;但它们往往价格昂贵…

C语言:学生成绩管理系统(含源代码)

一.功能 二.源代码 #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_NUM 100 typedef struct {char no[30];char name[10];char sex[10];char phone[20];float cyuyan;float computer;float datastruct; } *student, student1;typ…

SQL Server定期收缩日志文件详细步骤——基于SQL Server 2012

SQL Server定期收缩日志文件详细步骤 一、环境配置1、查看数据库的属性2、文件设置3、备份模式4、查看收缩配置5、查看收缩选项 二、编写作业计划1、选择新建作业2、常规配置3、步骤4、输入内容5、脚本详解6、新建计划7、输入名称、选择执行时间8、查看测试9、查看测试结果 一、…

如何学习ai agent?

如何学习Agent&#xff0c;推荐阅读《动手做AI Agent》这本书。 推荐理由&#xff1a; 1&#xff1a;一本书能够全方位了解并探索Agent的奥秘&#xff01; &#xff08;1&#xff09;Agent的发展进程。 &#xff08;2&#xff09;可以帮我们做哪些事&#xff1a;自动办公&am…

COZE工作流超详细教程(胎教版)

前言 不知道有没有人和我一样喜欢经常收集一些好看的壁纸&#xff0c;但是有时候寻找的壁纸却总是差强人意。最近在学习COZE的时候搭建了一个自己的智能体通过工作流设计感觉还不错今天和大家分享分享。COZE的工作流界面友好&#xff0c;操作直观&#xff0c;即便是没有设计经…

6月2号训练(Codeforces Round 306 (Div. 2))(待补)

前言&#xff1a; 昨天晚上的训练&#xff0c;昨天下午刚刚打了百度的编程之星大赛&#xff0c;已经被题目橄榄了&#xff0c;榜上清一色的都是搞oi的中学生大佬&#xff0c;而我想了一下午也才只写了一道题&#xff0c;只能说路阻且长啊。晚上去洗了个澡&#xff0c;就没怎么认…

jmeter发送webserver请求和上传请求

有时候在项目中会遇到webserver接口和上传接口的请求&#xff0c;大致参考如下 一、发送webserver请求 先获取登录接口的token&#xff0c;再使用cookie管理器进行关联获取商品(webserver接口)&#xff0c;注意参数一般是写在消息体数据中&#xff0c;消息体有点像HTML格式 执…

算法工程师需要学习C++的哪些知识?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「C的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;以下是算法工程师需要学习的一些…

输入a和b两个整数,按先大后小的顺序输出a和b(用指针变量处理)

解题思路&#xff1a; 定义两个&#xff08;int*&#xff09;型指针变量p1和p2&#xff0c;使它们分别指向a和b。使p1指向a和b中的大者&#xff0c;p2指向小者&#xff0c;顺序输出*p1,*p2就实现了按先大后小的顺序输出a和b。 编写程序&#xff1a; 运行结果&#xff1a; 程序…

coredns 被误删了,可以通过重新应用 coredns 的 Deployment 或 DaemonSet 配置文件来恢复

如果 coredns 被误删了&#xff0c;可以通过重新应用 coredns 的 Deployment 或 DaemonSet 配置文件来恢复。以下是恢复 coredns 的步骤&#xff1a; 1. 下载 coredns 配置文件 你可以从 Kubernetes 的官方 GitHub 仓库下载 coredns 的配置文件。以下是下载并应用配置文件的步…

计算机网络学习实践:模拟RIP动态路由

计算机网络学习实践&#xff1a;模拟RIP动态路由 模拟动态路由RIP协议 1.实验准备 实验环境&#xff1a;华为模拟器ENSP 实验设备&#xff1a; 3个路由器&#xff0c;3个二层交换机&#xff08;不是三层的&#xff09;&#xff0c;3个PC机 5个网段 192.168.1.0 255.255.…

Aras Innovator-Team(群组)的使用方法

当Aras Innovator在处理权限时&#xff0c;在不使用Team的情况下&#xff0c;系统的权限配置可以满足大部分业务场景&#xff0c;如&#xff1a;常见的按照组织架构&#xff0c;成员和角色分配权限&#xff0c;按照生命周期分配权限等。 如果遇到比较复杂的权限需求&#xff0c…

慢SQL的治理思路

慢SQL的治理思路 什么是慢SQL慢SQL产生的原因查看慢 SQL 是否开启开启慢 SQL 记录开启慢查询日志分析慢 SQL解决和优化慢SQL的方法 什么是慢SQL 慢 SQL 指的是 MySQL 中执行比较慢的 SQL&#xff0c;排查慢 SQL 最常用的方法是通过慢查询日志来查找慢 SQL。 MySQL 的慢查询日志…

深度学习复盘与论文复现B

文章目录 1、Knowledge Review1.1 NLLLoss vs CrossEntropyLoss1.2 MNIST dataset1.2.1 Repare Dataset1.2.2 Design Model1.2.3 Construct Loss and Optimizer1.2.4 Train and Test1.2.5 Training results Pytorch-Lightning MNIST :rocket::fire:1.3 Basic Convolutional Neu…

961题库 北航计算机 计算机网络 附答案 简答题形式

有题目和答案&#xff0c;没有解析&#xff0c;不懂的题问大模型即可&#xff0c;无偿分享。 第1组 习题 某网络拓扑如题下图所示&#xff0c;其中 R 为路由器&#xff0c;主机 H1&#xff5e;H4 的 IP 地址配置以及 R 的各接口 IP 地址配置如图中所示。现有若干以太网交换机…

Flask发送邮件有哪些步骤?怎么保障安全?

Flask发送邮件的模板如何设置&#xff1f;如何测试邮件发送功能&#xff1f; 无论是账户激活、密码重置&#xff0c;还是通知提醒&#xff0c;邮件系统的集成都显得尤为重要。AokSend将详细介绍如何在Flask应用中实现发送邮件的功能&#xff0c;并分步骤讲解其具体实现过程。 …

【Qt系列教程】一、认识Qt、安装Qt、运行Hello Qt

文章目录 1.1 Qt 简介1.2 Qt 的安装1.3 编写 Hello World 1.1 Qt 简介 Qt&#xff08;官网&#xff1a;https://www.qt.io&#xff09;于1995年5月首次公开发布&#xff0c;是一个跨平台的应用程序开发框架&#xff0c;也是最主流的 C 开发框架&#xff1b; Qt 具有其他编程…

Qt6.7 Android第一次尝试

安装qt online installer https://mirrors.tuna.tsinghua.edu.cn/qt/official_releases/online_installers/qt-unified-windows-x64-online.exe选择Android 和 Desktop, 然后默认安装 安装完成后设置Android Sdk, 这里可以直接使用在线下载(因为我也不会怎么配置Android SDK和…

界面控件DevExpress WinForms的流程图组件 - 可完美复制Visio功能(一)

DevExpress WinForms的Diagram&#xff08;流程图&#xff09;组件允许您复制Microsoft Visio中的许多功能&#xff0c;并能在下一个Windows Forms项目中引入信息丰富的图表、流程图和组织图。 P.S&#xff1a;DevExpress WinForms拥有180组件和UI库&#xff0c;能为Windows F…

挂上了代理加速器梯子之后,Git clone指令下载仍旧很慢的问题

当你使用了各种代理软件访问诸如Github、Google、油管、推特这些网址&#xff0c;你会发现基本可以访问&#xff0c;只不过是访问速度不同&#xff0c;但是不管你使用什么代理软件&#xff0c;你的git clone指令从Github远程库下载库的速度都不会受到影响。 当使用代理软件访问…