Java字符串知多少:String、StringBuffer、StringBuilder

一、String

1、简介

  • String 是 Java 中使用得最频繁的一个类了,不管是作为开发者的业务使用,还是一些系统级别的字符使用, String 都发挥着重要的作用。
  • String 是不可变的、final的,不能被继承,且 Java 在运行时也保存了一个字符串池(String pool) ,就使得 String 变得很特殊。
  • 实现三个接口:java.io.Serializable, Comparable, CharSequence

2、String对象两种创建方法

// 1:编译时就确定了字符串的内容
String strComplier = "A";
// 2:new,只有运行时才能确定字符串的内容
String strNew = new String("A");
  • String strComplier = “A”;

Java 程序在运行的时候会维护着一个常量池,编译期生成的各种字面量和符号引用会在类加载后进入方法区的运行时常量池。对于上述这种实现字符串的方式就可以在编译的时候确定字符串的内容,因此这一行生成的内存结构就如下图。

在这里插入图片描述

不严谨的讲:虚拟机栈中的 strComplier 存储的就是 A 在常量池中的地址

  • String strNew = new String(“A”);

因为使用的 new 的方式,所以这句代码只有运行的时候才能确定字符串的内容。而对于 new 关键字,java 是将对象的实例数据存放上的,但是又因 String 常量池的存在,因此实际上在堆上的 String 对象的数据又指向了字符串常量池。

在这里插入图片描述

不严谨的讲:虚拟机栈中的 strNew 存储的就是 strNew 这个对象在堆内存的地址,而 strNew 中的字符串数据又指向了常量池中的 A

3、比较: == 和 equals()

  • ==

    比较两个对象的引用是否相等,也就是说比较两个地址是否相等

//true : 地址相同
String a = "A";
String a1 = "A";
System.out.println(a1 == a); //指向的都是 A 的地址,地址相同,返回的是 true
//false : 地址不同,在堆中是两个不同的对象,虽然指向常量池中的同一个值 A
String b = new String("A");
String b1 = new String("A");
System.out.println(b==b1);//分别指向的是在堆内存上的不同对象的地址,地址不同,返回的是 false
//false : 地址不同
String c = "A";
String c1 = new String("A");
System.out.println(c == c1);//一个指向常量池,一个指向堆,返回 false
//true : 地址相同
String d = "A";
String d1 = d; 
System.out.println(d == d1);//把d中的 常量池地址 赋给d1,返回 true
//true : 地址相同
String a = "A1";
String a1 = "A" + 1;//编译时已确定
System.out.println(a == a1);//true

//false : 地址不同
String b = "A1";
String b1 = "A";
String b2 = b1 + 1;//编译时不确定
System.out.println(b == b2);//false

在这里插入图片描述

  • equals

    比较的两个对象是否相等,也就是说是同一个对象,在jvm堆内是惟一的

    String类对equals()方法进行了重写,只有值相等,才为true

//equals方法源码
public boolean equals(Object anObject) {
    //判断是否是相同对象
    if (this == anObject) {
        return true;
    }
    //判断anObject是否是String类型
    if (anObject instanceof String) {
        //        
        String aString = (String)anObject;
        //判断编码是否相同
        if (coder() == aString.coder()) {//
            //比较两个字符串是否完全相等
            return isLatin1() ? StringLatin1.equals(value, aString.value)                : StringUTF16.equals(value, aString.value);
        }
    } 
    return false;
}

在这里插入图片描述

String stringCompiler = "A";
String stringNew = new String("A");
StringBuilder stringBuilder = new StringBuilder("A");
StringBuffer stringBuffer = new StringBuffer("A");
//不是相同对象
System.out.println(stringCompiler == stringNew);  // false
//不是相同对象,但都是String类型,编码、字符值都相同
System.out.println(stringCompiler.equals(stringNew));  // true
//stringBuilder不是String类型
System.out.println(stringCompiler.equals(stringBuilder));  // false
//stringBuffer不是String类型
System.out.println(stringNew.equals(stringBuffer));   // false
比较==equals()
String s1 = new String(“java”);
String s2 = new String(“java”);
false
s1、s2在堆中是不同的对象,地址不同
虽然都指向了常量池中的’'java"
true
String类对equals()进行了重写
只要值相同,就返回true
String s1 = new String(“java”);
String s2 = s1;
true
同一对象,地址相同,值相同
true
同一对象,地址相同,值相同
String s1 = “java”;
String s2 = “java”;
true
编译时值已确定
指向常量池中同一地址
true
值相同

4、final

  • String 是 final ,也就是String 是不可变的。即一个 String 对象创建之后所有对它修改后的字符串,都是新生成的 String 对象。
  • String 设计成 final 主要有如下原因:
    • 实现字符串常量池,只有当字符串是不可变时字符串池才有可能实现,字符串池的实现可以在运行时节约很多 heap 空间,因为不同的字符串变量都指向池中的同一个字符串,即可以实现多个变量引用 JVM 内存中的同一个字符串实例,如果字符串不是不变的,String interning 将不能实现(String interning 是指对不同的字符串仅仅只保存一个,即不会保存多个相同的字符串),因为这样的话,如果变量改变了它的值,那么其它指向这个值的变量的值也会一起改变。
    • 安全问题,在系统中有很多地方都是以字符串的形式存在的,比如数据库的用户名,Socket 的主机和端口,当你在调用其他方法,比如调用一些系统级操作之前,可能会有一系列校验,如果是可变类的话,可能在你校验过后,其内部的值被改变了,可能引起严重的系统崩溃问题。
    • 缓存提升性能,String 大量运用在哈希的处理中,由于 String 的不可变性,可以只计算一次哈希值,然后缓存在内部,后续直接取就好了,字符串的处理速度要快过其它的键对象,这就是 HashMap 中的键往往都使用字符串的原因。
    • 线程安全,因为字符串是不可变的,所以是多线程安全的,同一个字符串实例可以被多个线程共享。这样便不用因为线程安全问题而使用同步。

5、编码与解码

//编码
String.getBytes()//方法是得到一个操作系统默认的编码格式的字节数组。
String.getBytes(String decode)//方法会根据指定的decode编码返回某字符串在该编码下的byte数组表示

//解码    
new String(byte[] b,String decode)//按照指定的方法编码
  • 示例
//编码解码一致,正确输出
byte[] b_gbk = "中".getBytes("GBK"); 
String s_gbk = new String(b_gbk,"GBK"); 
System.out.println(s_gbk);//中
//编码解码不一致,输出乱码
byte[] b_gbk = "中".getBytes("GBK"); 
String s_utf8 = new String(b_gbk,"UTF-8"); 
System.out.println(s_utf8);//��

二、StringBuffer

  • final,不能被继承,不能有子类
  • 线程安全
  • 通过append、insert进行字符串的操作
  • 实现接口:java.io.Serializable, CharSequence

三、StringBuilder

  • final,不能被继承,不能有子类
  • 线程不安全
  • 通过append、insert进行字符串的操作
  • 实现接口:java.io.Serializable, CharSequence

四、三者比较

比较StringStringBufferStringBuilder
继承性finalfinalfinal
实现接口Serializable
Comparable
CarSequence
Serializable
CarSequence
Serializable
CarSequence
速度
线程安全性线程安全线程安全线程不安全
常用方法equals()append()
insert()
append()
insert()
适用场景少量的字符串操作多线程下的大量操作单线程下的大量操作

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

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

相关文章

C++的cout详解

2023年5月20日,周六早上: 我发现我找不到非常详细的cout类的成员函数,只好自己写了。 不定期更新。 cout的继承关系 cout类继承自ostream类,ostream类继承自ios类,ios类继承自ios_base类 cout类拥有的所有成员函数 …

pg事务:事务的处理

事务的处理 事务块 从事务形态划分可分为隐式事务和显示事务。隐式事务是一个独立的SQL语句,执行完成后默认提交。显示事务需要显示声明一个事务,多个sql语句组合到一起称为一个事务块。 事务块通过begin,begin transaction,st…

QT学习记录(三)绘图

按照下面两个教程学习 QT学习教程(全面)_Strive--顾的博客-CSDN博客_qt学习 天山老妖S的博客_QT开发(3)_51CTO博客 1、绘图 VC项目右键增加QT GUI Class,在QT Designer中编辑DlgDraw.ui 在DlgDraw中重载函数 void DlgDraw::paintEvent(Q…

C++之堆排

堆排的原理和结构: 堆排序是一种常见的排序算法,基于堆这种数据结构实现。堆是一种特殊的树形数据结构,它满足以下两个条件: 堆是一棵完全二叉树。 堆的任意节点的值,都必须大于等于(或小于等于&#xff0…

基于ROS2的costmap中Obstacle Layer中对障碍物信息的增加与删除机制的方案调研。

文章目录 1.背景2.目标3. 障碍物信息添加方式发送数据的数据结构与接收数据的数据结构 4. 障碍物清理机制4.1 可调参数4.2 优化光追算法4.3 障碍物跟踪 1.背景 基于costmap地图,使用navigation导航时,会出现由于激光雷达/图像测距的局限性, …

由浅入深Netty粘包与半包解决方案

目录 1 粘包现象2 半包现象3 现象分析4 解决方案4.1 方法1:短链接4.2:方法2:固定长度4.3 方法3:固定分隔符4.4 方法4:预设长度 1 粘包现象 服务端代码 public class HelloWorldServer {static final Logger log Logg…

【libcurl 】win32 构建 Release版本 修改cmakelist 链接openssl1.1.*

以下库均已MD的构建以vs2019 V142构建MD构建 直接换用了一个openssl库,libcurl连接报错 $(ProjectDir)..\..\..\3rdparty\openssl\xdw_openssl1_1_1\lib\win32\libcrypto.lib

【Unity】 UI自适应案例

UI自适应案例 案例一:背包自动布局1. 创建背包面板2. 背包子项自动布局3. C#代码:动态添加子项到背包中案例二:文字自适应高度1. 创建文字面板2. 组件基本设置3. C#代码:动态更新文字并自适应高度案例一:背包自动布局 需求:动态添加背包组件,设定每行特定个数并自动匹配…

C++学习之路-变量和基本内置类型

变量和基本内置类型 一、基本内置类型1.1 算数类型1.2 带符号类型和无符号类型1.3 类型转换含有无符号类型的表达式 1.4 字面值常量整形和浮点型字面值字符和字符串字面值转义序列指定字面值的类型 二、变量2.1 变量的定义初始化列表初始化默认初始化 2.2 变量声明和定义的关系…

彻底理解粘性定位 - position: sticky(IT枫斗者)

彻底理解粘性定位 - position: sticky 介绍 粘性定位可以被认为是相对定位(position: relative)和固定定位(position: fixed)的混合。元素在跨越特定阈值前为相对定位,之后为固定定位。例如: .sticky-header { position: sticky; top: 10px; }在 视口滚动到元素…

python处理字符串、文本实例及注释

1、多个界定符切割字符串 代码 line = asdf fjdk; afed, fjek,asdf, foo import re re.split(r[;,\s]\s*, line) 结果 在上面的例子中,分隔符可以是逗号,分号或者是空格,并且后面紧跟着任意个的空格。只要这个模式被找到,那么匹配的分隔符两边的实体都会被当成是结果中…

【数据结构与算法】- 期末考试

课程链接: 清华大学驭风计划 代码仓库:Victor94-king/MachineLearning: MachineLearning basic introduction (github.com) 驭风计划是由清华大学老师教授的,其分为四门课,包括: 机器学习(张敏教授) , 深度学习(胡晓林教授), 计算…

可算是熬出头了,测试6年,费时8个月,入职阿里,涨薪14K

前言 你的努力,终将成就无可替代的自己。 本科毕业后就一直从事测试的工作,和多数人一样,最开始从事点点点的工作,看着自己的同学一步一步往上走,自己还是在原地踏步,说实话这不是自己想要的状态。 一年半…

在 Android 手机上恢复出厂设置后恢复照片的 4 种简单方法(新方法)

“嗨,谁能帮我恢复我的照片,因为我不小心恢复了出厂设置,而且我没有做备份?几个月来我一直试图通过使用恢复软件来恢复我的照片,root 了一个深扫描,但没用……” 恢复出厂设置可以清除电子设备的所有信息并…

Linux安装Redis数据库,无需公网IP实现远程连接

文章目录 1. Linux(centos8)安装redis数据库2. 配置redis数据库3. 内网穿透3.1 安装cpolar内网穿透3.2 创建隧道映射本地端口 4. 配置固定TCP端口地址4.1 保留一个固定tcp地址4.2 配置固定TCP地址4.3 使用固定的tcp地址连接 转发自cpolar内网穿透的文章:公网远程连接…

连续签到积分兑换试用流量主小程序开发

每日签到积分兑换试用流量主小程序开发 打卡兑奖小程序。用户签到活得积分。积分可以兑换商品。观看激励视频广告可以积分翻倍。 用户可以参加试用商品活动参加试用需要提交信息。可以通过分享方式直接获取试用资格。 以下是流量主小程序的功能列表: 广告位管理&a…

Java流程控制(一)

⭐ 控制语句⭐ 条件判断结构(选择结构)⭐ switch 语句 做任何事情事情都要遵循一定的原则,毕竟不以规矩,不成方圆,例如,到图书馆去借书,就必须要有借书证,并且借书证不能过期,这两个条件缺一不可…

Spring Boot 日志处理

Spring Boot 日志处理 Spring Boot 是一个非常流行的 Java 开发框架,它提供了简洁的配置和强大的开发工具。日志是应用程序中必不可少的一部分,因为它可以帮助开发人员进行调试和故障排除。Spring Boot 提供了多种日志框架,本文将重点介绍如…

Java泛型基本知识附面试题

一次平平无奇的面试 为什么要写这篇文档&#xff0c;主要就是在字节二面的时候&#xff0c;面试官提了这么一个问题 面试官&#xff1a;Java中的List<Integer>里有可能存String类型元素吗&#xff1f; 当时的我&#xff1a;应该…不可以吧&#xff0c;好像编译器会报错…

跟我一起使用 compose 做一个跨平台的黑白棋游戏(4)移植到compose-jb实现跨平台

前言 在上一篇文章中&#xff0c;我们已经实现了游戏的所有界面和逻辑代码&#xff0c;并且在 Android 上已经可以正常运行。 这篇文章我们将讲解如何将其从使用 jetpack compose 修改为使用 compose-jb 从而实现跨平台。 老规矩&#xff0c;先看效果图&#xff1a; 可以看到…