首页 > 编程语言 >Java字符串(String、字符串拼接、原理)

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

时间:2024-07-09 22:56:43浏览次数:12  
标签:Java String 对象 StringBuilder System 字符串 out

文章目录

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

一、String字符串

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

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 使用字符串字面值直接赋值:

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

字符串字面量创建String对象的转换过程主要包括在字符串常量池中进行查找和创建对象的两个步骤。
如果字符串常量池中不存在相同内容的字符串,就会创建新的字符串对象;
如果存在相同内容的字符串,就直接返回对应的字符串引用。最终,我们可以通过String类型的变量来引用这个被转化后的String对象。

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

在这里插入图片描述

(3)说明

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

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

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

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

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()的方式主要用于特定业务需求或者对字符串常量的副本进行修改的情况。

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关键字创建字符串对象时,都会得到一个新的、独立的字符串对象。

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)字符串拼接产生新的字符串

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

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)引用数据类型比较的是----地址值

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方法比较

String str1 = "hello";
String str2 = "hello";
System.out.println(str1.equals(str2));//true

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

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 遍历

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

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

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

toCharArray()是Java中String类的一个方法,用于将字符串转换为字符数组。
把字符串str转换成字符数组是因为在Java中,字符串是一个对象,而字符数组是字符的有序集合。
通过将字符串转换为字符数组,可以按照字符的顺序逐个遍历和访问每个字符元素。

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

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

使用字符数组:

String str = "Hello";
char[] charArray = str.toCharArray();

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

使用 StringBuilder 类:

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 统计字符

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()方法将一个字符串分割成多个子字符串。

// 字符串分割
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 截取

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

1.3.5 替换:

方法的返回值是替换的值

public String replace(char oldChar, char newChar)

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

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

1.3.6 大小写转换:

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

1.3.7 字符串转换:

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

1.3.8 字符串格式化:

  • 使用String.format()方法将数据格式化成特定的字符串形式。
// 字符串格式化
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()方法查找指定字符或子字符串在字符串中的位置。
// 字符串查找
String phrase = "Java programming language";
int index = phrase.indexOf("programming");
System.out.println("'programming'第一次出现的位置:" + index);//'programming'第一次出现的位置:5

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

1.3.10 字符串判断:

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

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

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

1.3.11 字符串去除空格:

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

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

1.3.12 字符串格式验证:

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

// 字符串格式验证
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()方法将一个字符串重复指定次数。

// 字符串重复
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. 使用双引号表示空字符串:
String emptyString = "";
  1. 使用String类的构造函数创建一个空字符串对象:
String emptyString = new String();
  1. 证明:
if(str.length()==0)
if(str.equals(""))
str.isEmpty()

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

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) 根据字符串的内容,来创建可变字符串对象

- StringBuilder():创建一个空的StringBuilder对象,初始容量为16个字符。
- StringBuilder(CharSequence seq):创建一个StringBuilder对象,并将指定的字符序列初始化为其内容。
- StringBuilder(int capacity):创建一个指定初始容量的StringBuilder对象。
- StringBuilder(String str):创建一个StringBuilder对象,并将指定的字符串初始化为其内容。

在这里插入图片描述

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()方法,将两个时间戳相减,就可以得到代码的执行时间。

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

【1】普通的拼接操作:
每次字符串拼接操作都会创建一个新的字符串对象

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

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不可变

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对象中的字符数量。
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()方法

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 使用CharSequence接口

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

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.6 在Java中,为了避免创建大量的临时字符串对象,我们可以使用StringBuilder或StringBuffer类来进行字符串拼接操作。

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

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

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

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

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 例如

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的出现主要是为了简化字符串连接的操作。

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对象的内容到当前对象中。

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对象中连接后的字符串结果。

StringJoiner sj = new StringJoiner("-");
sj.add("Java");
sj.add("Python");
sj.add("C++");

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

再例如:

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 字符串拼接的底层原理

如果没有变量参与,都是字符串直接相加,编译之后就是拼接之后的结果,
	会复用串池中的字符串如果有变量参与,每一行拼接的代码,都会在内存中创建新的字符串,浪费内存。
在Java中,字符串拼接可以使用"+"运算符或concat()方法来实现。
	字符串拼接的底层原理涉及到字符串对象和字符数组的处理。
所有要拼接的内容都会往StringBuilder中放,不会创建很多无用的空间,节约内存
  1. 使用"+“运算符:当我们使用”+"运算符进行字符串拼接时,实际上会创建一个新的StringBuilder对象,并将要拼接的字符串逐个添加到StringBuilder中。最后,StringBuilder会调用toString()方法生成一个新的String对象,该对象包含了拼接后的字符串内容。这种方式适用于少量的简单字符串拼接操作。

    String str = "Hello" + " " + "World!";
    

    多个字符串变量拼接,不要直接+。在底层会创建多个对象,浪费时间和性能。
    上面的代码相当于:

    StringBuilder sb = new StringBuilder();
    sb.append("Hello").append(" ").append("World!");
    String str = sb.toString();
    
  2. 使用concat()方法:String类提供了concat()方法,该方法用于将当前字符串与指定的字符串进行拼接,返回一个新的String对象。内部实现与"+"运算符类似,都是创建一个新的StringBuilder对象并进行字符串拼接。例如:

    String str1 = "Hello".concat(" ").concat("World!");
    

字符串拼接的本质都是通过StringBuilder或StringBuffer进行操作,各个字符串被逐个添加到可变的字符数组中,然后生成新的String对象返回。

请注意,由于Java中字符串是不可变的,每次进行字符串拼接操作都会创建一个新的字符串对象并返回。如果需要频繁拼接大量字符串或在循环中进行拼接操作,建议使用StringBuilder或StringBuffer来优化性能。

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

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

例如,以下代码片段:

String str = "Hello" + ", " + "World!";

在编译时,会被优化为:

String str = "Hello, World!";

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

标签:Java,String,对象,StringBuilder,System,字符串,out
From: https://blog.csdn.net/m0_74154295/article/details/140296855

相关文章

  • Java项目:基于SSM框架实现的中小型企业财务管理系统【ssm+B/S架构+源码+数据库+答辩PPT
    一、项目简介本项目是一套基于SSM框架实现的中小型企业财务管理系统包含:项目源码、数据库脚本等,该项目附带全部源码可作为毕设使用。项目都经过严格调试,eclipse或者idea确保可以运行!该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值二......
  • Java项目:基于SSM框架实现的农家乐信息管理平台含前后台【ssm+B/S架构+源码+数据库+答
    一、项目简介本项目是一套基于SSM框架实现的农家乐信息管理平台包含:项目源码、数据库脚本等,该项目附带全部源码可作为毕设使用。项目都经过严格调试,eclipse或者idea确保可以运行!该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值二、技......
  • Java基础进阶——128陷阱(剖析Integer类的自动拆箱和装箱)
    一、什么是128陷阱?下面用一段代码展示了什么是128陷阱:publicstaticvoidmain(String[]args){Integera=10;Integerb=10;Integeraa=127;Integerbb=127;Integeraaa=128;Integerbbb=128;......
  • JAVA零基础小白自学日志——第十天
    文章目录1.static修饰变量[1].静态变量的定义[2].静态变量属于类[3]静态变量的初始化[4].静态变量的调用2.static修饰方法[1].静态方法的定义[2].静态方法的调用3.静态块[1].静态块定义[2].静态块和匿名块的演示,运行下程序就能看出来区别小贴士今日提要:记得昨天我......
  • java10
    类特性封装属性私有,get/setprivate在set里面做安全验证继承extends扩展继承关键字子类只用单继承,没有多继承(通俗来讲,你可以有一个亲爸爸,不可能有两个亲爸爸)子类可以继承父类的方法pulic公共的private私有的私有的东西不能继承protected保护的default其他的ctrl+H......
  • Java毕设基于Vue+SpringBoot的汽车租赁管理系统(代码+数据库+文档LW+运行成功)
    文末获取资源,收藏关注不迷路文章目录前言主要使用技术研究内容核心代码文章目录前言快速发展的社会中,人们的生活水平都在提高,生活节奏也在逐渐加快。为了节省时间和提高工作效率,越来越多的人选择利用互联网进行线上打理各种事务,通过线上管理汽车租赁的方式出现......
  • Java毕设基于Vue+SpringBoot的汽车服务管理系统(代码+数据库+文档LW+运行成功)
    文末获取资源,收藏关注不迷路文章目录前言主要使用技术研究内容核心代码文章目录前言随着社会的发展,汽车服务的管理形势越来越严峻。越来越多的用户利用互联网获得信息,但汽车服务信息鱼龙混杂,信息真假难以辨别。为了方便用户更好的获得汽车服务信息,因此,设计一种......
  • Java毕设基于Vue+SpringBoot的校园失物招领平台(代码+数据库+文档LW+运行成功)
    文末获取资源,收藏关注不迷路文章目录前言主要使用技术研究内容核心代码文章目录前言信息化社会内需要与之针对性的信息获取途径,但是途径的扩展基本上为人们所努力的方向,由于站在的角度存在偏差,人们经常能够获得不同类型信息,这也是技术最为难以攻克的课题。针对......
  • JAVA中的反射机制讲解(JAVA基础)
    反射是什么Java反射机制是Java语言一个很重要的特性,它使得Java具有了“动态性”。在Java程序运行时,对于任意的一个类,我们能不能知道这个类有哪些属性和方法呢?对于任意的一个对象,我们又能不能调用它任意的方法?答案是肯定的!这种动态获取类的信息以及动态调用对象方法的功能就来......
  • Java毕设基于Vue+SpringBoot的校园台球厅人员与设备管理系统(代码+数据库+文档LW+运行
    文末获取资源,收藏关注不迷路文章目录前言主要使用技术研究内容核心代码文章目录前言在Internet高速发展的今天,我们生活的各个领域都涉及到计算机的应用,其中包括校园台球厅人员与设备管理系统的网络应用,在外国管理系统已经是很普遍的方式,不过国内的管理网站可能......