Java字符串(String、字符串拼接、原理)

文章目录

在Java中,String是一个类,用于表示字符串。它位于java.lang包下,需要进行导入语句就可以使用String类。String类用于存储和操作字符串数据。

一、String字符串

在Java中,String 是一个用于表示字符串的类。String 对象是不可变的,这意味着一旦创建了一个字符串对象,它的值就不能被改变。

java 复制代码
String str = "Hello";
//使用字符串字面值创建字符串对象。创建了一个字符串变量`str`,并将其赋值为`"Hello"`。
String str = new String("Hello");
//通过使用`new`关键字显式地创建了一个新的`String`对象,
//内容为`"Hello"`。这种方式会在堆内存中创建一个新的字符串对象。

String str = "Hello" + "World";
//**拼接是产生一个新的字符串**,通过字符串拼接操作将`"Hello"`和`"World"`连接起来,最终赋值给`str`。这种方式会在编译时进行字符串拼接优化。

String str = String.valueOf(123);
//这句将整数`123`转换为字符串类型,并将其赋值给`str`。

1.1创建方式【直接赋值、new一个对象】

1.1.1 使用字符串字面值直接赋值:

java 复制代码
String str1 = "Hello, World!";
(1)字符串字面量创建String对象的转换过程

字符串字面量创建String对象的转换过程主要包括在字符串常量池中进行查找和创建 对象的两个步骤。

如果字符串常量池中不存在相同内容的字符串,就会创建新的字符串对象;

如果存在相同内容的字符串,就直接返回对应的字符串引用。最终,我们可以通过String类型的变量来引用这个被转化后的String对象。

(2)一些方法
java 复制代码
String x="abc"
x.length();//字符串长度
x.charAt(int index);//返回下标字符
x.concat(y);//用来连接两个字符串
(3)说明

字符串字面值可以直接赋值给String类型的变量,而无需使用new关键字创建新的字符串对象。

当使用相同的字符串字面值创建多个String对象时,实际上它们会引用同一个常量池中的字符串对象。这种优化机制可以节省内存空间,并提高字符串比较的效率。

复制代码
好处:节省内存空间、提高字符串比较效率

需要注意的是,这种优化只适用于字符串字面值,而不适用于使用new关键字创建的字符串对象。使用new关键字创建的字符串对象会在堆内存中单独分配空间,并不会放入字符串常量池中。

java 复制代码
String str1 = "aaa";
String str2 = "aaa";

System.out.println(str1 == str2); // true,因为编译器会将相同的字符串常量指向同一个对象

str1和str2都是指向字符串常量池中的同一个"aaa"字符串对象,所以可以直接调用str1,无需使用new关键字创建新的对象。

1.1.2 使用new关键字创建字符串对象,将内容赋值给变量:

这种方式会在堆内存中创建一个新的字符串对象,无论原字符串常量池中是否已经存在相同内容的字符串,即使存在也会创建一个新的对象。

使用new关键字创建的字符串对象则会在堆内存中进行分配。

因为字符串是不可变的(immutable)对象,在大部分情况下,直接使用字符串字面量来创建字符串对象更加高效和推荐,而使用new String()的方式主要用于特定业务需求或者对字符串常量的副本进行修改的情况。

java 复制代码
String str2 = new String("Hello, World!");
(1)String类有多个构造函数,其中一些常用的包括:
  1. String(): 创建一个空字符串。
  2. String(String original): 根据指定的字符串创建一个新的字符串。
  3. String(char[] value): 根据字符数组的内容创建一个新的字符串。
  4. String(char[] value, int offset, int count): 根据字符数组的一部分内容创建一个新的字符串。
  5. String(byte[] bytes): 根据字节数组的内容使用平台默认字符集创建一个新的字符串。
  6. String(byte[] bytes, int offset, int length): 根据字节数组的一部分内容使用平台默认字符集创建一个新的字符串。
(2)说明

使用new关键字创建字符串对象时,会在堆内存中单独为该字符串分配空间,并且不会共享字符串常量池中的对象。

每次使用new关键字创建字符串对象时,都会得到一个新的、独立的字符串对象。

java 复制代码
String str1 = new String("aaa");
String str2 = new String("aaa");

System.out.println(str1 == str2); // false,因为使用new关键字创建的是两个独立的对象
System.out.println(str1.equals(str2)); // true,因为内容相同

它们在内存中是两个独立的对象。即使两个字符串的内容相同,它们的引用也是不同的。

由于new关键字创建的字符串对象不会共享字符串常量池中的对象,因此在进行字符串比较时,应使用equals()方法而不是简单的引用比较。

(3) 引用的是哪里的,最后又在哪里

在Java中,引用通常指的是对象的引用。

变量存储的是对象的引用,而不是对象本身。当你创建一个对象时,实际上是在内存中分配了一块空间,并返回了对该空间的引用。

在栈内存中创建了一个变量,并将该变量指向堆内存中的对象

举例子

例如1:

复制代码
String str = "Hello";

在这个例子中,str是一个引用类型的变量。在栈内存中,会创建一个名为str的变量,并且该变量保存了堆内存中字符串"Hello"的地址(或者说引用)。通过该引用可以访问到堆内存中存储的字符串对象。

当我们通过new关键字创建一个字符串对象时,Java会在堆内存中分配一块空间来存储该对象,并返回其地址(引用)给我们。

例如2:

复制代码
String str = new String("World");

在这个例子中,使用new关键字创建了一个字符串对象"World",并且将该对象的地址赋值给变量str。现在变量str指向堆内存中的字符串对象"World"。

需要注意的是,引用本身只是一个指向对象的地址,在栈内存中占用的空间相对较小。而实际的对象数据存储在堆内存中,占据更大的内存空间。

总结起来,引用保存在栈内存中,用于指向堆内存中的对象。通过引用可以访问、操作堆内存中的对象数据。

1.1.3 区别

  • 使用字符串字面值创建字符串时,如果字符串常量池中已经存在相同数值的字符串,则会直接引用该字符串,而不会创建新的对象。

  • 使用new关键字创建字符串对象时,每次都会创建一个新的字符串对象,即使字符串常量池中已经存在相同数值的字符串。

1.1.4 注意点:

(1)字符串拼接产生新的字符串

即下面的代码运行过程中是三个字符串

java 复制代码
String hello="hello";
String world="world";
Sout(hello+world)

任何对字符串内容的更改都需要创建一个新的字符串对象来存储更改后的内容。

当我们创建一个字符串变量时,计算机会为这个字符串分配一定的内存空间,这个空间是静态分配的,也就是说这个空间的大小是固定的,不能动态地改变大小。

(2)Java中为什么获取一个长度的时候,数组就是length,而字符串就得是length()要多加上个括号???
复制代码
在Java中,获取数组的长度使用的是length属性,、
而获取字符串的长度需要使用length()方法。

(1)这是因为数组在Java中是一个固定大小的容器,其长度是数组类型的属性,可以直接通过length属性访问。

(2)而字符串是一个对象,在Java中使用String类表示,它有一个内置的方法length()用于返回字符串的长度。

所以,数组是通过属性来获取长度,而字符串是通过方法来获取长度,因此在字符串上需要使用length()方法,并且由于方法需要调用,所以需要使用一对括号。

1.2 字符串比较内容

1.2.1 "==" 比较

(1)基本数据类型比较的是----数据值

(2)引用数据类型比较的是----地址值

java 复制代码
import java.util.Scanner;

public class Test1{
    public static void main(String[] args) {
        String str1 = "aaa";
        String str2 = "aaa";
        System.out.println(str1 == str2); // true,因为编译器会优化,将相同的字符串引用指向同一个对象

        String str3 = new String("aaa");
        System.out.println(str1 == str3); // false,使用new关键字创建新的对象,所以引用地址不同
        byte[] bytes = {97, 97, 97};
        String str4 = new String(bytes);
        System.out.println(str1 == str4); // false,通过字节数组创建的新对象,引用地址不同
        System.out.println(str3 == str4); // false,str3和str4是不同的对象

        Scanner sc = new Scanner(System.in);
        String str5 = sc.next();//控制台输入aaa
        System.out.println(str1 == str5); // false,用户输入的字符串是新的对象
        System.out.println(str3 == str5); // false
        System.out.println(str4 == str5); // false
        
        sc.close();
    }
}

1.2.2 equals方法比较

java 复制代码
String str1 = "hello";
String str2 = "hello";
System.out.println(str1.equals(str2));//true

equals方法用于比较两个字符串的内容是否相等

java 复制代码
import java.util.Arrays;
import java.util.Objects;
import java.util.Scanner;

public class Test1{
    public static void main(String[] args) {
        String str1 = "aaa";
        String str2 = "aaa";
        System.out.println(str1.equals(str2));

        String str3 = new String("aaa");
        System.out.println(str1.equals(str3));

        byte[] bytes = {97, 97, 97};
        String str4 = new String(bytes);
        System.out.println(str1.equals(str4));
        System.out.println(str3.equals(str4));

        Scanner sc = new Scanner(System.in);
        String str5 = sc.next();
        for (String s : Arrays.asList(str1, str3, str4)) {
            System.out.println(Objects.equals(s, str5));
        }

        sc.close();
    }
}

1.2.3--equalsIgnoreCase --验证码常用的方法

equalsIgnoreCase方法用于比较两个字符串的内容是否相等,忽略大小写

1.3 常用方法

1.3.1 遍历

java 复制代码
String str = "hello";
//length() - 返回字符串的长度
for(int i=0;i<str.length();i++){
	//charAt(int index) - 返回指定索引处的字符
    System.out.println(str.charAt(i));
}

另一种常用的方法是使用增强的for循环(也称为foreach循环)来遍历字符串中的每个字符。

java 复制代码
String str = "Hello, World!";
for (char ch : str.toCharArray()) {
    System.out.println(ch);
}

toCharArray()是Java中String类的一个方法,用于将字符串转换为字符数组。

把字符串str转换成字符数组是因为在Java中,字符串是一个对象,而字符数组是字符的有序集合。

通过将字符串转换为字符数组,可以按照字符的顺序逐个遍历和访问每个字符元素。

为了更高效地遍历字符串每个元素,通常建议使用字符数组(char array)或者 StringBuilder 类。字符数组是可变的,可以直接修改其中的元素,而 StringBuilder 类提供了可变的字符串序列,可以高效地进行字符串操作。

直接访问字符串中的每个字符,而无需创建新的字符串对象:

使用字符数组:

java 复制代码
String str = "Hello";
char[] charArray = str.toCharArray();

for (char ch : charArray) {
    System.out.println(ch);
}

使用 StringBuilder 类:

java 复制代码
String str = "World";
StringBuilder sb = new StringBuilder(str);

for (int i = 0; i < sb.length(); i++) {
    char ch = sb.charAt(i);
    System.out.println(ch);
}

1.3.2 统计字符

java 复制代码
public class CharacterCount {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        System.out.print("请输入一个字符串: ");
        String str = scanner.nextLine();
        
        // 统计大写字母、小写字母和数字字符的次数
        int uppercaseCount = 0;
        int lowercaseCount = 0;
        int digitCount = 0;
        
        // 遍历字符串中的每个字符
        for (char ch : str.toCharArray()) {
            if (Character.isUpperCase(ch)) {
                uppercaseCount++;
            } else if (Character.isLowerCase(ch)) {
                lowercaseCount++;
            } else if (Character.isDigit(ch)) {
                digitCount++;
            }
        }
        
        System.out.println("大写字母个数: " + uppercaseCount);
        System.out.println("小写字母个数: " + lowercaseCount);
        System.out.println("数字个数: " + digitCount);
    }

1.3.3 分割:

使用split()方法将一个字符串分割成多个子字符串。

java 复制代码
// 字符串分割
String sentence = "Java is a programming language";
String[] words = sentence.split(" ");
System.out.println("分割后的字符串数组:");
for (String word : words) {
   System.out.println(word);
}

1.3.4 截取

java 复制代码
public String substring(int beginIndex) 
public String substring(int beginIndex, int endIndex)//左闭右开
java 复制代码
// 字符串截取
String originalString = "Hello World";
String substring = originalString.substring(6);
System.out.println("截取子字符串:" + substring);//截取子字符串:World

1.3.5 替换:

方法的返回值是替换的值

java 复制代码
public String replace(char oldChar, char newChar)

使用replace()方法将一个字符串中的某个子串替换为另一个字符串。

java 复制代码
// 字符串替换
String originalSentence = "I love apples";
String replacedSentence = originalSentence.replace("apples",
                "oranges");
System.out.println("替换后的字符串:" + replacedSentence);//替换后的字符串:I love oranges

1.3.6 大小写转换:

  • 使用toUpperCase()方法将字符串中所有字符转换为大写。
  • 使用toLowerCase()方法将字符串中所有字符转换为小写。
java 复制代码
// 字符串大小写转换
String lowercaseString = "hello world";
String uppercaseString = lowercaseString.toUpperCase();
System.out.println("转换为大写字母:" + uppercaseString);//转换为大写字母:HELLO WORLD

1.3.7 字符串转换:

  • 使用valueOf()方法将其他数据类型转换为字符串。
  • 使用parseXxx()方法将字符串转换为其他数据类型。
java 复制代码
// 字符串转换
int number = 42;
String numberString =String.valueOf(number);
System.out.println("转换为字符串:" + numberString);//转换为字符串:42

1.3.8 字符串格式化:

  • 使用String.format()方法将数据格式化成特定的字符串形式。
java 复制代码
// 字符串格式化
String formattedString = String.format("The value of PI is approximately %.2f", Math.PI);
System.out.println("格式化后的字符串:" + formattedString);//格式化后的字符串:The value of PI is approximately 3.14

1.3.9 字符串查找:

  • 使用indexOf()方法查找指定字符或子字符串在字符串中的位置。
java 复制代码
// 字符串查找
String phrase = "Java programming language";
int index = phrase.indexOf("programming");
System.out.println("'programming'第一次出现的位置:" + index);//'programming'第一次出现的位置:5

使用lastIndexOf()方法查找指定字符或子字符串在字符串中的位置。

1.3.10 字符串判断:

使用startsWith()方法判断字符串是否以指定的前缀开头。

java 复制代码
// 字符串判断
String startsWithExample = "Hello World";
boolean startsWithHello = startsWithExample.startsWith("Hello");
System.out.println("是否以'Hello'开头:" + startsWithHello);//是否以'Hello'开头:true

使用endsWith()方法判断字符串是否以指定的后缀结尾。

1.3.11 字符串去除空格:

使用trim()方法去除字符串两端的空格。

java 复制代码
// 字符串去除空格
String stringWithSpaces = " Trim me ";
String trimmedString = stringWithSpaces.trim();
System.out.println("去除空格后的字符串:" + trimmedString);

1.3.12 字符串格式验证:

使用正则表达式和matches()方法验证字符串是否符合特定的格式要求。

java 复制代码
// 字符串格式验证
String email = "[email protected]";
boolean isValidEmail = email.matches("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}");
System.out.println("是否是有效的邮箱地址:" + isValidEmail);

1.3.13 字符串重复:

使用repeat()方法将一个字符串重复指定次数。

java 复制代码
// 字符串重复
String repeatedString = "Java".repeat(3);
System.out.println("重复字符串:" + repeatedString);
// 输出:重复字符串:JavaJavaJava
}

1.4 名词解释

  • (1)字符串常量池:字符串常量池是Java中的一个特殊存储区域,用于存储字符串字面值。当使用字符串字面值创建字符串时,如果字符串常量池中已经存在相同数值的字符串,则会直接引用该字符串,而不会创建新的对象。

    可以提高内存的利用率和效率。而通过new关键字创建的字符串对象不会被保存在字符串常量池中。

  • (2)字面量 :在编程中,字面量是表示固定值的符号表示法。在Java中,字符串字面值是指直接使用双引号括起来的字符串文本,例如"Hello, World!"就是一个字符串字面值。

当我们使用字符串字面值创建字符串对象时,Java会先在字符串常量池中查找是否存在相等的字符串。如果存在,则返回常量池中对应的引用;如果不存在,则在常量池中创建新的字符串并返回引用。

  • (3)堆内存(Heap Memory):除了字符串常量池外,Java中的字符串对象也可以存储在堆内存中。当我们使用关键字new来创建一个字符串对象时,该对象会被存储在堆内存中,并且不会进入字符串常量池。每次通过new创建的字符串对象都会在堆内存中分配新的空间,即使字符串的内容相同。

  • (4)长度为0的字符串和null的

  • 【1】空串

    空串是指长度为0的字符串,也就是不包含任何字符的字符串。在Java中,空串可以用双引号""表示。

  1. 使用双引号表示空字符串:
java 复制代码
String emptyString = "";
  1. 使用String类的构造函数创建一个空字符串对象:
java 复制代码
String emptyString = new String();
  1. 证明:
java 复制代码
if(str.length()==0)
if(str.equals(""))
str.isEmpty()

【2】Null是一个特殊的值

在Java中,null是一个特殊的关键字,表示一个变量不引用任何对象。当一个对象引用被赋予null值时,表示该引用不指向任何有效的对象实例。在这种情况下,任何对该引用的方法调用都会导致NullPointerException异常。

java 复制代码
if(str==null)
if(str!=null&&str.length()!=0)

if (str == null) 检查字符串引用是否指向null,即字符串对象是否未实例化。如果str为null,表示字符串对象不存在。

if (str != null && str.length() != 0) 则首先检查字符串引用是否不为null,然后再检查字符串的长度是否不为0。这个条件用于确保字符串既不为null,又不是空串。

因此,第一个条件主要检查字符串是否为null,而第二个条件则进一步确保字符串既不为null,又不是空串

复制代码
null	这个值可以是任何类型的对象,包括字符串、数组、类等等。当一个对象被赋值为null时,
它就不再指向任何对象,也就是说它不再引用任何对象,因此也就无法访问该对象的任何属性或方法。
  • 【3】区别
    空串是一个长度为0的字符串,表示一个有效的字符串对象,而null表示一个变量未引用任何对象。
    在Java中,空串是一个字符串对象,而null是一个特殊的关键字,表示缺少对象引用。

二、StringBuilder构建字符串(容器)内容可变

StringBuilder是Java中用于处理可变字符串的类。它位于java.lang包下。

其参与到的字符串进行修改,不会创建新的字符串对象,这在需要频繁修改字符串时可以提高性能。

2.1 分析

空参构造: public StringBuilder() 创建一个空白可变字符串对象,不含有任何内容

有参构造: public StringBuilder(String str) 根据字符串的内容,来创建可变字符串对象

java 复制代码
- StringBuilder():创建一个空的StringBuilder对象,初始容量为16个字符。
- StringBuilder(CharSequence seq):创建一个StringBuilder对象,并将指定的字符序列初始化为其内容。
- StringBuilder(int capacity):创建一个指定初始容量的StringBuilder对象。
- StringBuilder(String str):创建一个StringBuilder对象,并将指定的字符串初始化为其内容。
java 复制代码
StringBuilder sb = new StringBuilder(); // 默认创建一个长度为16的字符数组
System.out.println("最多能存储的字符数:" + sb.capacity()); // 输出最多能存储的字符数
sb.append("Hello, World!"); // 向StringBuilder中添加字符串
System.out.println("实际存储的字符数:" + sb.length()); // 输出实际存储的字符数

2.1.1 用到检测时间证明快速

(1)时间戳

要获取Java代码的运行时间,可以使用System.currentTimeMillis()方法。在给定的代码中,这个选择了System.currentTimeMillis()方法是正确的。这个方法返回自1970年1月1日以来的毫秒数,可以用来计算代码的执行时间。

使用System.currentTimeMillis()方法的返回值保存在一个变量中,然后在代码执行完毕后再次调用System.currentTimeMillis()方法,将两个时间戳相减,就可以得到代码的执行时间。

java 复制代码
public static void main(String[] args) {
   long startTime = System.currentTimeMillis();
   //此处需要检验操作事件的执行的雨具块
   long endTime = System.currentTimeMillis();
   long executionTime = endTime - startTime;
   System.out.println("代码执行时间:" + executionTime + "毫秒");
}
(2)开始对比

【1】普通的拼接操作:

每次字符串拼接操作都会创建一个新的字符串对象

java 复制代码
public static void main(String[] args) {
    long startTime = System.currentTimeMillis();
    String str = "";
    for (int i = 1; i < Math.pow(10, 5); i++) {
        str += "abc ";
    }
    System.out.println(str);
    System.out.println(str.toString());
    System.currentTimeMillis();
    long endTime = System.currentTimeMillis();
    long executionTime = endTime - startTime;
    System.out.println("代码执行时间:" + executionTime + "毫秒");
}

【2】StringBuilder拼接:

内部维护了一个可变的字符数组用于存储字符串,每次拼接只需修改数组中的内容,而不需要创建新的字符串对象

java 复制代码
public static void main(String[] args) {
    //StringBuilder拼接
    StringBuilder str = new StringBuilder();
    long startTime = System.currentTimeMillis();
    for (int i = 0; i < Math.pow(10, 5); i++) {
        str.append("abc ");
    }
    str.append(123).append(123123);//链式调用
    System.out.println(str);
    System.out.println(str.toString());
    long endTime = System.currentTimeMillis();
    long executionTime = endTime - startTime;
    System.out.println("StringBuilder拼接-代码执行时间:" + executionTime + "毫秒");
    }

2.2 toString

StringBuilder 用于动态构建字符串,而 toString() 方法用于将 StringBuilder 对象转换为一个字符串。当你需要将 StringBuilder 对象的内容作为一个字符串来处理时,就需要使用 toString() 方法。

对于StringBuilder对象,调用toString()方法会返回包含StringBuilder对象内容的String对象。这样可以方便地在StringBuilder和String之间进行转换,以便进行字符串的进一步处理或者与其他String对象进行拼接等操作。

2.3 需要将StringBuilder对象转换为String对象的主要原因

StringBuilder对象用于处理可变的字符串,允许在不创建新的字符串对象的情况下进行字符串操作,这在需要频繁修改字符串内容时非常高效。但有时候需要将StringBuilder对象转换为String对象,比如当需要将最终的字符串结果传递给需要String类型参数的方法时,或者希望保留字符串的不可变性。

String不可变

java 复制代码
public static void main(String[] args) {
    String str = new String("abc");
    System.out.println(str);
    System.out.println(str.hashCode());
    str = "123";
    System.out.println(str);
    System.out.println(str.hashCode());
    str = "abc";
    System.out.println(str.hashCode()); 
}

2.4 常用方法

  • append(String str):将指定的字符串追加到StringBuilder对象的末尾。
  • insert(int offset, String str):在指定位置插入指定的字符串。
  • delete(int start, int end):删除指定范围内的字符。
  • replace(int start, int end, String str):将指定范围内的字符替换为指定的字符串。
  • reverse():将StringBuilder对象中的字符顺序反转。-- 常用于对称问题
  • toString():将StringBuilder对象转换为字符串String。
  • length():获取StringBuilder对象中的字符数量。
java 复制代码
public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    System.out.print("请输入字符串:");
    String inputString = scanner.nextLine();
    //翻转
    StringBuilder stringBuilder = new StringBuilder(inputString);//或者用append()方法
    stringBuilder.reverse();
    
    //tostring
    String reversedString = stringBuilder.toString();
	System.out.println("翻转后的字符串:" + reversedString);
    }

链式编程一步到位 StringBuilder stringBuilder= new StringBuilder(inputString).reverse().toString();

StringBuilder对象调用了控制台输出语句时,java底层会自动调用StringBuilder中重写后的toString()方法

java 复制代码
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" World");
system.out.println(sb); //Java在底层的处理,打印对象不是地址值而是属性值
sb="123";
System.out.println(sb); //123
System.out.println(sb.toString()); // 输出:Hello World

sb.insert(6, "Java");
System.out.println(sb.toString()); // 输出:Hello Java World

sb.delete(0, 5);
System.out.println(sb.toString()); // 输出:Java World

sb.replace(0, 5, "Hello");
System.out.println(sb.toString()); // 输出:Hello World

sb.reverse();
System.out.println(sb.toString()); // 输出:dlroW olleH

StringBuilder sb2 = new StringBuilder("Hello");
System.out.println(sb2.toString()); // 输出:Hello

StringBuilder sb3 = new StringBuilder(10);
System.out.println(sb3.toString()); // 输出:空字符串

StringBuilder sb4 = new StringBuilder("Hello World");
System.out.println(sb4.toString()); // 输出:Hello World

2.5 不会创建很多无用的空间,节约内存

在Java中,StringBuilder用于创建可变的字符串对象,适合在需要频繁修改字符串内容的场景下使用。与String不同,StringBuilder不会创建新的对象,而是直接在原有对象上进行修改,从而提高了性能和节约了内存。

java 复制代码
public class StringBuilderExample {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        
        sb.append("Hello");
        sb.append(" ");
        sb.append("World");
        
        String result = sb.toString();
        System.out.println(result);  // 输出: Hello World
    }
}

在这个示例中,我们使用StringBuilder的append方法来拼接字符串,最后通过toString方法将其转换为String对象。这样做可以避免创建多个中间String对象,从而节约内存。

2.加粗样式6 使用CharSequence接口

CharSequence是Java中的一个接口,它是许多字符串类型的通用父接口,包括StringStringBuilderStringBuffer。它定义了一些基本的方法,如length()charAt(int index)subSequence(int start, int end),这些方法在所有实现了CharSequence接口的类中都可以使用。

java 复制代码
public class CharSequenceExample {
    public static void main(String[] args) {
        CharSequence cs1 = "Hello, World!";
        CharSequence cs2 = new StringBuilder("Hello, StringBuilder!");
        CharSequence cs3 = new StringBuffer("Hello, StringBuffer!");

        printCharSequence(cs1);
        printCharSequence(cs2);
        printCharSequence(cs3);
    }

    public static void printCharSequence(CharSequence cs) {
        System.out.println("Length: " + cs.length());
        System.out.println("First character: " + cs.charAt(0));
        System.out.println("Subsequence (0, 5): " + cs.subSequence(0, 5));
    }
}

在这个示例中,我们创建了三个不同类型的CharSequence对象,并使用一个通用的方法来打印它们的长度、首字符和子序列。这样可以展示CharSequence接口的多态性。

2.7 在Java中,为了避免创建大量的临时字符串对象,我们可以使用StringBuilder或StringBuffer类来进行字符串拼接操作。

这种方式利用了可变字符序列的特性,在内部只会创建一个StringBuilder(或StringBuffer)对象,并在这个对象中逐步修改和拼接字符串,避免了频繁地创建临时字符串对象,从而节约了内存空间。

三、Stringjoiner:特定的分隔符构建字符串序列,用来连接字符串

复制代码
StringJoiner类是在Java 8中引入的,作为Java标准库的一部分。
它提供了一种简便的方式来连接多个字符串,并且在连接过程中可以指定分隔符、前缀和后缀等信息。

StringJoiner用于以特定的分隔符构建字符串序列。它提供了一种方便的方式来连接字符串,并控制它们之间的分隔符。通过指定分隔符和可选的前缀和后缀,可以添加多个字符串,并使用指定的分隔符将它们转换为单个字符串。

java 复制代码
public static void main(String[] args) {
    int[] arr = { 1, 'A', 3, 4 };
    String strs = arrToString(arr);
    System.out.println(strs); // [1, A, 3, 4]
}

public static String arrToString(int[] arr) {
    StringJoiner sj = new StringJoiner(", ", "[", "]");
    for (int c : arr) {
       sj.add(String.valueOf(c));
    }
    return sj.toString();
}

3.1 例如

java 复制代码
public static void main(String[] args) {
    List<String> strings = Arrays.asList("apple", "banana", "orange");
        
    StringJoiner joiner = new StringJoiner(", "); // 使用逗号和空格作为分隔符
    for (String s : strings) {
        joiner.add(s);
    }

    String result = joiner.toString();
    System.out.println(result); // 输出:apple, banana, orange
}

我们首先创建了一个StringJoiner对象 joiner,并指定了逗号和空格作为分隔符。然后遍历字符串列表,将每个字符串都添加到StringJoiner对象中。最后调用toString()方法获取拼接后的字符串结果。

3.2 介绍 -- StringJoiner的出现主要是为了简化字符串连接的操作。

java 复制代码
public class StringJoiner {
    // 构造函数:创建一个新的StringJoiner对象
    public StringJoiner(CharSequence delimiter)
    
    // 构造函数:创建一个新的StringJoiner对象,指定分隔符和前缀、后缀
    public StringJoiner(CharSequence delimiter, CharSequence prefix, CharSequence suffix)

    // 添加一个元素到StringJoiner中
    public StringJoiner add(CharSequence element)
    
    // 合并另一个StringJoiner对象到当前对象中
    public StringJoiner merge(StringJoiner other)
    
    // 获取当前StringJoiner对象中的字符串结果
    public String toString()
}

StringJoiner类提供了多个构造函数来创建实例。第一个构造函数传入一个分隔符 delimiter,用于指定在连接字符串时使用的分隔符。第二个构造函数还可以传入一个前缀 prefix 和一个后缀 suffix,用于在最终的连接结果前面和后面添加额外的字符串。

通过调用add()方法,可以将一个元素添加到StringJoiner对象中。可以连续调用add()方法以添加多个元素。

使用merge()方法可以合并另一个StringJoiner对象的内容到当前对象中。

java 复制代码
StringJoiner sj1 = new StringJoiner(",");
sj1.add("apple");
sj1.add("banana");

StringJoiner sj2 = new StringJoiner(":");
sj2.add("car");
sj2.add("bike");

sj1.merge(sj2);
System.out.println(sj1.toString()); // 输出:apple,banana,car:bike

最后,调用toString()方法可以获取StringJoiner对象中连接后的字符串结果。

java 复制代码
StringJoiner sj = new StringJoiner("-");
sj.add("Java");
sj.add("Python");
sj.add("C++");

String result = sj.toString();
System.out.println(result); // 输出:Java-Python-C++

再例如:

java 复制代码
public class Main {
    public static void main(String[] args) {
        StringJoiner stringJoiner = new StringJoiner(", ", "[", "]"); 
        
        stringJoiner.add("Apple"); // Add "Apple" to the StringJoiner
        stringJoiner.add("Banana"); // Add "Banana" to the StringJoiner
        stringJoiner.add("Orange"); // Add "Orange" to the StringJoiner
        
        String result = stringJoiner.toString(); // Get the string result of the StringJoiner
        
        System.out.println(result); // Output: [Apple, Banana, Orange]
    }
}

四、字符串原理

4.1 直接会复用字符串常量值或者new一个

Java中的字符串存储内存原理可以简单归纳为:由于字符串一旦创建,就不能修改它的值,其创建通过字符串常量池实现字符串的共享和复用,提高性能和节省内存空间;而使用new关键字创建的字符串对象则会在堆内存中分配独立的空间。

4.2 字符串拼接的底层原理

  1. 如果没有变量参与,都是字符串直接相加,编译之后就是拼接之后的结果,会复用字符串池中的字符串。
  2. 如果有变量参与,每一行拼接的代码,都会在内存中创建新的字符串,浪费内存。
java 复制代码
public class StringConcatenation {
    public static void main(String[] args) {
        // 没有变量参与的字符串拼接
        String str1 = "Hello, " + "world!";
        System.out.println(str1); // 输出: Hello, world!

        // 有变量参与的字符串拼接
        String str2 = "Hello, ";
        String str3 = str2 + "world!";
        System.out.println(str3); // 输出: Hello, world!
    }
}

在第一个例子中,字符串拼接在编译时完成,结果是一个常量字符串,存储在字符串池中。而在第二个例子中,字符串拼接在运行时完成,会在内存中创建新的字符串对象。

在编译时已经确定了要拼接的字符串,并且没有涉及变量时,Java编译器会将连续的字符串字面值直接连接在一起

这种情况下的字符串拼接操作会在编译时被优化为一个单独的字符串常量。

例如,以下代码片段:

java 复制代码
String str = "Hello" + ", " + "World!";

在编译时,会被优化为:

java 复制代码
String str = "Hello, World!";

请注意,==这种优化只适用于字符串字面值的拼接,而不适用于包含变量的字符串拼接。==在涉及变量的情况下,仍然建议使用 StringBuilder 或 StringBuffer 来进行字符串拼接,以避免频繁创建临时对象和提高性能。

相关推荐
q567315233 分钟前
Java使用Selenium反爬虫优化方案
java·开发语言·分布式·爬虫·selenium
kaikaile19957 分钟前
解密Spring Boot:深入理解条件装配与条件注解
java·spring boot·spring
守护者17021 分钟前
JAVA学习-练习试用Java实现“一个词频统计工具 :读取文本文件,统计并输出每个单词的频率”
java·学习
bing_15833 分钟前
Spring Boot 中ConditionalOnClass、ConditionalOnMissingBean 注解详解
java·spring boot·后端
ergdfhgerty35 分钟前
斐讯N1部署Armbian与CasaOS实现远程存储管理
java·docker
勤奋的知更鸟1 小时前
Java性能测试工具列举
java·开发语言·测试工具
三目君1 小时前
SpringMVC异步处理Servlet
java·spring·servlet·tomcat·mvc
用户0595661192091 小时前
Java 基础篇必背综合知识点总结包含新技术应用及实操指南
java·后端
fie88891 小时前
Spring MVC扩展与SSM框架整合
java·spring·mvc
不太可爱的叶某人1 小时前
【学习笔记】深入理解Java虚拟机学习笔记——第3章 垃圾收集器与内存分配策略
java·笔记·学习