浅谈java中的String

news/2024/5/2 0:38:36/文章来源:https://blog.csdn.net/qq_56044032/article/details/127693566

Java中的String类型不属于八大基本数据类型,而是一个引用数据类型,所以在定义一个String对象的时候如果不直接赋值给这个对象,它的默认值就是null。我们要怎么理解String类型的不可变,在JDK源码中String这个类的value方法被final关键字修饰,导致String里的值不可以被修改。

在这里插入图片描述

也就是说双引号括起来的String对象,从出生到死亡,都无法发生变化。
对于任意一个String对象,都由value[]和hash组成,例如:String str = “hello”;
在这里插入图片描述

当我们直接使用双引号括起来的字符串初始化String对象时,该字符串会被存储在“方法区”的“字符串常量池”当中。而上图中的hash值就是用于查找字符串在常量池中的位置。

字符串常量池主要用于存储字符串常量,本质是一个哈希表(StringTable)。从JDK1.8开始,这个哈希表就存放在了堆中

为什么会将字符串放在常量区?
因为字符串在实际的开发中使用太频繁。为了执行效率,所以把字符串放到了方法区的字符串常量池当中。


从这里开始,将从内存的角度分析问题

例1:

在这里插入图片描述
这里输出false的原因大家应该都知道,因为str1和str2的引用指向不,那底层是如何实现的呢?
通过内存图可以清晰明了看起问题本质:

在这里插入图片描述

String str1 = “hello”,首先会创建一个字符数组,用来存放"hello",然后再开辟一块空间(假如为ptr1),指向这个数组,最后会在栈上用一个引用去指向ptr1。

String str2 = new String(“hello”),首先会查看StringTable是否存在"hello",这里已经存在,会开辟一块空间(假如为ptr2),将ptr1中的内容拷贝到ptr2中,那么ptr2就指向了"hello"数组,最后在栈上用一个引用去指向ptr2

JDK源码:
在这里插入图片描述
在这里插入图片描述

JDK源码的大致含义:当我们用一个字符串去实例化一个String对象时,会将这个字符串通过hash函数得到一个hash值,然后在StringTable中找,如果存在就不需要重新再创建,如果不存在就需要创建这个字符串


例2:

public class Test6 {public static void main(String[] args) {String str1 = "hello";String str2 = "hello";System.out.println(str1 == str2);}
}

在这里插入图片描述

在这里插入图片描述
因为hello在SringTable中已经存在,所以在栈上引用str2直接指向了0x3344。而例1中str2是new出来的,所以需要在堆上开辟空间,然后这块空间再指向0x3344


例3:

public class Test6 {public static void main(String[] args) {String str1 = "hello";String str2 = "he" + "llo";System.out.println(str1 == str2);}
}

在这里插入图片描述
这里的结果依然是true
因为“he”和“llo”都是常量,代码在编译的时候就已经确定了最终结果是常量字符串“hello”

如果“he”或者“llo”有一个是变量,最终结果为false,例如:
在这里插入图片描述


例4:

public class Test6 {public static void main(String[] args) {String str1 = "11";String str2 = new String("1") + new String("1");System.out.println(str1 == str2);}
}

在这里插入图片描述

还是画图分析:

在这里插入图片描述

当new string(“1”)时,产生的匿名对象,也会将"1"放入到字符串常量池中,当两个"1"相加,就会形成一个StringBuilder对象, 里面存放"11",当这个StringBuilder对象要转换成String对象时,需要调用to_string()方法,这个方法并不会判断"11"是否在字符串常量池中,而是直接创建新的字符串。也就是说to_string并不会入池,所以最终的结果为false

例5:

public class Test6 {public static void main(String[] args) {String str2 = new String("1") + new String("1");String str1 = "11";System.out.println(str1 == str2);}
}

在这里插入图片描述

这里的结果也为false,跟例4的原因是一样的。这里就不画图了。

但是只要对这个代码稍作修改,结果就为true

在这里插入图片描述

intern()这个方法叫做手动入池(将String放在字符串常量池中)

本来str2(“11”)是没有入池,但是调用了intern()方法后,“11"就被放进了常量池中。当定义str1时,发现常量池中有"11”,就不需要再创建字符串"11",直接赋值,就和例2一样,最终str1和str2引用的是同一块空间,所以为true

注意:使用intern()方法时,如果常量池中没有才会入池,有的话就不会入池


equals()方法进行比较String

在这里插入图片描述

以上是equals的源码,是按照逐一比较字符的方式。

如果String没有重新equals方法的话,默认是比较地址
在这里插入图片描述

任何一个引用去调用方法,都要预防空指针异常,equals方法也不例外,例如:
在这里插入图片描述

除此之外,下面两种字符串引用有着本质的区别

String str1 = null;
String str2 = "";

str1这个引用,不指向任何对象
str2这个引用,指向的字符串是空的

可以使用length方法验证一下:
在这里插入图片描述

String作为参数,在使用之前也需要进行判断:

public static void func(String str) {//正确写法if (str == null || str.length() == 0) {return;}//错误写法if(str.length() == 0 || str == null) {return;}
}

第二种写法,先判断str的长度可能会存在空指针异常


尽量少用String去拼接字符串

public class Test7 {public static void main(String[] args) {String str = "abcde";for(int i = 0; i < 10; ++i) {str += i;}System.out.println(str);}
}

这段代码,大家可能认为平平无奇,就是一个单纯的字符串拼接。但是每一次拼接都会产生临时对象,可以看一下java的汇编代码:

在这里插入图片描述
从汇编代码可以看出,每一次的for循环,都会创建一个StringBuilder对象,最后再调用String方法。这样是非常浪费内存和时间的,虽说循环10次影响比较小,但是循环10w次呢?那开销就很大了,所以,由于String是不可变的,所以应当少用String去拼接字符串。


StringBuffer和StringBuilder

首先来回顾下String类的特点:
任何的字符串常量都是String对象,而且String的常量一旦声明不可改变,如果改变对象内容,改变的是其引用的指向而已。
通常来讲String的操作比较简单,但是由于String的不可更改特性,为了方便字符串的修改,提供StringBuffer和StringBuilder类。
StringBuffer 和 StringBuilder 大部分功能是相同的,主要介绍 StringBuffer

public class Test8 {public static void main(String[] args) {StringBuilder sb = new StringBuilder();sb.append("fff");System.out.println(sb);sb.append("lll");System.out.println(sb);}
}

在这里插入图片描述

String是无法更改的,而StringBuilder对象时可以更改的,这里调用了两次append方法,都是在原有对象进行添加操作,并且有重新创建新的对象。

在这里插入图片描述

前面也了解了,在循环中用String去拼接字符串,会产生临时对象StringBuilder,而StringBuilder对象又是可以更改的,所以优先使用StringBuilder对象去拼接字符串


StringBuffer和StringBuilder的区别

StringBuilder中的部分append:
在这里插入图片描述

StringBuffer中的部分append:
在这里插入图片描述

通过对比,发现StringBuffer中的append方法是多了synchronized,也就是说StringBuffer是线程安全的,而StringBuilder不是线程安全的。
因此StringBuilder用于单线程,而StringBuffer用于多线程。

String、StringBuffer、StringBuilder的区别

  • String的内容不可修改,StringBuffer与StringBuilder的内容可以修改.
  • StringBuffer与StringBuilder大部分功能是相似的
  • StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作

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

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

相关文章

【C++】如何修改set的值

问题&#xff1a;尝试通过begin方法得到的迭代器去修改值&#xff0c;发现会报错。 set<string> st{"hello", "world", "good"}; set<string>::iterator it st.begin(); *it "test"; 原因&#xff1a;我们可以在源码里…

怎么搭建搜题接口api

怎么搭建搜题接口api 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 查题校园题库&#xff1a;查题校园题库后台&#xff08;点击…

RTSP协议学习Ubuntu环境准备

文章目录RTSP协议学习Ubuntu环境准备RTSP协议概述Ubuntu环境准备一、Ubuntu安装FFmpeg二、安装ZLMediaKit1、获取代码2、强烈推荐3、编译器3.1、编译器版本要求3.2、安装编译器4、cmake5、依赖库5.1、依赖库列表5.2、安装依赖库6、构建和编译项目7、运行8、测试三、测试推流测试…

【Tomcat】解决Tomcat服务器乱码问题

俩地方开展出现乱码的原因1、以startup.bat文件打开的服务器出现乱码2、在IDEA中运行Tomcat服务器出现乱码问题3、有关社区版IDEA如何开发JavaWeb项目出现乱码的原因 使用了错误的字符编码去解码字节流&#xff0c;所以出现乱码咱思维要清晰&#xff0c;就去找字符编码是否与其…

【TS04——接口的多态——泛型接口】

接口的多态&#xff0c;同一个方法&#xff0c;传入不同的参数&#xff0c;他所对应的操作不同成为多态【参数不同】或者可以理解为同一个方法&#xff0c;返回不同的结果&#xff0c;称之多态。 interface IfnPerson {run():voidrun(id:number):voidrun(id:number,name:strin…

【生日快乐】Node.js 实战 第1章 欢迎进入Node.js 的世界 1.3 安装Node

Node.js 实战 文章目录Node.js 实战第1章 欢迎进入Node.js 的世界1.3 安装Node第1章 欢迎进入Node.js 的世界 1.3 安装Node 安装Node的最简单的方法是使用其官网上的安装程序。可以用对应Mac或 Windows的安装程序安装最新的当前版。 官网安装包下载地址&#xff1a;https://…

Jenkins部署详细教程

Jenkins简介 Jenkins 是一个可扩展的持续集成引擎。是一个自成一体的开源自动化服务器, 可用于自动化与构建、测试、交付或部署软件相关的各种任务; Jenkins是一个高度可扩展的产品, 其功能可以通过安装插件来扩展。 在gitlab里可以完成源代码的管理&#xff0c;但是对于研发…

[ACTF2020 新生赛]Exec1命令注入

1.来看题目如下 得到一个ping的输入框&#xff0c;老样子先检查网页源码看有没有什么好东西&#xff0c;得到一个链接&#xff0c;我们来访问一下 发现也没什么有用处的信息&#xff0c;于是看到题目的标题之后联想到了命令注入&#xff0c; 那么是怎么判断使用命令注入的呢&am…

MyBatis初步了解

1.Mybatis简介 1.1原始jdbc操作&#xff08;查询数据&#xff09; 1.2原始jdbc操作&#xff08;插入数据&#xff09; 1.3 原始jdbc操作的分析 原始jdbc开发存在的问题如下&#xff1a; ①数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能 ②sql 语句在代码中硬编…

深入理解Java虚拟机:Java运行内存结构

本篇内容包括&#xff1a;JAVA 运行内存结构&#xff0c;即 程序计数器、Java 虚拟机栈、本地方法栈 、Java堆、方法区、运行时常量池 以及 直接内存等相关内容&#xff01; 一、JAVA 运行内存结构 Jvm 执行 Java 程序时&#xff0c;会把它所管理的内存划分为若干个不同的数据…

软件设计与体系——创建型模式

如果有兴趣了解更多相关内容&#xff0c;欢迎来我的个人网站看看&#xff1a;瞳孔的个人空间 创建型模式&#xff1a; 创建型模式抽象了实例化过程帮助系统独立于如何创建、组合和表示对象 一个类创建型模式使用继承改变被实例化的类类创建型模式使用继承改变被实例化的类对象…

自学Python需要掌握哪些知识点?怎么学?

其实这个问题你可以转换一下思路&#xff0c;自学python有什么合适的学习路线&#xff0c;基本路线里涉及到的都是需要掌握的~ 单个知识点那罗列起来可是太多了~ 另外你可以考虑下你自学python的目的和方向&#xff0c;是单纯兴趣还是说要学了就业~ 想往人工智能方向发展还是P…

如何实现CAN/LIN通信路由测试?

目前车载网络通信越来越复杂&#xff0c;通信总线、协议多种多样&#xff0c;网关作为信息交互的载体&#xff0c;主要通过报文路由转发、报文过滤、信号重组等方式实现不同网段、不同协议间的信息交互&#xff1b;在复杂的车载网络中&#xff0c;网关通信路由转发功能具有非常…

软件设计师--数据结构考点细节总结

视频2022软件设计师-提炼高频考点-个人学习过程的总结&#xff0c;仅供参考&#xff01;_哔哩哔哩_bilibili 目录 1&#xff0c;栈 2,二叉树 3&#xff0c;HUFFMAN 特点&#xff1a; 4&#xff0c;文件压缩比 5&#xff0c;拓扑排序 -- 有向无环图 6&#xff0c;查找 7…

CSDN编程竞赛第八期 | 参赛经历分享

参赛经历分享 一共参与七次了&#xff0c;这是我第二次在CSDN竞赛上获奖&#xff0c;上一次获奖是在第四期&#xff0c;获得了第三名&#xff0c;拿到了一个高级背包、一件T恤和一个获奖证书&#xff0c;我之前拍照发过动态的&#xff0c;详情请见&#xff1a;动态 - CSDN 然后…

机器视觉(三):摄像机标定技术

目录&#xff1a; 机器视觉&#xff08;二&#xff09;&#xff1a;机器视觉硬件技术 机器视觉&#xff08;三&#xff09;&#xff1a;摄像机标定技术 &#x1f30f;&#x1f9d0;以下为正文&#x1f984;&#x1fa90; 摄像机标定的目的&#xff1a;三维重建 空间物体表面…

FOTS:端到端的文本检测与识别方法的原理应用与优势

导言&#xff1a; 基于深度学习的可端到端训练的自然场景检测与识别算法(text spotting)由于其简洁高效且统一的结构&#xff0c;逐渐取代了过去将检测与识别分阶段训练然后拼接在一起的方案&#xff0c;成为自然场景文本检测与识别的主流研究方向之一。端到端自然场景文本检测…

rk3288-android8.1-OV13850

第一步,要到规格书,知道引脚定义 第二步,对应原理图定义 第三步 电源是打开的,如果是GPIO请设置 第四步,查看是否有驱动 out目录可以看到驱动 第五步,查看是否有以下文件

分组后再子集再查询

【问题】 有如下查询结果 从这里面查询每个产品 (ProductID) 具有某个&#xff08;或同时具有某几个&#xff09;属性 (即 AttID) 的产品数量&#xff0c;并按 ClassID 分组 如果只查询某个产品具有单个属性的话&#xff0c;可以用如下 SQL&#xff0c;如下面查询具有 AttID 为…

AI图像合成技术如何用于数字营销和创意领域?

全栈程序员推荐&#xff0c;AI好书必读 北大出版社&#xff0c;人工智能原理与实践 人工智能和数据科学从入门到精通 详解机器学习深度学习算法原理 人工智能原理与实践 全面涵盖人工智能和数据科学各个重要体系经典 “哇&#xff0c;你拍到的吗&#xff1f;等一下——那是专…