我在VScode学Java(Java字符串)

我的个人博客主页:如果’'真能转义1️⃣说1️⃣的博客主页
关于Java基本语法学习---->可以参考我的这篇博客:《我在VScode学Java》
关于Java数组学习、JVM中的堆和栈—>可以参考我的这篇文章我在VScode学Java(Java一维数组、二维数组、JVM中的堆和栈)重制版

文章目录

Java_字符串

在Java中,String是一个类,被定义在java.lang包中。因此,不需要进行导入语句就可以使用String类。

创建(直接赋值、new一个)

要创建一个Java的String,你可以使用如下的方法之一:

一、使用字符串字面量创建(串池–直接赋值获取一个字符串的对象):

 String str = "Hello, World!";

直接使用字符串字面量赋值给变量 str。这种方式首先会在字符串常量池1中查找是否存在相同内容的字符串对象,如果已经存在,则直接引用该对象;如果不存在,则在常量池中创建一个新的字符串对象。这样可以节省内存空间,避免重复创建相同内容的字符串对象。

字面量:

字面量是一种特殊的表示值的语法形式,它可以表示不同类型的数据,例如字符串、数字、布尔值等。在Java中,字面量是通过类来确定对象的。

对于不同类型的字面量,Java编译器会根据语法规则将其转化为对应类型的对象。

  • 字符串字面量被转化为String对象,通过String类来确定对象。
  • 数字字面量可以根据具体的数值范围和表示方式被转化为不同类型的对象,例如整数字面量可能转化为int、long等类型的对象。
  • 布尔字面量可以被转化为Boolean对象,通过Boolean类来确定对象。

无论字面量表示何种类型的数据,最终都会通过相应的类来确定对象,并给该对象赋予对应的值。

需要注意的是,字面量本身并不是对象,而是代表一个特定值的语法表示形式。编译器会在编译过程中根据字面量的类型,生成对应的对象或者常量,并将其包含在代码中供程序使用。

字符串字面量被转化为String对象,通过String类来确定对象。转换的过程是什么:

在Java中,编译器会将字符串字面量转化为String对象的过程如下:

  1. 当我们在代码中使用双引号括起来的字符串字面量,例如:“Hello, World!”,编译器会首先检查字符串常量池(String Pool)中是否已经存在相同内容的字符串。

  2. 如果字符串常量池中不存在相同内容的字符串,编译器会在字符串常量池中创建一个新的字符串对象,并将该字符串字面量的内容存储在这个新对象的字符数组中。

  3. 如果字符串常量池中已经存在相同内容的字符串,编译器会直接返回字符串常量池中对应字符串的引用。

  4. 无论是创建新的字符串对象还是返回已有字符串的引用,都会将结果赋值给String类型的变量或表达式。

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

二、new 一个(堆内存)

空参构造一个字符串对象

使用String关键字和构造函数创建:

在Java中,空白可以用空字符串("")来表示。在给定的代码中:
String s = new String();
System.out.println(s); // 
System.out.println(s.hashCode());//0
System.out.println(s.equals(""));//true
空串“”是长度为0的字符串:是Java的一个对象

```java
if(str.length()==0)
或者:
if(str.equals(""))
或者:
isEmpty()方法

而Null是一个特殊的值

if(str==null)
if(str!=null&&str.length()!=0)
区别:

长度为0的字符串中虽然没有字符,但其仍然是一个有效字符串对象。
而null是一个对象没有被实例化,或者没有指向任何有效的内存地址

obj是需要判断的对象。如果obj为NULL,则执行do something中的代码。否则,不执行任何操作。
if (obj == null) {
    
    
    // do something
}
null串。
	这个对象可以是任何类型的对象,包括字符串、数组、类等等。当一个对象被赋值为null时,
它就不再指向任何对象,也就是说它不再引用任何对象,因此也就无法访问该对象的任何属性或方法。
##### 传递了一个字符串,相当于传递了一个字符串的对象
 [使用new关键字创建的字符串对象则会在堆内存中进行分配。]
```java
    String str = new String("Hello, World!");

使用构造函数创建字符串对象,并将字符串内容赋值给变量 str。这种方式会在堆内存中创建一个新的字符串对象,无论原字符串常量池中是否已经存在相同内容的字符串。即使存在相同内容的字符串对象,也会创建一个新的对象。这样就会占用更多的内存空间。
字符串是不可变的(immutable)对象,在大部分情况下,直接使用字符串字面量来创建字符串对象更加高效和推荐,而使用new String()的方式主要用于特定业务需求或者对字符串常量的副本进行修改的情况。

其他
'=='比较

基本数据类型比较的是----数据值
引用数据类型比较的是----地址值

// 字符串比较
String str1 = "aaa"; // 创建一个字符串变量str1并赋值为"aaa"
String str2 = "aaa"; // 创建一个字符串变量str2并赋值为"aaa"
System.out.println(str1 == str2); // 使用"=="比较两个字符串的引用,输出结果为true
System.out.println(str1.equals(str2)); // 使用equals()方法比较两个字符串的内容,输出结果为true
String str3 = new String("aaa"); // 创建一个新的字符串对象str3,并赋值为"aaa"

System.out.println(str1 == str3); // 使用"=="比较str1和str3的引用,输出结果为false
System.out.println(str1.equals(str3)); // 使用equals()方法比较str1和str3的内容,输出结果为true

byte[] bytes = {
    
     97, 97, 97 }; // 创建一个字节数组bytes,包含三个字节97(对应ASCII码中的字符'a')
String str4 = new String(bytes); // 使用字节数组创建一个新的字符串对象str4,值为"aaa"
System.out.println(str4 == str3); // 使用"=="比较str4和str3的引用,输出结果为false
System.out.println(str4.equals(str3)); // 使用equals()方法比较str4和str3的内容,输出结果为true

System.out.println(str4 == str2); // 使用"=="比较str4和str2的引用,输出结果为true
System.out.println(str4.equals(str2)); // 使用equals()方法比较str4和str2的内容,输出结果为true

Scanner sc = new Scanner(System.in); // 创建一个Scanner对象sc,用于接收用户输入

String str5 = sc.nextLine(); // 从用户输入中读取一行字符串aaa,并赋值给str5
System.out.println(str1 == str5); // 使用"=="比较str1和str5的引用,输出结果为false
System.out.println(str1.equals(str5)); // 使用equals()方法比较str1和str5的内容,输出结果为true

String str6 = sc.next(); // 从用户输入中读取一个字符串aaa,并赋值给str6
System.out.println(str1 == str6); // 使用"=="比较str1和str6的引用,输出结果为false
System.out.println(str1.equals(str6)); // 使用equals()方法比较str1和str6的内容,输出结果为true

sc.close(); // 关闭Scanner对象sc

这些方法中,第一种方法最为常用,直接使用双引号括起来的字符串字面量即可创建一个String对象。

对比:

直接调用

在Java中,字符串字面值可以直接赋值给String类型的变量,而无需使用new关键字创建新的字符串对象。这是因为Java对字符串字面值做了特殊处理,会自动将其存储在一个名为"字符串常量池"的内存区域中。当使用相同的字符串字面值创建多个String对象时,实际上它们会引用同一个常量池中的字符串对象。这种优化机制可以节省内存空间,并提高字符串比较的效率。
这种优化机制的好处是:

节省内存空间:由于字符串常量池中的字符串对象是唯一的,相同内容的字符串只需要在常量池中存储一份。
提高字符串比较效率:由于相同内容的字符串共享同一个对象,可以通过引用比较来判断它们是否相等,而无需逐个字符比较,提高了比较的效率。
需要注意的是,这种优化只适用于字符串字面值,而不适用于使用new关键字创建的字符串对象。使用new关键字创建的字符串对象会在堆内存中单独分配空间,并不会放入字符串常量池中。
因此,
在你的代码中,str1和str2都是指向字符串常量池中的同一个"aaa"字符串对象,所以可以直接调用str1,无需使用new关键字创建新的对象。

new的:

使用new关键字创建字符串对象时,会在堆内存中单独为该字符串分配空间,并且不会共享字符串常量池中的对象。这意味着每次使用new关键字创建字符串对象时,都会得到一个新的、独立的字符串对象。

例如:

String str1 = new String("aaa");
String str2 = new String("aaa");

在上面的代码中,str1str2分别是使用new关键字创建的两个新的字符串对象,它们的内容都是"aaa",但是它们在内存中是两个独立的对象。即使两个字符串的内容相同,它们的引用也是不同的。

使用new创建字符串对象的情况通常发生在需要动态地构造字符串内容的场景下,比如从用户输入或其它运行时数据构建字符串。需要注意的是,由于new关键字创建的字符串对象不会共享字符串常量池中的对象,因此在进行字符串比较时,应使用equals()方法而不是简单的引用比较。

总之,通过new关键字创建的字符串对象在堆内存中独立存在,并不与字符串常量池中的对象共享,适用于需要动态构建字符串内容的情况。

引用的是哪里的,最后又在哪里

引用是指变量或者对象对内存中某个位置的引用。在Java中,当我们声明一个引用类型的变量时,实际上是在栈内存中创建了一个变量,并将该变量指向堆内存中的对象。

例如:

String str = "Hello";

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

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

例如:

String str = new String("World");

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

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

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

注意点:

字符串拼接产生新的字符串即下面的代码运行过程中是三个字符串

String hello="hello";
String world="world";
Sout(hello+world)

如果在原来的变量的内容更改仍是创建了新的字符串(即:创建后不可更改 )

在大多数编程语言中,字符串是不可变的数据类型,这意味着一旦字符串被创建,它的内容就不能被更改。当我们对一个字符串进行更改时,实际上是创建了一个新的字符串对象,而原始字符串对象并未被修改。
这是因为字符串的内存空间是静态分配的(在内存中分配一段固定的空间),而一旦分配了空间,就不能再更改它的大小或内容。因此,任何对字符串内容的更改都需要创建一个新的字符串对象来存储更改后的内容。

String hello="hello";
hello ="hello world";

在计算机内存中,每个变量被分配了一定的空间。对于字符串类型的变量,它需要一段连续的内存空间来存储这个字符串的内容。当我们创建一个字符串变量时,计算机会为这个字符串分配一定的内存空间,这个空间是静态分配的,也就是说这个空间的大小是固定的,不能动态地改变大小。

因此,当我们对一个字符串变量的内容进行更改时,实际上是创建了一个新的字符串对象,而不是在原来的内存空间中直接更改字符串内容。因为原来分配的内存空间已经固定了大小,无法再扩展或缩小。所以,任何对字符串内容的更改都需要创建一个新的字符串对象来存储更改后的内容。这也是为什么字符串是不可变的数据类型的原因。

什么是,可以用‘+’拼接

在JDK8以前,字符串的拼接操作确实会通过系统底层自动创建一个StringBuilder对象,并调用其append()方法完成拼接。拼接完成后,再调用toString()方法将StringBuilder对象转换为String类型。在toString()方法的底层实现中,会使用new关键字创建一个新的字符串对象来存储最终的拼接结果。
而在JDK8版本及之后,JVM会根据预估的拼接后的总大小,直接分配一个字符数组来存储要拼接的内容,并进行拼接操作。这样可以避免在拼接过程中频繁地创建和销毁临时的StringBuilder对象。只有当进行显式的字符串转换为String类型或调用特定的String类方法(例如toUpperCase())时,才会生成一个新的字符串对象。

JDK8版本中引入了基于字节数组的Compact Strings特性,它使用字节数组来存储字符串的数据,节省内存空间并提高性能。这种优化方式也使得字符串的拼接更加高效。

以下是Java中常见的字符串处理方式:

  1. 字符串连接:
    • 使用"+"号将两个字符串连接起来。
    • 使用concat()方法将两个字符串连接起来。
  // 字符串连接
        String str1 = "Hello";
        String str2 = "World";
        String concatenationResult1 = str1 + " " + str2;
        String concatenationResult2 = str1.concat(' ' + str2);
                                    //str1.concat(" ").concat(str2);
        System.out.println("字符串连接结果:" + concatenationResult1);//字符串连接结果:Hello World
        System.out.println(concatenationResult2);//字符串连接结果:Hello World
  1. 字符串截取:
    • 使用substring()方法从字符串中获取子字符串。
   // 字符串截取
        String originalString = "Hello World";
        String substring = originalString.substring(6);
        System.out.println("截取子字符串:" + substring);//截取子字符串:World
  1. 字符串比较:
    • 使用equals()方法比较两个字符串是否相等。
   -     // 字符串比较
        String string1 = "Hello";
        String string2 = "Hello";
        boolean isEqual = string1.equals(string2);
System.out.println("字符串比较结果:" + isEqual);
  • 使用compareTo()方法比较两个字符串的大小关系。

  • 
       // 定义字符串变量
       String str1 = "apple";
       String str2 = "banana";
    
       // 使用compareTo()方法比较两个字符串的大小关系
       int result = str1.compareTo(str2);
    
       // 输出比较结果
       if (result < 0) {
          
          
           System.out.println("str1 小于 str2");
       } else if (result > 0) {
          
          
           System.out.println("str1 大于 str2");
       } else {
          
          
           System.out.println("str1 等于 str2");
       }```
    
    
  • equalslgnoreCase(要比较的字符串) 忽略大小写的比较

.==号比较

 //基本数据类型:比的是数据值
  //引用数据类型: 比的是地址值

在这里插入图片描述

要比较内容用equals

  1. 字符串分割:
    • 使用split()方法将一个字符串分割成多个子字符串。
        // 字符串分割
        String sentence = "Java is a programming language";
        String[] words = sentence.split(" ");
        System.out.println("分割后的字符串数组:");
        for (String word : words) {
    
    
            System.out.println(word);
        }
  1. 字符串替换:
    • 使用replace()方法将一个字符串中的某个子串替换为另一个字符串。

        // 字符串替换
        String originalSentence = "I love apples";
        String replacedSentence = originalSentence.replace("apples",
                "oranges");
        System.out.println("替换后的字符串:" + replacedSentence);
    }
}
  1. 字符串大小写转换:
    • 使用toUpperCase()方法将字符串中所有字符转换为大写。
    • 使用toLowerCase()方法将字符串中所有字符转换为小写。
        // 字符串大小写转换
        String lowercaseString = "hello world";
        String uppercaseString = lowercaseString.toUpperCase();
        System.out.println("转换为大写字母:" + uppercaseString);

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

  1. 字符串格式化:
    • 使用String.format()方法将数据格式化成特定的字符串形式。
        // 字符串格式化
        String formattedString = String.format("The value of PI is approximately %.2f", Math.PI);
        System.out.println("格式化后的字符串:" + formattedString);
  1. 字符串查找:
    • 使用indexOf()方法查找指定字符或子字符串在字符串中的位置。

        // 字符串查找
        String phrase = "Java programming language";
        int index = phrase.indexOf("programming");
        System.out.println("'programming'第一次出现的位置:" + index);
  • 使用lastIndexOf()方法查找指定字符或子字符串在字符串中的位置。
  1. 字符串判断:
    • 使用startsWith()方法判断字符串是否以指定的前缀开头。
   // 字符串判断
    String startsWithExample = "Hello World";
    boolean startsWithHello = startsWithExample.startsWith("Hello");
    System.out.println("是否以'Hello'开头:" + startsWithHello);
- 使用`endsWith()`方法判断字符串是否以指定的后缀结尾。
  1. 字符串去除空格:
    • 使用trim()方法去除字符串两端的空格。
        // 字符串去除空格
        String stringWithSpaces = " Trim me ";
        String trimmedString = stringWithSpaces.trim();
        System.out.println("去除空格后的字符串:" + trimmedString);
  1. 字符串转换为字符数组:
    • 使用toCharArray()方法将字符串转换为字符数组。
    // 字符串转换为字符数组
        String word = "Java";
        char[] charArray = word.toCharArray();
        System.out.println("字符数组:");
        for (char c : charArray) {
    
    
            System.out.println(c);
        }
  1. 字符串格式验证:
    • 使用正则表达式和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. 字符串重复:
    • 使用repeat()方法将一个字符串重复指定次数。

// 字符串重复
String repeatedString = “Java”.repeat(3);
System.out.println(“重复字符串:” + repeatedString);
}

其他:

public class StringFunctionsExample {
    
    
    public static void main(String[] args) {
    
    
        // 字符串比较
        String str1 = "aaa";
        String str2 = "aaa";
        System.out.println(str1 == str2);
        System.out.println(str1.equals(str2));
        String str3 = new String("aaa");

        System.out.println(str1 == str3);
        System.out.println(str1.equals(str3));

        byte[] bytes = {
    
     97, 97, 97 };

        String str4 = new String(bytes);
        System.out.println(str4 == str3);
        System.out.println(str4.equals(str3));

        System.out.println(str4 == str2);
        System.out.println(str4.equals(str2));

    }
}

字符串String的定义

格式(两种)

遍历字符串:

public char charAt(int index): 根据索引返回字符

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

public int length(): 返回此字符串的长度
数组的长度: 数组名.lengthlength属性
字符串的长度: 字符串对象.length()length()方法

 String str6 = sc.next(); // 从用户输入中读取一个字符串,并赋值给str6
 int[] arr = new int[2];
 System.out.println(arr.length);
 System.out.println(str6.length());

如何遍历

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

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

这段代码将输出字符串中的每个字符。

另一种方法是通过索引循环来遍历字符串。示例如下:

String str = "Hello, World!";
for (int i = 0; i < str.length(); i++) {
    
    
    char ch = str.charAt(i);
    System.out.println(ch);
}

这段代码将依次输出字符串中的每个字符。

这些是Java中常见的遍历字符串的方法,你可以根据具体需求选择适合的方式进行操作。

Java中为什么获取一个长度的时候,数组就是length,而字符串就得是length()要多个括号

在Java中,获取数组的长度使用的是length属性,而获取字符串的长度需要使用length()方法。

(1)这是因为数组在Java中是一个固定大小的容器,其长度是数组类型的属性,可以直接通过length属性访问。
(2)而字符串是一个对象,在Java中使用String类表示,它有一个内置的方法length()用于返回字符串的长度。

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

快速遍历字符串的方法

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

toCharArray()是Java中String类的一个方法,用于将字符串转换为字符数组。
把字符串str转换成字符数组是因为在Java中,字符串是一个对象,而字符数组是字符的有序集合。
通过将字符串转换为字符数组,可以按照字符的顺序逐个遍历和访问每个字符元素。
使用字符数组可以更方便地对字符串进行操作和处理,例如搜索、替换、拆分等操作。
String str = "Hello, World!";
for (char ch : str.toCharArray()) {
    
    
    System.out.println(ch);
}

这段代码将输出字符串中的每个字符。

为什么不能直接通过字符串操作遍历字符串每个元素:

在Java中,字符串是不可变的对象,也就是说一旦创建,字符串的值就不能被修改。这意味着你无法直接通过字符串操作来遍历字符串中的每个元素。

当你使用字符串操作方法(例如charAt()、substring()等)遍历字符串时,实际上是通过返回新的字符串对象来表示每个字符。这样会导致在每次操作时都创建一个新的字符串对象,从而导致内存开销和性能损耗。

而将字符串转换为字符数组可以直接操作字符数组中的元素,因为字符数组是可变的。你可以按索引访问每个字符,或者通过循环迭代遍历字符数组来获取每个字符元素,而无需创建新的字符串对象。这样可以更高效地处理和操作字符串的内容。

因此,在需要遍历字符串每个元素并进行操作的情况下,将字符串转换为字符数组可能更加高效和方便。

另一种方法是通过索引循环来遍历字符串。示例如下:

String str = "Hello, World!";
for (int i = 0; i < str.length(); i++) {
    
    
    char ch = str.charAt(i);
    System.out.println(ch);
}

这段代码将依次输出字符串中的每个字符。

这些是Java中常见的遍历字符串的方法,你可以根据具体需求选择适合的方式进行操作。

统计字符

键盘录入一个字符串,统计该字符串中大写字母字符,小写字母字符,数字字符出现的次数:

import java.util.Scanner;

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);
    }
}

解释:

  • 首先,我们创建了一个Scanner对象来读取用户输入。
  • 然后,通过System.out.printscanner.nextLine()来提示用户输入一个字符串,并将用户输入的字符串保存在变量str中。
  • 我们定义了三个变量uppercaseCountlowercaseCountdigitCount分别用于统计大写字母、小写字母和数字字符的数量,初始值都为0。
  • 接下来,我们使用增强的for循环遍历字符串中的每个字符。
  • 对于每个字符:
    -(0) toCharArray()是Java中String类的一个方法,用于将字符串转换为字符数组。
    (1)使用Character.isUpperCase(ch)判断是否为大写字母,如果是,则uppercaseCount加1;
    (2)使用Character.isLowerCase(ch)判断是否为小写字母,如果是,则lowercaseCount加1;
    (3)使用Character.isDigit(ch)判断是否为数字字符,如果是,则digitCount加1。
  • 最后,我们通过System.out.println打印统计结果,分别输出大写字母个数、小写字母个数和数字个数。

其他:

for循环遍历字符串并使用逻辑与(&&)给出范围


        // 遍历字符串中的每个字符
        for (int i = 0; i < str.length(); i++) {
    
    
            char ch = str.charAt(i);
            
            // 判断字符是否为大写字母,并增加uppercaseCount计数
            if (ch >= 'A' && ch <= 'Z') {
    
    
                uppercaseCount++;
            }
            // 判断字符是否为小写字母,并增加lowercaseCount计数
            else if (ch >= 'a' && ch <= 'z') {
    
    
                lowercaseCount++;
            }
            // 判断字符是否为数字,并增加digitCount计数
            else if (ch >= '0' && ch <= '9') {
    
    
                digitCount++;
            }
        }


}

解释:

  • 我们使用一个整型变量i作为索引,初始值为0,逐渐增加直到达到字符串的长度str.length()之前继续循环。
  • 在每次循环中,使用charAt(i)方法获取索引i处的字符,并将其保存在变量ch中。
  • 接下来,我们使用逻辑与(&&)和字符的ASCII码范围来判断字符是否为大写字母、小写字母或数字,并进行相应的计数操作。

翻转字符串

你可以使用Java中的StringBuilder类或字符数组来翻转字符串。以下是两种方法的示例:

方法一:使用StringBuilder类

public static String reverseString(String str) {
    
    
    StringBuilder sb = new StringBuilder(str);
    sb.reverse();
    return sb.toString();
}

使用示例:

String originalString = "Hello, World!";
String reversedString = reverseString(originalString);
System.out.println(reversedString);  // 输出:!dlroW ,olleH

方法二:使用字符数组【双指针】

public static String reverseString(String str) {
    
    
    char[] charArray = str.toCharArray();
    int left = 0;
    int right = charArray.length - 1;
    
    while (left < right) {
    
    
        char temp = charArray[left];
        charArray[left] = charArray[right];
        charArray[right] = temp;
        left++;
        right--;
    }
    
    return new String(charArray);
}

使用示例:

String originalString = "Hello, World!";
String reversedString = reverseString(originalString);
System.out.println(reversedString);  // 输出:!dlroW ,olleH

方法三:使用for循环进行字符串翻转也是可行的。

你可以通过遍历字符串中的每个字符,并将其倒序拼接到一个新的字符串中来实现。以下是使用for循环的方法示例:

public static String reverseString(String str) {
    
    
    StringBuilder sb = new StringBuilder();
    
    for (int i = str.length() - 1; i >= 0; i--) {
    
    
        sb.append(str.charAt(i));
    }
    
    return sb.toString();
}

使用示例:

String originalString = "Hello, World!";
String reversedString = reverseString(originalString);
System.out.println(reversedString);  // 输出:!dlroW ,olleH

这种方式同样能够将字符串进行翻转,通过遍历并逐步拼接字符,最终得到反转后的字符串。

旋转字符串:

(1)用substring进行截取
(2)把字符串先变成一个字符数组,调整字符数组数据,最后把字符数组转出字符串。

StringBuilder: 构造一个空的字符串的构建器(容器),内容可变。用于拼接字符串和翻转字符串

首先,String 类型是不可变的,即每次字符串拼接操作都会创建一个新的字符串对象当需要拼接大量字符串时,频繁地创建和销毁字符串对象会引发性能问题。
而 StringBuilder 类解决了这个问题。它内部维护了一个可变的字符数组用于存储字符串,每次拼接只需修改数组中的内容,而不需要创建新的字符串对象。这样可以避免频繁的内存分配和销毁,提高了拼接性能。

其次,StringBuilder 还提供了一些优化方法,如预分配内存空间,避免频繁的数组扩容操作;通过设置初始容量,减少数组扩容次数和内存拷贝的开销。
总的来说,StringBuilder 通过可变的字符数组和一些优化策略,提供了更高效的字符串拼接方式,特别适用于需要频繁拼接大量字符串的场景。

可以通过调用 StringBuilder 对象的 append() 方法来追加字符串内容,从而实现字符串的拼接。每次追加字符串时,StringBuilder 内部的字符数组会自动扩容以容纳新的字符。这样可以避免创建新的字符串对象,提高了拼接效率。

StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" ");
sb.append("World");
String result = sb.toString(); // 将 StringBuilder 对象转换为 String 类型的结果
System.out.println(result); // 输出:Hello World

可以看到,通过连续调用 append() 方法,我们可以在 StringBuilder 对象上不断追加字符串内容,最后通过调用 toString() 方法将其转换为 String 类型的结果。需要注意的是,由于 StringBuilder 是可变的,所以它不是线程安全的,如果在多线程环境下使用,需要进行适当的同步控制。

StringBuilder的源码分析如下:

  1. 默认创建一个长度为16的字符数组(char[] value)。这个数组用于存储字符串内容。

  2. 添加字符串内容时,如果待添加内容的长度小于等于当前数组剩余容量(value.length - count),则直接将内容复制到数组中,并更新count表示已存储的字符数量。

  3. 如果添加的内容长度大于当前数组剩余容量,则需要进行扩容操作。扩容采用以下步骤:

    • 计算新的容量:原来的容量 * 2 + 2,即int newCapacity = (oldCapacity << 1) + 2
    • 检查新的容量是否足够容纳待添加的内容长度(count + len)。如果不够,则新容量取待添加的内容长度为准。
    • 创建一个新的字符数组(char[] newValue = new char[newCapacity])作为扩容后的存储空间。
    • 将原有的字符数据复制到新的数组中(System.arraycopy(value, 0, newValue, 0, count))。
    • 更新value引用指向新的数组,count更新为已存储的字符数量。
  4. 在执行拼接操作时,实际上是通过连续调用append()系列方法将新的字符串内容追加到value数组的末尾,并更新count指示已存储的字符数量。

总结:StringBuilder通过使用一个可变的字符数组来存储字符串内容,并通过扩容机制来保证能够容纳任意长度的字符串。在进行拼接操作时,避免了频繁地创建和销毁字符串对象,从而提高了性能和节约内存空间。

StringBuilder str = new StringBuilder();


System.out.println(str.capacity());//容量: 当前最多能存储多少
System.out.println(str.length());//长度: 当前实际能存储多少

用到检测时间的代码;

要获取Java代码的运行时间,可以使用System.currentTimeMillis()方法。在给定的代码中,你已经选择了System.currentTimeMillis()方法,这是正确的方法。这个方法返回自1970年1月1日以来的毫秒数,可以用来计算代码的执行时间。
在你的代码中,你可以将System.currentTimeMillis()方法的返回值保存在一个变量中,然后在代码执行完毕后再次调用System.currentTimeMillis()方法,将两个时间戳相减,就可以得到代码的执行时间。

public class new1 {
    
    
    public static void main(String[] args) {
    
    
        long startTime = System.currentTimeMillis();

        // 你的代码

        long endTime = System.currentTimeMillis();
        long executionTime = endTime - startTime;
        System.out.println("代码执行时间:" + executionTime + "毫秒");
    }
}

构造方法:new出来的–>StringBuilder str = new StringBuilder();

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

常用方法:

StringBuilder是一个可变的字符串,它提供了一些常用的方法来操作字符串内容。以下是StringBuilder的常用方法:

  1. append(String str):将指定的字符串追加到StringBuilder的末尾。append()方法是StringBuilder类的方法。
  2. insert(int offset, String str):在指定的位置插入字符串。
  3. delete(int start, int end):删除指定范围内的字符。
  4. replace(int start, int end, String str):用指定的字符串替换指定范围内的字符。
  5. reverse():将StringBuilder中的字符顺序反转。
  6. length():返回当前StringBuilder的长度。
  7. capacity():返回当前StringBuilder的容量。
  8. charAt(int index):返回指定位置的字符。
  9. setCharAt(int index, char ch):将指定位置的字符替换为新的字符。
  10. substring(int start):返回从指定位置开始到字符串末尾的子字符串。
  11. substring(int start, int end):返回指定范围内的子字符串。
    这些方法可以帮助我们在操作字符串时进行插入、删除、替换、反转等操作,而不需要创建新的字符串对象。这样可以提高性能和效率。

对比

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("代码执行时间:" + executionTime + "毫秒");

在这里插入图片描述

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

	  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 + "毫秒");

在这里插入图片描述

toString

java 中有了stringbuilder为什么还需要tostring:

在Java中,StringBuilder类用于高效地处理可变的字符串。它提供了一组方法来进行字符串的连接、插入和删除等操作,而无需创建新的String对象。

然而,StringBuilder对象本身并不是一个字符串,它是一个可变的字符序列。如果你需要将StringBuilder对象转换为String对象以便进行其他操作,就需要使用toString()方法。

toString()方法是Object类的一个方法,因此所有的Java对象都可以调用它。它的作用是返回一个表示该对象值的字符串,通常用于将对象转换为字符串表示形式。

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

所以,虽然StringBuilder提供了丰富的字符串操作方法,但在某些场景下仍需要通过调用toString()方法将其转换为String对象。

需要将StringBuilder对象转换为String对象的主要原因有以下几点:

  1. 字符串操作:StringBuilder类提供了很多用于字符串拼接、插入和删除等操作的方法,但这些方法返回的仍然是StringBuilder对象。如果需要进行其他字符串操作,比如使用正则表达式、字符串匹配函数或者其他字符串处理方法,通常会需要将其转换为String对象才能进行进一步操作。

  2. API兼容性:某些API或库只接受String类型的参数,无法直接接受StringBuilder类型。在这种情况下,需要将StringBuilder对象转换为String对象才能与这些API或库进行交互。

  3. 输出打印:在输出结果到控制台或日志文件时,通常需要将StringBuilder中的内容转换为String对象,以便进行输出。

  4. 不可变性:String对象是不可变的,而StringBuilder对象是可变的。如果希望保留字符串的不可变性,可以将StringBuilder转换为String对象,避免意外的修改。

总结来说,需要将StringBuilder转换为String的原因是基于业务需求、API兼容性、输出格式以及字符串的不可变性等考虑,以便更好地适应不同的场景和操作。

代码举例:

 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();
我们首先创建了一个Scanner对象,并传入System.in作为参数,表示从标准输入读取数据。然后使用scanner.nextLine()方法读取用户输入的完整一行字符串,并将其存储到名为inputString的String变量中。

接下来,我们创建了一个StringBuilder对象,并将inputString作为构造函数的参数传入,以便初始化StringBuilder的内容为输入的字符串。然后,我们调用reverse()方法来翻转字符串。

最后,通过调用toString()方法,将翻转后的StringBuilder对象转换为String对象,并将结果存储在名为reversedString的变量中。最后一步就是打印输出翻转后的字符串。

执行以上代码,在控制台输出提示消息后,您可以输入任意字符串并按下回车键确认。然后程序将会将您输入的字符串进行翻转,并显示在控制台上。

返回:

  public static void main(String[] args) {
    
    
        char[] arr = {
    
     1, 'A', 3, 4 };
        String strs = arrtostring(arr);
        System.out.println(strs);//[, A, , ]
    }

    public static String arrtostring(char[] arr) {
    
    
        StringBuilder sss = new StringBuilder();
        sss.append('[');
        for (int i = 0; i < arr.length; i++) {
    
    
            if (i == arr.length - 1) {
    
    
                sss.append(arr[i]);
            } else {
    
    
                sss.append(arr[i]).append(", ");  //  sss.append(arr[i] + ",");
            }
        }
        sss.append(']');
        return sss.toString();
    }

如果是 int[] arr = { 1, 'A', 3, 4 }
结果就是[1, 65, 3, 4]

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

StringBuilder和StringBuffer都是可变的字符序列,它们提供了一系列方法用于在现有字符串的基础上进行修改和拼接,而不会每次都创建新的字符串对象。这样可以有效地节约内存空间,并提高性能。

当我们需要拼接多个字符串时,可以通过连续调用StringBuilder(或StringBuffer)的append()方法将内容逐步追加到StringBuilder(或StringBuffer)对象中,最后通过调用toString()方法获取最终的结果字符串。

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

需要注意的是,StringBuilder是非线程安全的,适用于单线程环境;而StringBuffer是线程安全的,适用于多线程环境。根据实际需求选择合适的类来进行字符串拼接操作。

类与对象中的表现:

在这里插入图片描述

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

StringJoiner is a class in Java that is used to build a sequence of strings with a specific delimiter. It provides a convenient way to concatenate strings while controlling the separator between them. By specifying a delimiter and optional prefix and suffix, you can add multiple strings and convert them into a single string using the specified delimiter. This is useful when you need to construct comma-separated values (CSV), SQL statements, or any other string concatenation scenarios.

StringJoiner是Java中的一个类,用于以特定的分隔符构建字符串序列。它提供了一种方便的方式来连接字符串,并控制它们之间的分隔符。通过指定分隔符和可选的前缀和后缀,可以添加多个字符串,并使用指定的分隔符将它们转换为单个字符串。在需要构建逗号分隔值(CSV)、SQL语句或其他字符串拼接场景时,这非常有用。

 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();
    }

介绍

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

StringJoiner的出现主要是为了简化字符串连接的操作。在Java 8之前,要将多个字符串连接起来,通常需要使用StringBuilder或StringBuffer来进行手动拼接,涉及到一些繁琐的操作,比如追加字符串、处理分隔符等。而使用StringJoiner类可以更直观地完成这一任务,使代码更简洁清晰。

使用StringJoiner类可以方便地进行列表、数组等数据的字符串拼接,特别适用于需要拼接多个元素的场景。

例如,可以使用StringJoiner来拼接一个字符串列表:

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()方法获取拼接后的字符串结果。

需要注意的是,StringJoiner类的出现主要是为了提供一个便利的工具,用于简化字符串连接的操作,并不是必须使用的。在某些场景下,仍然可以使用StringBuilder等方式进行字符串拼接。

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

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

请注意,CharSequence是一个接口,它是许多Java字符串类型的通用父接口,包括String和StringBuilder。

public StringJoiner merge(StringJoiner other)

merge(StringJoiner other):将另一个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 StringJoiner(CharSequence delimiter, CharSequence prefix, CharSequence suffix)和 public StringJoiner add(CharSequence element)配合写一个代码

public class Main {
    
    
    public static void main(String[] args) {
    
    
        StringJoiner stringJoiner = new StringJoiner(", ", "[", "]"); // Create a new StringJoiner object with comma and space as the delimiter, "[" as prefix, and "]" as suffix
        
        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]
    }
}

This code creates a StringJoiner object with a comma and space (", ") as the delimiter, “[” as the prefix, and “]” as the suffix. It adds three elements (“Apple”, “Banana”, and “Orange”) to the StringJoiner using the add() method. Finally, it retrieves the string result using the toString() method and prints it to the console, which will output “[Apple, Banana, Orange]”.

public StringJoiner(CharSequence delimiter)和 public StringJoiner add(CharSequence element)配合写一个代码

public class Main {
    
    
    public static void main(String[] args) {
    
    
        StringJoiner stringJoiner = new StringJoiner(", "); // Create a new StringJoiner object with comma and space as the delimiter
        
        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
    }
}

This code creates a StringJoiner object with a comma and space (", ") as the delimiter. It then adds three elements (“Apple”, “Banana”, and “Orange”) to the StringJoiner using the add() method. Finally, it retrieves the string result using the toString() method and prints it to the console, which will output “Apple, Banana, Orange”

字符串原理

字符串存储的内存原理:直接会复用字符串常量值;new一个

Java中的字符串是不可变的,即一旦创建,就不能修改它的值。Java字符串的存储内存原理涉及到三个重要的概念:字符串常量池、堆内存和字符串字面值。

  1. 字符串常量池(String Pool):字符串常量池是Java中用于存储字符串常量的特殊内存区域。当我们创建一个字符串时,如果该字符串的值已经存在于字符串常量池中,那么会直接返回常量池中对应的引用;如果该字符串的值不在常量池中,那么会将该字符串添加到常量池,并返回新添加的引用。字符串常量池的存在可以节省内存空间,并提高字符串的访问效率。

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

  3. 字符串字面值:字符串字面值是指直接使用双引号括起来的字符串文本。当我们使用字符串字面值创建字符串对象时,Java会先在字符串常量池中查找是否存在相等的字符串。如果存在,则返回常量池中对应的引用;如果不存在,则在常量池中创建新的字符串并返回引用。

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

==号和equals()方法比较

在Java中,"=="运算符和equals()方法都可以用于比较对象的相等性,但它们之间有着不同的行为。

  1. "" 运算符:它是一种比较引用的运算符。当使用""来比较两个对象时,它会检查两个对象的引用是否指向内存中的同一个对象。如果两个引用指向的是同一个对象,则返回true;否则返回false。例如:

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

    基本数据类型比较数据值
    引用数据类型比较地址值
    在上面的例子中,str1和str2虽然是两个不同的引用,但它们指向了字符串常量池中的同一个字符串对象。

  2. equals() 方法:它是定义在Object类中的方法,在许多具体的类中被重写以实现自定义的相等比较逻辑。equals()方法用于比较两个对象的内容是否相等。默认情况下,equals()方法与"=="运算符的行为相同,即比较两个对象的引用是否相同。但是,许多类(如String、Integer等)已经重写了equals()方法,将其用于比较内容。例如:

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

    在上面的例子中,调用了String类的equals()方法,比较两个字符串对象的内容是否相等。

总结:

  1. “==” 运算符比较的是引用是否相同。
  2. equals() 方法在不同的类中可能有不同的实现,用于比较对象的内容是否相等。对于String类来说,equals() 方法比较的是字符串的内容。
    因此,在比较两个字符串时,通常推荐使用equals()方法来比较它们的内容,而不是使用"=="运算符。

字符串拼接的底层原理:

**如果没有变量参与,都是字符串直接相加,编译之后就是拼接之后的结果,
	会复用串池中的字符串如果有变量参与,每一行拼接的代码,都会在内存中创建新的字符串,浪费内存。
在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!");
    

无论是使用"+"运算符还是concat()方法,字符串拼接的本质都是通过StringBuilder或StringBuffer进行操作,各个字符串被逐个添加到可变的字符数组中,然后生成新的String对象返回。这种方式避免了频繁创建和销毁字符串对象,提高了效率。
请注意,由于Java中字符串是不可变的,每次进行字符串拼接操作都会创建一个新的字符串对象并返回。如果需要频繁拼接大量字符串或在循环中进行拼接操作,建议使用StringBuilder或StringBuffer来优化性能。

在编译时已经确定了要拼接的字符串,并且没有涉及变量时,Java编译器会将连续的字符串字面值直接连接在一起,以避免使用StringBuilder或StringBuffer类。

这是由于Java编译器对字符串的优化机制,称为字符串常量池。在编译阶段,如果发现连续的字符串字面值,编译器会将它们合并为一个字符串,并将其存储在字符串常量池中。因此,这种情况下的字符串拼接操作会在编译时被优化为一个常量。

例如,以下代码片段:

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

在编译时,会被优化为:

String str = "Hello, World!";

这样可以减少运行时的字符串拼接操作,提高性能。

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

对于Java中的字符串拼接,当所有参与拼接的部分都是字符串字面量时,编译器会进行字符串优化。这种优化机制被称为"字符串常量池",它的作用是通过将相同的字符串共享在内存中,以减少内存的使用。

在编译阶段,如果发现表达式中只包含字符串字面量,编译器就会将它们连接成一个字符串,并将最终结果作为一个字符串常量直接嵌入到生成的字节码中。这样,在运行时就不需要再进行字符串拼接操作,因为拼接已经在编译时完成了。

需要注意的是,当字符串拼接涉及到变量或表达式时,编译器无法在编译时确定最终的字符串结果,因此这种情况下并不会触发字符串优化机制。在这种情况下,字符串的拼接操作将在运行时进行。

总结一下,只有当所有参与拼接的部分都是字符串字面量时,Java编译器才会触发字符串的优化机制,将其在编译时连接成一个字符串常量。

在Java 8之前,字符串拼接的机制与现在相比有所不同。

在Java 8之前,使用加号"+"进行字符串拼接时,每次拼接都会创建一个新的字符串对象。这是因为字符串在Java中是不可变的,每次拼接都需要创建一个新的字符串对象来保存结果。这会导致频繁的内存分配和回收,对性能造成一定的影响。

而在Java 8引入的新特性中,引入了StringJoiner类和String.join()方法,可以更高效地进行字符串拼接操作。这些方法内部使用了StringBuilder类来处理字符串拼接,避免了频繁的字符串对象创建和销毁,提高了性能。

除此之外,在Java 9之后还引入了更加简便的字符串拼接方式,使用占位符(“%s”)和静态方法String.format()来进行字符串格式化和拼接。

综上所述,Java 8之前的字符串拼接机制以及Java 8之后引入的新特性在处理字符串拼接方面有一定差异。新特性的引入可以提高字符串拼接的性能和代码的可读性。

字符串拼接的时候,如果有变量:

在JDK8以前,字符串的拼接操作确实会通过系统底层自动创建一个StringBuilder对象,并调用其append()方法完成拼接。拼接完成后,再调用toString()方法将StringBuilder对象转换为String类型。在toString()方法的底层实现中,会使用new关键字创建一个新的字符串对象来存储最终的拼接结果。

而在JDK8版本及之后,JVM会根据预估的拼接后的总大小,直接分配一个字符数组来存储要拼接的内容,并进行拼接操作。这样可以避免在拼接过程中频繁地创建和销毁临时的StringBuilder对象。只有当进行显式的字符串转换为String类型或调用特定的String类方法(例如toUpperCase())时,才会生成一个新的字符串对象。

JDK8版本中引入了基于字节数组的Compact Strings特性,它使用字节数组来存储字符串的数据,节省内存空间并提高性能。这种优化方式也使得字符串的拼接更加高效。


  1. 由于字符串字面量的特性,相同内容的字符串对象会被共享并保存在字符串常量池中,可以提高内存的利用率和效率。而通过new关键字创建的字符串对象不会被保存在字符串常量池中。 ↩︎

猜你喜欢

转载自blog.csdn.net/m0_74154295/article/details/131483696