String类

目录

一、常用构造方法

二、String类源码

2.1 字符串的不可变性

2.2 String对象如何存储字符串?

2.3 StringTable字符串池

2.3.1 intern()方法

三、字符串对象比较

3.1 ==比较是否引用同一个对象

3.2 equals()方法

3.3 使用compareTo()方法

四、String类常用实用方法

4.1 获取字符串长度

4.2 字符串查找

4.3 字符串转化

4.3.1 数值与字符串的转化

4.3.2 大小写转化 

4.3.3 字符串转数组

4.3.4 格式化

4.4 字符串替换

4.5 字符串拆分 

4.6 字符串截取 

五、StringBuilder和StringBuffer

六、String,StringBuilder,StringBuffer的区别


Java中提供了String类,用来构造字符串并且提供了很多操作字符串的方法,让我们处理字符串相关的问题更加方便。


一、常用构造方法

public class StringStructureDemo {
    public static void main(String[] args) {
        String str1 = "hello";                    //字符串字面量构造
        String str2 = new String("hello");        //使用构造方法构造
        char []chars = {'h','e','l','l','o'};
        String str3 = new String(chars);          //使用字符数组构造
        String str4 = new String();               //得到空字符串
        //传递一个byte类型的数组,根据ASCII值确定字符
        byte[] bytes = new byte[]{97,98,99,100};
        String str5 = new String(bytes);
        System.out.println(str5);                //传递一个byte类型的数组       
    }
}

二、String类源码

String类被final修饰,表明String类不能被继承。被final修饰的变量(基本数据类型与引用数据类型)在初始化后,不能被重新赋值,所以value数组一旦被赋值变不能修改。同时,value被private所修饰,所以只能在String类当中使用。


2.1 字符串的不可变性

value数组被final与private修饰,不能指向新的数组对象并且不能被外部直接引用,原则上来说,value数组的内容是可以修改的,但是在String类内部并没有提供任何方法来改变value数组的内容,所以无法通过外部手段修改。

  • final确保value引用在对象的生命周期内不会指向新的数组。
  • String没有提供修改value数组内容的方法,因此,即使value数组内部可变,String类保持了不可变性。 

2.2 String对象如何存储字符串?

我们知道String是引用类型,String声明的变量是引用变量,而引用变量存储的是所引用的对象的地址,所以String类型的变量并不是直接存储字符串本身,这一点要切记。

在Java8及之前的版本中,String类使用一个内部的char[]数组来存储字符数据。

public class StringCompareDemo {
    public static void main(String[] args) {
        String str1 = new String("hello");
        String str2 = new String("world");
        String str3 = str1;
    }
}

在Java9之后:

value数组用来存储字符串内容,coder表示编码类型


2.3 StringTable字符串池

StringTable是 Java 中 JVM 内部用来存储字符串池的实现部分。字符串池是 Java 中的一个机制,用于存储所有使用字符串字面量创建的字符串对象。这样可以减少内存消耗并提高效率,因为相同的字符串字面量只会在内存中存储一次。

String str1 = "hello";
String str2 = "hello";
String str3 = "world";
String str4 = new String("hello");

上面代码中先声明变量str1,字符串字面量“hello” 存储在字符串池当中,str1指向字符串池中的字符串对象“hello”。在声明变量str2时,JVM会在StringTable中查找“hello”,如果存在就返回引用,对于上述代码,已经存在“hello”,所以不会在StringTable中创建“hello”,即str1和str2指向同一个字符串对象“hello”。声明变量str3,对于字符串字面量“world”在字符串池当中没有“world”,所以会在StringTable中创建该字符串并返回引用,使得str3指向“world”对象。对于str4,使用new关键字创建字符串会在堆上分配一个新的String对象,而不在StringTable中查找或存储,因此str4不会与str1与str2共享一个对象。

2.3.1 intern()方法

intern()方法会将字符串对象放入StringTable中。如果字符串已经存在于池中,它会返回池中字符串的引用,否则会将该字符串添加到池中并返回引用。

public class StringDome1 {
    public static void main(String[] args) {
        String str1 = "hello";
        String str2 = new String("hello").intern();
        String str3 = new String("hello");
        System.out.println(str1==str2);     //true
        System.out.println(str1==str3);     //false
    }
}

尽量使用字符串字面量:可以让JVM自动使用字符串池,避免重复创建相同内容的字符串对象。节省了内存并且提高了性能。


三、字符串对象比较

3.1 ==比较是否引用同一个对象

public class StringCompareDome1 {
    public static void main(String[] args) {
        String str1 = "hello";
        String str2 = "hello";
        String str3 = new String("hello");
        String str4 = str2;
        System.out.println(str1==str2);     //true
        System.out.println(str1==str3);     //false
        System.out.println(str1==str4);     //true
    }
}

3.2 equals()方法

String类重写了父类Object的equals()方法,对于Object类的equals方法判断的还是两个引用是否引用同一个对象,而String类的equals()方法判断的是两个对象的内容是否相同,当然如果两个引用指向同一个对象,那么内容自然也相同了。

Object类的equals()方法:

String类的equals()方法:  

public class StringCompareDome2 {
    public static void main(String[] args) {
        String str1 = "hello";
        String str2 = new String("hello");
        String str3 = new String("HEllo");
        System.out.println(str1==str2);                  //false
        System.out.println(str1.equals(str2));           //true
        System.out.println(str1.equalsIgnoreCase(str3)); //忽略大小写 ture
    }
}

3.3 使用compareTo()方法

String类实现了Comparable接口,可以使用compareTo()方法,用于按字典序顺序比较两个字符串。根据返回值判断大小:

  • 返回0表示两个字符串相等。
  • 返回负数表示调用者字符串小于参数字符串。
  • 返回正数表示调用者字符串大于参数字符串。

比较规则:先根据字典次序大小比较,如果出现不等的字符,直接返回这两个字符的大小差值;如果前面n个字符相等(n为两个字符串中长度小的字符串的长度),返回值为两个字符串长度差值。注意一个点:是谁为调用者谁为参数,两者互换得到的结果不同。

public class StringCompareDome3 {
    public static void main(String[] args) {
        String str1 = "adc";
        String str2 = "abc";
        System.out.println(str1.compareTo(str2));          //ret:2
        String str3 = "abcasd";
        System.out.println(str2.compareTo(str3));          //ret:-3
        String str4 = "ABCasD";
        //忽略大小写比较
        System.out.println(str3.compareToIgnoreCase(str4));//ret:0
    }
}

四、String类常用实用方法

4.1 获取字符串长度

        String str = "hello";
        System.out.println(str.length()); //5
        int[] array = {1,2,3,4,5};
        System.out.println(array.length); //5
        /*
        注意区分str.length()与array.length
        都是为了获取长度
        字符串调用的是方法,数组是属性
         */

4.2 字符串查找

char charAt()

int indexOf()

int lastIndexOf()

        String str = "hello";
        //char charAt(int index) 返回index上的字符
        //index为负数或者越界,抛出IndexOutOfBoundsException异常
        System.out.println(str.charAt(2));                         //'l'
        //int indexOf(int ch) 返回ch第一次出现的位置,没有返回-1
        //ch是int类型,可以传字符也可以使用其Unicode值
        System.out.println(str.indexOf('e'));                      //'e'/'101'输出1
        //int indexOf(int ch, int fromIndex)
        //从fromIndex位置开始找ch第一次出现的位置,没有返回-1
        System.out.println(str.indexOf('l',3));       //'l'
        //int indexOf(String str)
        //返回str第一次出现的位置(字串中第一个字符的下标),没有返回-1
        System.out.println(str.indexOf("ll"));                     //2
        //int indexOf(String str, int fromIndex)
        //从fromIndex位置开始找str第一次出现的位置,没有返回-1
        System.out.println(str.indexOf("he", 1));    //-1
        //int lastIndexOf(int ch)
        //从后往前找,返回ch第一次出现的位置,没有返回-1
        System.out.println(str.lastIndexOf('l'));             //3
        //int lastIndexOf(int ch,int fromIndex)
        //从fromIndex位置开始找,从后往前找ch第一次出现的位置,没有返回-1
        System.out.println(str.lastIndexOf('l', 2));//2
        //int lastIndexOf(String str)
        //从后往前找,返回str第一次出现的位置,没有返回-1
        System.out.println(str.lastIndexOf("ll"));           //2
        //int lastIndexOf(String str, int fromIndex)
        //从fromIndex位置开始找,从后往前找str第一次出现的位置,没有返回-1
        System.out.println(str.lastIndexOf("ll", 2));//2
        System.out.println(str.lastIndexOf("ll", 3));//2

4.3 字符串转化

4.3.1 数值与字符串的转化

String.valueOf(),String类的静态方法,可以直接通过类名去调用。将不同类型的值(如int、double、char、boolean、Object等)转换为String类型。字符串也可以转化为数字。

        String str1 = String.valueOf(123);
        String str2 = String.valueOf(false);
        String str3 = String.valueOf(3.14);
        String str4 = String.valueOf(new Student(18,"ls"));
        int data1 = Integer.parseInt("123");
        double data2 = Double.parseDouble("12.23");

4.3.2 大小写转化 

toLowerCase()大写转化为小写

toUpperCase()小写转化为大写

        String str1 = "HELLO";
        String str2 = "world";
        System.out.println(str1.toLowerCase());
        System.out.println(str2.toUpperCase());

4.3.3 字符串转数组

toCharArray()

        String str1 = "hello";
        //字符串转数组
        char[] chars = str1.toCharArray();
        for (char ch:chars){
            System.out.print(ch+" ");
        }
        System.out.println();
        //数组转字符串
        String str2 = new String(chars);
        System.out.println(str2);

4.3.4 格式化

String.format()
        String str = String.format("%d-%d-%d",2024,11,9);
        System.out.println(str);

4.4 字符串替换

replace(char oldChar,char newChar)               替换字符串中的单个字符(全部替换)

replaceAll(String regex,String replacement)    替换字符串中的字串(全部字串)

replaceFirlt(String regex,String replacement)  替换字符串中第一个字串

        String str1 = "How are you";
        System.out.println(str1.replaceAll(" ","-"));
        System.out.println(str1.replace('o','x'));
        System.out.println(str1.replaceFirst("How", "Where"));

4.5 字符串拆分 

String[] split(String regex)                        将字符串全部拆分

String[] split(String regex, int limit)           将字符串以指定的格式,拆分为limit

返回值为一个String类型的数组,将拆分的字符串存入字符数组中。

        String str = "How are you";
        String[] strings1 = str.split(" ");
        for (String s:strings1){
            System.out.println(s);
        }
        String[] strings2 = str.split(" ",2);
        for (String s:strings2){
            System.out.println(s);
        }

4.6 字符串截取 

String substring(int beginIndex)                         从指定索引截取到结尾
String substring(int beginIndex,int endIndex)  截取部分内容(左闭右开)
        System.out.println("hello world".substring(5));
        System.out.println("hello world".substring(2, 6));

前面2.1讲到String是一种不可变对象,字符串的内容是不可改变的。对于上面这些方法,“改变”字符串实际上是返回一个新的String对象,而不是在原本的String对象上进行修改。


五、StringBuilder和StringBuffer

StringBuilderStringBuffer都是可变字符序列类,它们可以用于高效地修改字符串内容(例如追加、插入、删除等操作)。与不可变的String类不同,StringBuilder和StringBuffer对象可以在原始对象上进行修改,不会创建新的对象实例。两者所使用的方法类似:

        StringBuilder stringBuilder = new StringBuilder("hello");
        //将指定的字符串追加到当前序列末尾
        System.out.println(stringBuilder.append(" world"));
        //在指定位置插入字符串
        System.out.println(stringBuilder.insert(5, "test"));
        //删除从start到end位置的字符
        System.out.println(stringBuilder.delete(0, 5));
        //将字符序列反转
        System.out.println(stringBuilder.reverse());
        //用指定字符串替换指定范围内的字符
        System.out.println(stringBuilder.replace(1, 4, "se"));
        //将字符序列转换为String
        stringBuilder.toString();

StringBuffer:线程安全,因为它的所有方法都被synchronized修饰,确保了多线程环境下的同步访问。适用于多线程环境,适合在需要线程安全的情况下使用。

StringBuilder:非线程安全,没有同步机制,因此在单线程环境下执行速度比StringBuffer快。适用于单线程环境,推荐在不涉及多线程的情况下使用,以获得更高的性能。


六、String,StringBuilder,StringBuffer的区别

String对象一旦创建,内容不可以修改,线程安全适用不可变的字符串处理,每次修改字符串时,都会创建一个新的String对象,因此在频繁修改字符串时性能较差;而StringBuffer与StringBuilder的内容可以修改(在原对象上修改字符串,而不需要创建新的对象),对于需要频繁修改字符串的场景,它们比String更高效;StringBuffer的方法是 synchronized的,保证了多线程环境下的安全性,适用于多个线程共享同一 StringBuffer对象时,避免数据不一致;StringBuilder不提供同步机制,因此在多线程环境下,多个线程同时访问同一 StringBuilder 对象时,可能会导致数据不一致。在单线程环境下性能更高,因为没有同步开销。

  • String 适用于常量字符串和不需要修改字符串的场景。
  • StringBuffer 适用于多线程环境中需要修改字符串的场景,保证线程安全。
  • StringBuilder 适用于单线程环境中需要频繁修改字符串的场景,提供最佳性能。

根据实际的线程安全需求和性能要求,选择合适的类来处理字符串。 


 完

上一篇:【前端】Svelte:Store 状态管理


下一篇:从0开始深度学习(27)——卷积神经网络(LeNet)