「Java」- String 字符串详解

本文详细介绍了Java中的String类,包括字符串的创建、比较相等、字符串常量池及intern()方法、不可变特性和常用操作。字符串通过==比较引用,equals()方法比较内容。字符串常量池可以减少内存开销,intern()方法用于手动入池。此外,文章还讨论了StringBuilder和StringBuffer的可变性、效率和线程安全性,以及它们与String的区别。
摘要由CSDN通过智能技术生成

目录

认识字符串

1.创建字符串

2.字符串比较相等

3.字符串常量池

intern( ) 方法

4.字符串不可变特性

字符串常用操作

1.字符串比较

2.字符串查找

3.字符串替换

4.字符串拆分

5.字符串截取

6.字符串其它方法

​ StringBuilder & StringBuffer 类

构造方法

常用方法

String & StringBuilder & StringBuffer 的区别

认识字符串

Java 中的 String 类代表字符串 , 在程序中使用 " " 定义的内容都是字符串 , 字符串是常量 , 它们的值在创建之后不能更改.

1.创建字符串

String类的构造方法

public String()
// 初始化新创建的 String 对象,使其表示空字符序列。
public String(byte[] bytes)
// 通过使用平台的默认字符集解码指定的字节数组来构造新的 String 。
public String(byte[] bytes,Charset charset)
// 构造一个新的 String 由指定用指定的字节的数组解码 charset
public String(byte[] bytes,  int offset, int length)
// 通过使用平台的默认字符集解码指定的字节子阵列来构造新的 `String` 。
public String(byte[] bytes,  int offset, int length, Charset charset)
// 构造一个新的 String 通过使用指定的指定字节子阵列解码charset
public String(byte[] bytes,  int offset, int length, String charsetName)
// 构造一个新的 String 通过使用指定的字符集解码指定的字节子阵列。
public String(byte[] bytes,  String charsetName)
// 构造一个新的 String 由指定用指定的字节的数组解码charset
public String(char[] value)
// 分配一个新的 String ,以便它表示当前包含在字符数组参数中的字符序列。
public String(char[] value,  int offset, int count)
// 分配一个新的 String ,其中包含字符数组参数的子阵列中的字符。
public String(int[] codePoints,  int offset, int count)
// 分配一个新的 String ,其中包含 Unicode code point数组参数的子阵列中的字符
public String(String original)
// 初始化新创建的 String 对象,使其表示与参数相同的字符序列
public String(StringBuffer buffer)
// 分配一个新的字符串,其中包含当前包含在字符串缓冲区参数中的字符序列。
public String(StringBuilder builder)
// 分配一个新的字符串,其中包含当前包含在字符串构建器参数中的字符序列。

常见的构造方式

String 类提供的构造方法太多太多了 , 挑几个常见或者常用的创建方式给大家演示一下

  • 方式一 , 最常用也最简单的方式 , 直接赋值

    String str = "Hello";
  • 方式二 , 给定字符串创建一个实例

    String str = new String("Hello");
  • 方式三 , 给定一个字符数组构造字符串

    char[] array = {'H','e','l','l','o'};
    String str = new String(array);

2.字符串比较相等

双等号==比较字符串

以上每种创建方式达到的效果都是一样 , 唯一的区别就是内存布局不一样了 , 先看一段代码

public static void main(String[] args) {
    String str1 = "Hello";
    String str2 = new String("Hello");
    System.out.println(str1 == str2);

    String str3 = "He" + "llo";
    System.out.println(str1 == str3);

    String str4 = "He";
    String str5 = "llo";
    String str6 = str4 + str5;
    System.out.println(str1 == str6);
}
// 运行结果
false
true
false

为什么明明看着差不多的代码 , 得到的答案就不是一样的呢 ? 这是因为 String 类是个引用类型 , 如果引用类型之间使用 == 比较 , 那么比较的就是引用类型的指向的对象是否相等 , 既然是比较对象为什么又会有相同的呢 ? 那么接下来我们分段来解析以上代码

代码一

String str1 = "Hello";
String str2 = new String("Hello");
System.out.println(str1 == str2);

// 运行结果
false

 首先我们要明确一个概念 , " " 引起的内容都是一个常量 , 而这个常量会放在内存中的堆区 ( 在JDK1.7开始,被移到堆区中 ) 的字符串常量池中 , 常量池中的字符串也只能存在一份 , 接下来看一下 代码 1 的内存布局

在代码1中 , str1 变量是直接指向的是在常量池中 "Hello" 的地址 , 而 str2 变量指向的是堆区另外一块地址 , 而那一块地址指向的是常量池中的 "Hello" 的地址 , 这是为什么 ? 这里就要先看看 String 类的底层实现了 , 如果你使用的开发工具是 IDEA 的话 , 只需要按住 ctrl + 鼠标左键 点击 代码中的 String 类 , 就能转到实现文件

点进来可以看到 , 在 String 类底层是用一个 value 字符数组用来存储字符串的 , 还有另一个变量用来存储字符串的 哈希 地址

回到代码中 , 我们查看一下 str2 的构造方法是如果实现的

点进来可以看到 , 构造方法是用本类的 value 数组引用传入的字符串参数的 value 数组 , 本类的 hash 变量保存 传入的字符串参数的 hash 变量 , 况且用new这个关键字的话,是调用new指令创建一个对象,然后调用构造方法来初始化这个对象 , 而 new 出来的对象都是放在堆区的 , 那么此时在内存的堆区就新创建了一个 String 类的对象 , 而这个对象指向的就是 字符串常量池 中的 "Hello" , 而 str2 变量引用的是堆区的 String 对象

现在可以知道 str1 变量是直接指向 字符串常量池"Hello" 的地址 , 而 str2 变量是指向堆区中的 String 对象的地址 , 两个指向的地址不同 , 所以结果是 false

代码二

String str1 = "Hello";
String str3 = "He" + "llo";
System.out.println(str1 == str3);

// 运行结果
true

代码 2 的结果为 true 的原因也很简单 , 因为 "He""llo" 两个都是字符串常量 , 代码在编译期间就会自动拼接成 "Hello"

所以 , str3 的代码就相当于 "String str3 = "Hello"; , 由于前面 str1 指向的 Hello 已经放入到在 字符串常量池 中了 , 所以 str3 变量产生的 "Hello" 字符串准备放入 字符串常量池 时 , 发现 "Hello" 已经存在 , JVM 此时就会将 字符串常量池 中的 "Hello"地址返回给 str3 ,这时 str1str3 指向的都是同一块地址 , 所以两个变量比较的结果等于 true

代码三

String str1 = "Hello";
String str4 = "He";
String str5 = "llo";
String str6 = str4 + str5;
System.out.println(str1 == str6);

这个代码看过去跟代码2差不多 , 也是两个字符串相加, 为什么这个比较结果是 false 呢 ? 这是因为 str4str5 是变量 , 在编译期间 , 并不知道 变量中放的是什么内容 , 只有在运行的时候才能知道 , 来看一下内存图

为什么 str6 会在堆中产生一个 String 对象呢 ? 我们可以反编译代码查看一下执行过程 , 先在 IDEA 中找到终端 , 然后将目录切换至要反编译的字节码文件目录下 , 输入 Javap -c <字节码文件> >> <指定文件> 指令 , 生成一个文本文件 , 文本文件中包含了反编译的信息

打开生成的文本文件 , 在编译文件中可以看到 , 代码在执行的过程中 , 创建了一个 StringBuilder 对象进行拼接字符串 , 主要是在后面还调用了一个 toString() 方法

我们转到 StringBuilder 类实现文件下查看 , 发现 StringBuilder 类实现的 toString() 方法是返回一个 String 对象

而这个 String 对象正是 str6 变量引用的 , 在这个 String 对象中传入的参数正是 "He""llo" 拼接得到的 "Hello" , 所以 str6 并不是直接指向 字符串常量池 中的 Hello , 而是指向堆区中的 String 对象 , 再由这个 String 去指向 "Hello" , 所以和 str1 进行比较 , 比较的结果是 false

equals( )方法比较字符串

String 使用 == 比较并不是在比较字符串内容, 而是比较两个引用是否是指向同一个对象 , Java中要想比较字符串内容的话 , 必须采用 String 类 提供的 equals 方法

public static void main(String[] args) {
    String str1 = "Hello";
    String str2 = new String("Hello");
    System.out.println(str1.equals(str2));
    
    String str3 = "He" + "llo";
    System.out.println(str1.equals(str3));

    String str4 = "He";
    String str5 = "llo";
    String str6 = str4 + str5;
    System.out.println(str1.equals(str6));
}

// 运行结果
true
true
true

equals 使用注意事项

如果一个 String变量 和 字符串常量 比较的话有两种方式

String str = new String("Hello");

// 方式一
System.out.println(str.equals("Hello"));
// 方式二
System.out.println("Hello".equals(str));

"Hello" 这样的字面值常量, 本质上也是一个 String 对象, 完全可以使用 equals 等 String 对象的方法 , 两种方式更推荐写成方式二 , 因为一旦 str 值为 null , 方式一 会抛出异常 , 而方式二能正常运行

  • 50
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 46
    评论
评论 46
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值