Java基础之字符串操作——String

字符串

什么是字符串?如果直接按照字面意思来理解就是多个字符连接起来组合成的字符序列。为了更好的理解以上的理论,我们先来解释下字符序列,字符序列:把多个字符按照一定的顺序排列起来;而字符序列就是作为字符串的内容而存在的。所以可以把字符串理解为:把多个字符按照一定的顺序排列起来而构成的排列组合。

如果还是不好理解,没有关系,我还有法宝。我们可以用烤串来比喻说明,可以把字符串看作是烤串,烤串上的每一块肉都相当于是一个字符。把一块块肉按照肥瘦相间的顺序排列并串起来便成了我们吃的烤串,同理,把多个字符按照一定的顺序“串”起来就构成了字符串。

字符串的分类,字符串分为可变的字符串不可变的字符串两种;这里的不可变与可变指的是字符串的对象还是不是同一个,会不会因为字符串对象内容的改变而创建新的对象。

  • 不可变的字符串:当字符串对象创建完毕之后,该对象的内容(上述的字符序列)是不能改变的,一旦内容改变就会创建一个新的字符串对象;Java中的String类的对象就是不可变的。
  • 可变的字符串StringBuilder类和StringBuffer类的对象就是可变的;当对象创建完毕之后,该对象的内容发生改变时不会创建新的对象,也就是说对象的内容可以发生改变,当对象的内容发生改变时,对象保持不变,还是同一个。

String 类

String类表示不可变的字符串,当前String类对象创建完毕之后,该对象的内容(字符序列)是不变的,因为内容一旦改变就会创建一个一个新的对象。

String对象的创建:

  1. 方式一:通过字面量赋值创建,String s1 = “laofu”; 需要注意这里是双引号:“”,区别与字符char类型的单引号:‘’
  2. 方式二:通过构造器创建, String s2 = new String(“laofu”); ;

以上两种创建方式的对象在JVM中又是如何分布的呢? 分别有什么区别呢?

方式一和方式二在JVM中又是如何分布?

字符串对象在JVM中的分布

上图中的常量池:用于存储常量的地方内存区域,位于方法区中。常量池又分为编译常量池运行常量池两种:

  • 编译常量池:当把字节码加载斤JVM的时候,其中存储的是字节码的相关信息(如:行号等)。
  • 运行常量池:其中存储的是代码中的常量数据。

方式一和方式二有何不同?

方式一:String s1 = “laofu”; 有可能只创建一个String对象,也有可能创建不创建String对象;如果在常量池中已经存在”laofu”,那么对象s1会直接引用,不会创建新的String对象;否则,会先在常量池先创建常量”laofu”的内存空间,然后再引用。

方式二:String s2 = new String(“laofu”); 最多会创建两个String对象,最少创建一个String对象。可使用new关键字创建对象是会在堆空间创建内存区域,这是第一个对象;然后对象中的字符串字面量可能会创建第二个对象,而第二个对象如方式一中所描述的那样,是有可能会不被创建的,所以至少创建一个String个对象。

字符串的本质,字符串在底层其实就是char[]char表示一个字符,比如:

String str = "laofu"; // 等价于 char[] cs = new char[]{'l','a','o','f','u'};

字符串在底层其实就是char[]

String对象的空值:

  1. 对象引用为空,即:String s1 = null; 此时s1没有初始化,也在JVM中没有分配内存空间。
  2. 对象内容为空字符串, 比如:String s2 = “”; 此时对象s2已经初始化,值为“”JVM已经为其分配内存空间。

字符串的比较:使用“==”和“equals”会有不同效果,详情在之前的文章中分享过:

老夫不正经:「JAVA」只知对象属性,不知类属性?就算类答应,static都不答应​zhuanlan.zhihu.com

  1. 使用”==”号:用于比较对象引用的内存地址是否相同。
  2. 使用equals方法:在Object类中和”==”号相同,但在自定义类中,建议覆盖equals方法去实现比较自己内容的细节;由于String类覆盖已经覆盖了equals方法,所以其比较的是字符内容。
String equals方法的实现细节

所以可以这样来判断字符串非空:

  1. 对象引用不能为空:s1 != null;;
  2. 字符内容不能为空字符串(“”):“”.equals(s1);;

如果上述两个条件都满足,说明字符串确实为空!

字符串拼接:Java中的字符串可以通过是“+”实现拼接,那么代码中字符串拼接在JVM中又是如何处理的呢?我们通过一个例子说明:通过比较拼接字符串代码编译前后的代码来查看JVM对字符串拼接的处理。

字符串拼接的JVM实现

通过上述例子不难发现,JVM会对字符串拼接做一些优化操作,如果字符串字面量之间的拼接,无论有多少个字符串,JVM都会一样的处理;如果是对象之间拼接,或者是对象和字面量之间的拼接,亦或是方法执行结果参与拼接,String内部会使用StringBuilder先来获取对象的值,然后使用append方法来执行拼接。由此可以总结得出:

  1. 使用字符串字面量创建的字符串,也就是单独使用""引号创建的字符串都是直接量,在编译期就会将其存储到常量池中;
  2. 使用new String("")创建的对象会存储到堆内存中,在运行期才创建;
  3. 使用只包含直接量的字符串连接符如:"aa" + "bb"创建的也是直接量,这样的字符串在编译期就能确定,所以也会存储到常量池中;
  4. 使用包含String直接量的字符串表达式(如:"aa" + s1)创建的对象是运行期才创建的,对象存储在堆中,因为其底层是创新了StringBuilder对象来实现拼接的;

5. 无论是使用变量,还是调用方法来连接字符串,都只能在运行期才能确定变量的值和方法的返回值,不存在编译优化操作。

String 的常用API

这里列举了一些常用String API,更多的可以查阅jdk使用手册,做Java一定得学会查阅jdk手册。

jdk 手册

String 的创建和转换:

// 把字符串转换为byte数组。
byte[] getBytes();// 把字符串转换为char数组。
char[] toCharArray();// 把byte数组转换为字符串。
String(byte[] bytes);// 把char数组转换为字符串。
String(char[] value);

获取字符串信息

// 返回此字符串的长度。
int length(); // 返回指定索引处的 char 值。
char charAt(int index); // 返回指定字符串在此字符串中首次(从最左边算起)出现处的索引。
int indexOf(String str);// 返回指定字符串在此字符串中最后(最右边算起)出现处的索引。
int lastIndexOf(String str); 

字符串比较判断

// 将此字符串与指定的对象比较。
boolean equals(Object anObject); // 将此 String 与另一个 String 做忽略大小写的比较。
boolean equalsIgnoreCase(String anotherString); // 将此字符串与指定的 CharSequence 比较,比较的是内容;
// ps:String类是现实了CharSequence(字符序列)接口的。
boolean contentEquals(CharSequence cs); 

字符串大小写转换:调用方法的字符串就是当前字符串

// 把当前字符串转换为大写
String toUpperCase(); // 把当前字符串转换为小写
String toLowerCase(); 

StringBuilder/StringBuffer

先来分别使用String/StringBuilder/StringBuffer来拼接30000次字符串,对比各自损耗的时间,经过测试发现:

  • String做字符串拼接的时候,耗时最高,性能极低,原因是String内容是不可变的,每次内容改变都会在内存中创建新的对象。
  • 性能最好的是StringBuilder,其次是StringBuffer,最后是StringStringBuilderStringBuffer区别并不是很大,也有可能是测试次数还不够吧。感兴趣的小伙伴可以增加拼接次数来看看。代码很简单,就不展示出来了。

所以在开发中拼接字符串时,优先使用StringBuffer/StringBuilder,不要轻易使用String

StringBuilder以及StringBuffer的区别

StringBufferStringBuilder都表示可变的字符串,他俩的功能方法都是相同的。但唯一的区别:

  • StringBufferStringBuffer中的方法都使用了synchronized修饰符,表示同步操作,在多线程并发的时候可以保证线程安全,但在保证线程安全的时候,对其性能有一定影响,会降低其性能。
  • StringBuilderStringBuilder中的方法都没有使用了synchronized修饰符,线程不安全,正因为如此,其性能较高。

对并发安全没有很高要求的情况下,建议使用StringBuilder,因为其性能很高。像这样的情况会较多些。使用StringBuilder无参数的构造器,在底层创建了一个长度为16char数组:

StringBuilder 无参构造器

此时该数组只能存储16个字符,如果超过了16个字符,会自动扩容(创建长度更大的数组,再把之前的数组拷贝到新数组),此时性能极低;如果事先知道大概需要存储多少字符,可以通过构造器来设置字符的初始值:

设置初始值的StringBuilder构造器
// 创建一个长度为80的char数组.
new StringBuilder(80);

StringBuilder的常用方法:

// 追加任意类型数据到当前StringBuilder对象中。
append(Object val); // 删除字符串中指定位置的字符。
StringBuilder deleteCharAt(int index);

完结。老夫虽不正经,但老夫一身的才华

android string拼接字符串_「JAVA」细述合理创建字符串,分析字符串的底层存储,你不该错过...相关推荐

  1. java字符串底层实现_「JAVA」细述合理创建字符串,分析字符串的底层存储,你不该错过...

    Java基础之字符串操作--String 字符串 什么是字符串?如果直接按照字面意思来理解就是多个字符连接起来组合成的字符序列.为了更好的理解以上的理论,我们先来解释下字符序列,字符序列:把多个字符按 ...

  2. java 同步解决不安全类_「JAVA」Java 线程不安全分析,同步锁和Lock机制,哪个解决方案更好...

    线程不安全 线程不安全的问题分析:在小朋友抢气球的案例中模拟网络延迟来将问题暴露出来:示例代码如下: public class ImplementsDemo { public static void ...

  3. java里面string什么意思_「Java基础知识」Java中的字符串是什么

    原标题:「Java基础知识」Java中的字符串是什么 字符串顾名思义就是一些字符组合在一起组成的一串数据,称作字符串,在Java中字符串用双引号包围起来,格式为String string = &quo ...

  4. bigint对应java什么类型_「JAVA」从格式化输出到扫描输入,深究Java正则表达式匹配之道

    字符串是不可变的 字符串是不可变的,也就是说当字符串的内容发生改变的时候,会创建一个新的String对象:但是如果内容没有发生改变的时候,String类的方法会返回原字符串对象的引用. 而正则表达式往 ...

  5. android javamail获取邮件太多太慢_「Java」 - SpringBoot amp; 邮件发送

    发送邮件是web应用系统的必备功能之一,用于用户注册验证.忘记密码找回或者发送营销信息.最早期使用JavaMail相关API写发送邮件的相关代码,后来Spring推出了JavaMailSender简化 ...

  6. android image 位移动画_「translateanimation」Android 补间动画之平移动画TranslateAnimation - seo实验室...

    translateanimation 博客导航: 1.介绍: Android补间动画之平移动画,在实际的开发过程中,其实有好多地方需要用到平移动画,这是对于平移动画的简单介绍. 2.属性 durati ...

  7. java接口多态的变量能_「JAVA」多态的灵魂,面向接口的程序设计,这才是你该懂得的接口(interface)...

    Java面向对象之接口--interface 什么是接口 一般计算机中的接口分为硬件接口和软件接口. 硬件接口:是指两个硬件设备之间的连接方式,既包括物理上的接口,还包括逻辑上的数据传送协议. 软件接 ...

  8. java借口不同程序_「JAVA」多态的灵魂,面向接口的程序设计,这才是你该懂得的接口(interface)...

    Java面向对象之接口--interface 什么是接口 一般计算机中的接口分为硬件接口和软件接口.硬件接口:是指两个硬件设备之间的连接方式,既包括物理上的接口,还包括逻辑上的数据传送协议. 软件接口 ...

  9. 抛出运行时异常的目的_「JAVA」运行时异常、编译时异常、自定义异常,通过案例实践转译和异常链...

    Java基础之异常处理机制 什么是异常 从事Java开发的小伙伴对于"异常"应该不陌生,因为每天都会遇到不少异常,或捕获,或抛出.那究竟什么是异常?异常即非正常的,不同于平常.一般 ...

最新文章

  1. 神器推荐,可视化 Python 打包 exe,牛逼
  2. 8种图数据库对 NULL 属性值支持情况
  3. u-boot移植随笔:u-boot2010.09移植到8MB Nor Flash的S3C2440,第一步搞定(补记)
  4. 在windows平台下搭建Django项目虚拟环境
  5. java虚拟机缩写为_(01-03)Java虚拟机缩写为。
  6. SQL高级---SQL NULL 值
  7. Yii2学习笔记002---Yii2的控制器和视图
  8. mimics中三维构造,建立MASK
  9. 怎么样把书上的字很快的弄成电子版,哈哈,我也会了
  10. 如何运行计算机学报的LaTeX模板?
  11. 二十三种设计模式之工厂模式(含Java工厂模式的实现)
  12. 【微信小程序】快进来弹钢琴啦~钢琴小程序源码分享
  13. ROS on DDS
  14. 知识图谱(knowledge graph)——概述
  15. 仙剑5手游服务器维护,仙剑奇侠传手游维护及内容更新公告
  16. 苹果手机如何深度清理_安卓手机必备清理软件APP,完全免费超级深度清理
  17. 项目管理过程中六种冲突解决方法
  18. JZOJ 3518. 【NOIP2013模拟11.6A组】进化序列(evolve)
  19. laravel学习笔记------使用 Entrust 扩展包在 Laravel 5 中实现 RBAC 权限管理
  20. android 1g运行内存,全新安卓系统首曝光:安卓9.0只要1G运存就能流畅运行

热门文章

  1. Java高手需要注意的25个学习目标
  2. VC“cannot execute program”错误的解决方法
  3. nginx普通配置/负载均衡配置/ssl/https配置
  4. Python学习week4-set集合
  5. python3--多目录之间的协作的一些必备知识
  6. YJX_Driver_031_再谈SSDT_HOOK驱动保护原理
  7. Silverlight访问Wcf Ria Library的问题总结
  8. WAV格式中常见的压缩编码
  9. C++_结构体中const使用场景_结构体_毕业设计案例_使用结构体数组_随机数种子---C++语言工作笔记027
  10. 大数据之-Hadoop伪分布式_启动YARN并运行MR程序---大数据之hadoop工作笔记0026