【JavaSE】----- 认识 String 类

🚆简单说明:

在C语言中是没有字符串这种数据类型的,但是在Java中有字符串类型 String。

  • 字符:使用单引号引起的一个字符。'a','中'
  • 字符串:使用双引号引起的若干个字符。"abcde","a"

💥注意:在Java中字符串没有所谓的以 '\0' 结尾的说法!

目录

一、创建字符串

🍓常见的构造 String 的方式

🍓引用

🍓方法传参

二、字符串常量池

🍓常量池的概念

三、字符串比较相等

🍓易错题

🍓equals方法

四、理解字符串不可变

🍓分析字符串拼接

🍓修改字符串的方法

五、字符,字节与字符串

🍓字符与字符串

🍓字节与字符串

六、字符串常见操作

🍓字符串比较

🍓字符串查找

🍓字符串替换

🍓字符串拆分

🍓字符串截取

🍓其他操作方法

七、StringBuffer 和 StringBuilder

🍓StringBuilder 的使用

🍓普通的String拼接,底层会被优化为StringBuilder

🍓StringBuffer 和 StringBuilder的区别

🍓String 与 StringBuffer 或 StringBuilder 之间的转换

🍓StringBuffer类中部分方法的使用

八、字符串的常见问题


一、创建字符串

String 类是被final修饰的,所以它不可以被继承。 

🍓常见的构造 String 的方式

public class TestDemo {
    public static void main(String[] args) {
        // 方式一:直接赋值
        String str = "Hello Java";
        // 方式二:调用构造方法进行构造对象
        String str2 = new String("Hello Java");
        // 方式三:使用数组
        char[] array = {'a', 'b', 'c'};
        String str3 = new String(array);
    }
}

官方文档可以看到 String 还支持很多其他的构造方式, 用到的时候去查就可以了。

💥注意事项:

  • "hello" 这样的字符串字面值常量, 类型也是 String.
  • String 也是引用类型。
String str = "Hello"; 

代码内存布局

🍓引用

  • 在介绍数组的文章中就提到了引用的概念.
  • 引用类似于 C 语言中的指针, 只是在栈上开辟了一小块内存空间保存一个地址. 但是引用和指针又不太相同, 指针能进行各种数字运算(指针+1)之类的, 但是引用不能, 这是一种 "没那么灵活" 的指针.
  • 也可以把引用想象成一个标签, "贴" 到一个对象上. 一个对象可以贴一个标签, 也可以贴多个。如果一个对象上面一个标签都没有, 那么这个对象就会被 JVM 当做垃圾对象回收掉.
  • Java 中数组, String, 以及自定义的类都是引用类型

String 是引用类型, 因此对于以下代码

    String str1 = "Hello";
    String str2 = str1;

内存布局

如果修改 str1 , str2 会不会随之变化呢?

事实上, str1 = "world" 这样的代码并不算 "修改" 字符串, 而是让 str1 这个引用指向了一个新的 String 对象。

内存布局

 补充:

  • 虽然str1与str2指向了同一个字符串"Hello",但是不可以通过str1修改字符串"Hello"的值,因为双引号引起来的是字面常量,常量是不可以被修改的。
  • 上面的代码是修改的是str1的指向。

🍓方法传参

✨传引用不一定可以改变实参的值,要看这个引用具体做了什么。

public class TestDemo1 {
    public static void func(String s,char[] array){
        s = "abcdef";
        array[0] = 'P';
    }
    public static void main(String[] args) {
        String str = "Hello";
        char[] chars = {'W','o','r','l','d'};
        func(str,chars);
        System.out.println(str);
        System.out.println(Arrays.toString(chars));
    }
}

内存布局

 运行结果

二、字符串常量池

🍓常量池的概念

🎄Class文件常量池:

  • 程序编译后生成字节码文件,在字节码文件中会有一个常量池(Class文件常量池)。
  • 例如:int a = 10; 在编译期间,10就放在Class文件常量池。
  • Class文件常量池存放在磁盘上的。

🎄运行时常量池:

  • 当程序把编译好的字节码文件加载到JVM当中后,会生成一个运行是常量池,这个常量池存放在方法区。
  • 运行时常量池实际上是Class文件常量池被加载后放到了JVM中。

🎄字符串常量池:

  • 主要存放字符串常量。
  • 字符串常量池本质上是一个哈希表(StringTable)。
  • 双引号引起来的内容会放到字符串常量池中。
  • 从JDK1.8开始,将字符串常量池存放在堆里面。

🎄理解 "池" (pool)

  • "池" 是编程中的一种常见的, 重要的提升效率的方式,。"池" 有很多,例如: "内存池", "线程池", "数据库连接池"。
  • 以字符串常量池举例:需要使用的字符串在常量池中,用的时候直接在常量池里拿,常量池里面没有需要的字符串就去创建,下一次使用的时候就不用再创建了,直接在常量池里拿。

🎄哈希表:

  • 是一种数据结构,描述和组织数据的一种方法。
  • 存储数据的时候,会根据一个映射关系进行存储,至于如何映射,需要设计一个函数(哈希函数)。

这里只是简单的描述一下哈希表。有关哈希表的具体内容会在后面的数据结构中详细介绍。

因为双引号引起来的内容会存放到字符串常量池中,字符串常量池本质上是一个哈希表,所以字符串会挂到哈希表的结点上。

三、字符串比较相等

🍓易错题

🌀 易错 1:

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

//运行结果:false

 内存布局

✨代码分析:

最终str1产生了一个"hello"字符串对象

str2产生了两个"hello"字符串对象:

  1. 常量池中的"hello"对象赋给了str2
  2. str2 new 的一个"hello"对象

🌀 易错 2:

public class TestDemo1 {
    public static void main(String[] args) {
        String str1 = "hello";
        String str2 = "hello";
        System.out.println(str1==str2);
    }
}

//运行结果:true

内存布局

🌀 易错 3:

public class TestDemo1 {
    public static void main(String[] args) {
        String str1 = "hello";
        String str2 = "he"+"llo"; //此时,两个都是常量,在编译的时候就已经确定是"hello"
        System.out.println(str1==str2);
    }
}

//运行结果:true

🌀 易错 4:

public class TestDemo1 {
    public static void main(String[] args) {
        String str1 = "hello";
        String str2 = "he";
        String str3 = str2 + "llo"; //str3 是一个变量,在编译的时候并不确定里面是什么
        System.out.println(str1 == str3);
    }
}

//运行结果:false

内存布局

🌀 易错 5:

public class TestDemo1 {
    public static void main(String[] args) {
        String str1 = "11";
        String str2 = new String("1")+new String("1");
        System.out.println(str1==str2);
    }
}

//运行结果:false

 内存布局

🌀 易错 6:

public class TestDemo1 {
    public static void main(String[] args) {
        String str2 = new String("1")+new String("1");
        str2.intern();//手动入池
        String str1 = "11";
        System.out.println(str1==str2);
    }
}

//运行结果:true

内存布局

🌀 易错 7:针对上面的代码换一个顺序

public class TestDemo1 {
    public static void main(String[] args) {
        String str1 = "11";
        str1.intern();//手动入池 -》当字符串常量池中没有该对象的时候,就会入池。如果有就不会入池。
        String str2 = new String("1")+new String("1");
        System.out.println(str1==str2);
    }
}

//运行结果:false

代码分析:

  • 将str1的"11"字符串先入池,定义str2最终拼接出字符串"11",字符串常量池中有"11"对象,所以不会将str2指向的对象入池。
  • str1 与 str2 指向不同的地址,所以打印出来的结果是false。
  • 这个代码的内存布局图与第(5)个一样。

String 使用 == 比较并不是在比较字符串内容, 而是比较两个引用是否是指向同一个对象

🍎Java 中要想比较字符串的内容, 必须采用String类提供的 equals 方法

🍓equals方法

🌌Java底层中的 equals 方法的分析

Object.java

String.java

  • Java底层中的 equals 方法在 Object 类中比较两个引用是否相等(是否指向同一个对象)。
  • 在String类中重写了 equals 方法,重写 equals 方法后,先比较两个字符串长度是否相等,如果不相等就返回false,如果相等再比较两个数组中的每一个字符 ,如果字符都相等就返回 true,只要有一个字符不相等就返回 false。

🌀 equals 方法的使用(1):

public class TestDemo1 {
    public static void main(String[] args) {
        String str1 = "hello";
        String str2 = new String("hello");
        System.out.println(str1.equals(str2));
    }
}

//运行结果:true

🌀 equals 方法的使用(2):

public class TestDemo1 {
    public static void main(String[] args) {
        String str1 = "hello";
       //"Hello"这样的字面值常量, 本质上也是一个 String 对象, 完全可以使用 equals 等 String 对象的方法
        System.out.println("hello".equals(str1));
    }
}

🌀 equals 方法的使用(3):

public class TestDemo1 {
    public static void main(String[] args) {
        String str1 = null; //str1这个引用不指向任何对象
        String str2 = " "; //str2这个引用指向的字符串是空
        //这两个对象不可能相等
    }
}

💥注意:任何一个引用在调用方法时都要预防空指针异常。

如果将str1放到括号里面就不会出现空指针异常,因为调用方法的 str2 不是空指针。

⭐底层的 equals 方法中一开始是判断两个引用是否是相等,如果相等就返回true。

四、理解字符串不可变

  • 字符串是一种不可变对象. 它的内容不可改变.
  • String 类的内部实现也是基于 char[] 来实现的, 但是 String 类并没有提供 set 方法之类的来修改内部的字符数组。

🍓分析字符串拼接

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "hello" ;
        str = str + " world" ;
        str += "!!!" ;
        System.out.println(str);
    }
}

//运行结果:hello world!!!

形如 += 这样的操作, 表面上好像是修改了字符串, 其实不是。这段代码中共创建了5次对象:

  1. "hello"
  2. "world"
  3. "hello world"
  4. "!!!"
  5. "hello world!!!"

+= 之后 str 打印的结果却是变了, 但不是 String 对象本身发生改变, 而是 str 引用到了其他的对象。

🍓修改字符串的方法

  • 例如, 现有字符串 str = "Hello" , 想改成 str = "hello"

🌊常见办法: 借助原字符串, 创建新的字符串

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "Hello";
        str = "h" + str.substring(1);
        System.out.println(str);
    }
}

//运行结果:hello
  • 关于substring()方法的使用在文章的后面会介绍

🌊特殊办法:使用 "反射" 操作可以破坏封装, 访问一个类内部的 private 成员。

  • 通过前面的内容可以知道,字符串是将字符保存在 value 数组中的。通过"反射"更改value数组中的字符。
import java.lang.reflect.Field;

public class TestDemo1 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        String str = "Hello";
        // 获取 String 类中的 value 字段. 这个 value 和 String 源码中的 value 是匹配的.
        Field valueField = String.class.getDeclaredField("value");
        // 将这个字段的访问属性设为 true
        valueField.setAccessible(true);
        // 把 str 中的 value 属性获取到.
        char[] value = (char[]) valueField.get(str);
        // 修改 value 的值
        value[0] = 'h';
        System.out.println(str);
    }
}

//运行结果:hello

关于反射

  • 反射是面向对象编程的一种重要特性, 有些编程语言也称为 "自省".
  • 指的是程序运行过程中, 获取/修改某个对象的详细信息(类型信息, 属性信息等), 相当于让一个对象更好的 "认清自己"
  • 这里只是简单的用了一下反射,关于反射的具体内容会在后面的文章中详细介绍。

五、字符,字节与字符串

🍓字符与字符串

  • 字符串内部包含一个字符数组,String 可以和 char[] 相互转换。

💦将字符数组中的所有内容变成字符串

public class TestDemo1 {
    public static void main(String[] args) {
        char ch[] = {'a','b','c','d','e'};
        String str = new String(ch);
        System.out.println(str);
    }
}

//运行结果:abcde

💦将部分字符数组中的内容变为字符串

public class TestDemo1 {
    public static void main(String[] args) {
        char ch[] = {'a','b','c','d','e'};
        String str = new String(ch,1,3); //从下标1开始,将后面的3个字符变成字符串
        System.out.println(str);
    }
}

//运行结果:bcd

💦获取指定索引(下标)位置的字符,索引从0开始

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "hello";
        char ch = str.charAt(2);//获取2下标的字符
        System.out.println(ch);
    }
}

//运行结果:l

💦将字符串变成字符数组

import java.util.Arrays;

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "hello";
        char chars[] = str.toCharArray(); //将str指向的字符串对象,变成字符数组
        System.out.println(Arrays.toString(chars));
    }
}

//运行结果:[h, e, l, l, o]

练习

  • 给定字符串一个字符串, 判断其是否全部由数字所组成。

🌊方法一:使用 charAt() 方法获取指定位置的字符,然后使用ASCII码值进行判断

public class TestDemo1 {
    public static boolean isNumberChar(String s){
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            if(ch<'0' || ch>'9'){
                return false;
            }
        }
        return true;
    }
    public static void main(String[] args) {
        String str = "123456";
        System.out.println(isNumberChar(str));
    }
}

//运行结果:true

🌊方法二:使用 charAt()方法获取指定位置的字符,然后使用 Character 类中的 isDigit() 方法。

public class TestDemo1 {
    public static boolean isNumberChar(String s){
        for (int i = 0; i < s.length(); i++) {
           char ch = s.charAt(i);
           //判断某个字符是不是数字(是返回 true,不是返回 false)
           boolean flag = Character.isDigit(ch);
           if(flag==false){
               return false;
           }
        }
        return true;
    }
    public static void main(String[] args) {
        String str = "123456";
        System.out.println(isNumberChar(str));
    }
}

//运行结果:true

🍓字节与字符串

  • 字节常用于数据传输以及编码转换的处理之中,String 也能方便的和 byte[] 相互转换。

💦将字节数组变为字符串

public class TestDemo1 {
    public static void main(String[] args) {
        byte[] bytes = {97,98,99,100};
        String str = new String(bytes);
        System.out.println(str);
    }
}

//abcd

💦将部分字节数组中的内容变为字符串

public class TestDemo1 {
    public static void main(String[] args) {
        byte[] bytes = {97,98,99,100};
        String str = new String(bytes,1,2);
        System.out.println(str);
    }
}

//运行结果:bc

💦将字符串以字节数组的形式返回

import java.util.Arrays;

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "abcd";
        byte[] bytes = str.getBytes();
        System.out.println(Arrays.toString(bytes));
    }
}

//运行结果:[97, 98, 99, 100]

💦编码转换处理

import java.io.UnsupportedEncodingException;
import java.util.Arrays;

public class TestDemo1 {
    public static void main(String[] args) throws UnsupportedEncodingException {
        String str = "语言";
        byte[] bytes = str.getBytes("utf-8");
        System.out.println(Arrays.toString(bytes));
        byte[] bytes2 = str.getBytes("GBK");
        System.out.println(Arrays.toString(bytes2));
    }
}

运行结果

  • 除数字和英文字母以外,不同编码格式转换后的字节数组不同。

💦何时使用 byte[], 何时使用 char[]?

  • byte[] 是将 String 按照一个字节一个字节的方式处理, 这种适合在网络传输, 数据存储这样的场景下使用. 更适合针对二进制数据来操作.
  • char[] 是将 String 按照一个字符一个字符的方式处理, 更适合针对文本数据来操作, 尤其是包含中文的时候。

六、字符串常见操作

🍓字符串比较

  • 前面简单的介绍过String类提供的 equals()方法,该方法本身是可以进行区分大小写的相等判断。除了这个方法之外,String类还提供了一些比较操作。

💦区分大小写的比较

public class TestDemo1 {
    public static void main(String[] args) {
        String str1 = "Hello";
        String str2 = "hello";
        System.out.println(str1.equals(str2));
    }
}

//运行结果:false

💦不区分大小写的比较

public class TestDemo1 {
    public static void main(String[] args) {
        String str1 = "Hello";
        String str2 = "hello";
        System.out.println(str1.equalsIgnoreCase(str2));
    }
}

//运行结果:true

💦比较两个字符串的大小关系,使用 compareTo() 方法

public class TestDemo1 {
    public static void main(String[] args) {
        String str1 = "abc";
        String str2 = "Abc";
        int ret = str1.compareTo(str2);
        System.out.println(ret);
    }
}

//运行结果:32

在String类中compareTo()方法是一个非常重要的方法,该方法返回一个整型,该数据会根据大小关系返回三类内容:

  1. 相等:返回0
  2. 小于:返回内容小于0
  3. 大于:返回内容大于0

🍓字符串查找

  • 从一个完整的字符串之中可以判断指定内容是否存在,查找方式如下:

💦判断子字符串是否存在主字符串中

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "ababcabcd";
        String tmp = "abc";
        boolean flag =  str.contains(tmp);
        System.out.println(flag);
    }
}

//运行结果:true
  • 该判断形式是从JDK1.5之后开始追加的,在JDK1.5以前要想实现与之类似的功能,就必须借助 indexOf() 方法完成。

💦从头开始查找指定字符串的位置,找到了然后位置的开始索引,找不到返回-1

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "ababcabcd";
        String tmp = "abc";
        //返回主串中第一次出现子串的起始位置
        int index = str.indexOf(tmp);
        System.out.println(index);
    }
}

//运行结果:2
  • 使用indexOf():如果内容重复,它只能返回查找的第一个位置

💦从指定位置开始查找子串的起始位置

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "ababcabcd";
        String tmp = "abc";
        //从下标为3的位置开始查找子串的起始位置
        int index = str.indexOf(tmp,3);
        System.out.println(index);
    }
}

//运行结果:5

💦从后向前查找字符的起始位置

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "ababcabcd";
        String tmp = "abc";
        //从后向前查找子串的起始位置
        int index = str.lastIndexOf(tmp);
        System.out.println(index);
    }
}

//运行结果:5

💦从指定位置开始,从后向前查找子串的起始位置

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "ababcabcd";
        String tmp = "abc";
        //从下标为4的位置开始从后向前查找子串的起始位置
        int index = str.lastIndexOf(tmp,4);
        System.out.println(index);
    }
}

//运行结果:2

💦判断一个字符串是否以指定字符串开头

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "ababcabcd";
        String tmp = "ac";
        //判断str指向的字符串是否以 "ac" 字符串开头
        boolean flag = str.startsWith(tmp);
        System.out.println(flag);
    }
}

//运行结果:false

💦从指定位置开始,判断一个字符串是否以指定字符串开头

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "ababcabcd";
        String tmp = "c";
        //从下标为4的位置开始,判断str指向的字符串是否以 "c" 字符串开头
        boolean flag = str.startsWith(tmp,4);
        System.out.println(flag);
    }
}

//运行结果:true

💦判断一个字符串是否以指定字符串结尾

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "ababcabcd";
        String tmp = "cd";
        //判断str指向的字符串是否以 "cd" 字符串结尾
        boolean flag = str.endsWith(tmp);
        System.out.println(flag);
    }
}

//运行结果:true

🍓字符串替换

  • 使用一个指定的新的字符串替换掉已有的字符串数据。

💦替换所有的指定内容

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "ababcabcd";
        //将字符串中的"ab"替换为字符串"vn"
        String s = str.replaceAll("ab","vn");
        System.out.println(s);
    }
}

//运行结果:vnvncvncd

💦替换第一个内容

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "ababcabcd";
        //将字符串中的第一个"ab"替换为字符串"vn"
        String s = str.replaceFirst("ab","vn");
        System.out.println(s);
    }
}

//运行结果:vnabcabcd

💥注意事项: 由于字符串是不可变对象, 替换不修改当前字符串, 而是产生一个新的字符串

🍓字符串拆分

  • 可以将一个完整的字符串按照指定的分隔符划分为若干个子字符串

💦将字符串全部拆分(多次拆分)

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "name=zhangsan&age=20";
        //先按 "&" 进行分割
        String[] strings = str.split("&");
        for (String ret : strings) {
            System.out.println(ret);
            
            //再按 "=" 进行分割
            String[] ss = ret.split("=");
            for (String tmp : ss) {
                System.out.println(tmp);
            }
        }
    }
}

//运行结果:
name=zhangsan
name
zhangsan
age=20
age
20

💦将字符串部分拆分

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "hello world hello java";
        //将字符串以空格" " 部分拆分成两部分,不会均匀拆分
        String[] ret = str.split(" ",2);
        for (String s:ret) {
            System.out.println(s);
        }
    }
}

//运行结果:
hello
world hello java

💦有些特殊字符作为分割符可能无法正确切分, 需要加上转义

💥注意事项:

  1. 字符"|","*","+"都得加上转义字符,前面加上"\".
  2. 而如果是"\",那么就得写成"\\\\".
  3. 如果一个字符串中有多个分隔符,可以用"|"作为连字符.

🌊代码示例: 拆分IP地址

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "192.168.1.1";
        //因为'.'是一个特殊字符,要使用转义字符'\'
        //又因为'\'也是一个特殊字符,要使用"\\"表示'\',然后通过'\'对'.'进行转义
        String[] strings = str.split("\\.");
        for (String s:strings) {
            System.out.println(s);
        }
    }
}

//运行结果:
192
168
1
1

🌊代码示例: 以 '\' 字符进行拆分

public class TestDemo1 {
    public static void main(String[] args) {
        //使用"\\"表示'\'
        String str = "192\\168\\1\\1";
        //以 '\' 进行拆分,需要"\\\\"
        //因为'\'是一个特殊字符,要使用"\\"表示'\','\'又会对后面的字符进行转义,所以要使用4个
        String[] strings = str.split("\\\\");
        for (String s:strings) {
            System.out.println(s);
        }
    }
}

//运行结果:
192
168
1
1

🌊代码示例: 符串中有多个分隔符,用 "|" 作为连字符

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "Java30 12&21#hello";
        String[] strings = str.split(" |&|#");
        for (String s:strings) {
            System.out.println(s);
        }
    }
}

//运行结果:
Java30
12
21
hello

🍓字符串截取

  • 从一个完整的字符串之中截取出部分内容

💥注意事项:

  1. 索引从0开始,返回原字符串
  2. 注意前闭后开区间的写法, substring(0, 5) 表示包含 0 号下标的字符, 不包含 5 号下标

💦从字符串指定索引位置截取到结尾

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "abcdefg";
        //从下标为2的位置开始截取字符串
        String s = str.substring(2);
        System.out.println(s);
    }
}

//运行结果:cdefg

💦截取部分内容(左闭右开)

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "abcdefg";
        //从下标为2的位置开始截取字符串到下标为3的位置 [2,4)
        String s = str.substring(2,4);
        System.out.println(s);
    }
}

//运行结果:cd

🍓其他操作方法

💦去除字符串中的左右空格,保留中间的空格

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "   hello    world    " ;
        System.out.println("["+str+"]");
        System.out.println("["+str.trim()+"]");
    }
}

//运行结果:
[   hello    world    ]
[hello    world]
  • trim()方法会去掉字符串开头和结尾的空白字符(空格, 换行, 制表符等)

💦字符串转大写

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "abcdeFYKL";
        String ret = str.toUpperCase();
        System.out.println(ret);
    }
}

//运行结果:ABCDEFYKL

💦字符串转小写

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "abcdeFYKL";
        String ret = str.toLowerCase();
        System.out.println(ret);
    }
}

//运行结果:abcdefykl

💦字符串入池操作

  •  使用 intern() 方法,此方法在前面已经介绍了。

💦字符串连接,连接后的字符串不会放到常量池中。

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "hello";
        String ret = str.concat("world");
        System.out.println(ret);
    }
}

//运行结果:helloworld

💦获取字符串长度

public class TestDemo1 {
    public static void main(String[] args) {
        String str = "abcdef";
        int len = str.length();
        System.out.println(len);
    }
}

//运行结果:6
  • 注意:数组长度使用数组名称.length属性,而String中使用的是length()方法

💦判断字符串是否为空。(字符串长度是否为0,不是null)

public class TestDemo1 {
    public static void main(String[] args) {
        String str1 = ""; //长度为0,所以该字符串为空
        String str2 = "    ";//空格,长度不为0,所以该字符串不为空
        boolean flag1 = str1.isEmpty();
        boolean flag2 = str2.isEmpty();
        System.out.println(flag1);
        System.out.println(flag2);
    }
}

//运行结果:
true
false

🌊练习:将字符串的首字母变成大写

public class TestDemo1 {
    public static String fistUpper(String s){
        if("".equals(s) || s==null){
            return s;
        }
        if(s.length()>1){
            return s.substring(0,1).toUpperCase()+s.substring(1);
        }
        return s.toUpperCase();
    }
    public static void main(String[] args) {
        String str = "hello";
        String ret = fistUpper(str);
        System.out.println(ret);
    }
}

//运行结果:Hello

七、StringBuffer 和 StringBuilder

🚀回顾String类的特点:

  • 任何的字符串常量都是String对象,而且String的常量一旦声明不可改变,如果改变对象内容,改变的是其引用的指向而已。
  • 通常来讲String的操作比较简单,但是由于String的不可更改特性,为了方便字符串的修改,提供StringBuffer和StringBuilder类。
  • StringBuffer 和 StringBuilder 大部分功能是相同的,这里主要介绍 StringBuilder
  • 在String中使用"+"来进行字符串连接,但是这个操作在StringBuffer类中需要更改为append()方法

🍓StringBuilder 的使用

💦使用构造方法赋值

public class TestDemo1 {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder("abcde");
        //StringBuilder 类中有一个 toString()方法,直接传引用会默认调用toString()方法打印
        System.out.println(sb); //默认调用toString()方法
        System.out.println(sb.toString()); //直接调用toString()方法
    }
}

//运行结果
abcde
abcde

💦使用append()方法赋值

public class TestDemo1 {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        sb.append("abcde");
        System.out.println(sb);
    }
}

运行结果:abcde

public class TestDemo1 {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        sb.append("abced");
        sb.append("1234");
        System.out.println(sb);
    }
}

//运行结果:abced1234

append()方法可以连用

public class TestDemo1 {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        sb.append("abced").append("1234");
        System.out.println(sb);
    }
}
//abced1234

🍓普通的String拼接,底层会被优化为StringBuilder

public class TestDemo2 {
    public static void main(String[] args) {
        String str = "hello";
        str +="abc";
        System.out.println(str);
    }
}

//运行结果:helloabc

如果在循环里面进行字符串拼接,尽量不要使用String,优先使用StringBuffer 和 StringBuilder。

🍓StringBuffer 和 StringBuilder的区别

  • 可以看到StringBuffer类中的方法都被 synchronized 关键字修饰,而StringBuilder类中的方法并没有。
  • synchronized关键字可以保证线程安全。
  • StringBuffer 一般用于多线程,StringBuilder 一般用于单线程

🍓String 与 StringBuffer 或 StringBuilder 之间的转换

String和StringBuffer类不能直接转换。如果要想互相转换,可以采用如下原则:

String变为StringBuffer:利用StringBuffer的构造方法或append()方法

    /**
     * String -> StringBuffer 或  StringBuilder
     * 使用构造方法
     * @return
     */
    public static StringBuffer func1(){
        String str = "abcde";
        StringBuffer sb =  new StringBuffer(str);
        return sb;
    }
    /**
     * String -> StringBuffer 或  StringBuilder
     * 使用append()方法
     * @return
     */
    public static StringBuffer func2(){
        String str = "abcde";
        StringBuffer sb = new StringBuffer();
        sb.append(str);
        return sb;
    }

⭐StringBuffer变为String:调用toString()方法

   /**
     * StringBuffer 或 StringBuilder -> String
     * 调用 toString() 方法
     * @return
     */
    public static String func3(){
        StringBuffer sb =  new StringBuffer("abcde");
        return sb.toString();
    }

🍓StringBuffer类中部分方法的使用

💦字符串反转

public synchronized StringBuffer reverse()

代码演示:

public class TestDemo1 {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer("abcdef");
        System.out.println(sb.reverse());
    }
}

//运行结果:fedcba

💦删除指定范围的数据

public synchronized StringBuffer delete(int start, int end)

代码演示:

public class TestDemo1 {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer("hello world");
        System.out.println(sb.delete(5, 10));
    }
}


//运行结果:hellod

💦插入数据

public synchronized StringBuffer insert(int offset, 各种数据类型 b)

代码演示:

public class TestDemo1 {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer("helloworld");
        System.out.println(sb.delete(5, 10).insert(0, "你好"));
    }
}

//运行结果:你好hello

八、字符串的常见问题

1、解释String类中两种对象实例化的区别?

  1. 直接赋值:只会开辟一块堆内存空间,并且该字符串对象可以自动保存在对象池中以供下次使用。
  2. 构造方法:会开辟两块堆内存空间,不会自动保存在对象池中,可以使用intern()方法手工入池。

2、为什么 String 要不可变?(不可变对象的好处是什么?)

  1. 方便实现字符串对象池. 如果 String 可变, 那么对象池就需要考虑何时深拷贝字符串的问题。
  2. 不可变对象可以使线程是安全的。
  3. 不可变对象更方便缓存 hash code, 作为 key 时可以更高效的保存到 HashMap 中。

3、解释String、StringBuffer、StringBuilder的区别

  1. String的内容不可修改,StringBuffer与StringBuilder的内容可以修改。频繁修改字符串的情况考虑使用 StingBuffer 或 StringBuilder。
  2. StringBuffer与StringBuilder大部分功能是相似的。
  3. StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

潇湘夜雨.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值