1.常量池与不可变

(1)几种定义方式

public static void main(String[] args) {
        // 使用常量串构造
        String s1 = "hello";
        System.out.println(s1);

        // 直接newString对象
        String s2 = new String("hello");
        System.out.println(s2);

        // 使用字符数组进行构造
        char[] array = {'h','e','l','l','o'};
        String s3 = new String(array);
        System.out.println(s3);
}

结果:

(2)字符串常量池:java内置机制,JVM运行时会自动构造,不用程序员关注

(3)不可变:String类被final修饰,表明value自身的值不能改变,即不能引用其它字符数组,但是其引用空间中 的内容可以修改(String 不可变,常量池地址也不变;new 对象内容不变,地址可换

    // 1. 常量池:地址不变

    String s1 = "Java";
    String s2 = "Java";          // 同一常量池对象
    System.out.println("常量池地址相同? " + (s1 == s2));      // true

    // 2. new 对象:内容相同,地址不同

    String s3 = new String("Java");
    String s4 = new String("Java");
    System.out.println("new 地址相同? " + (s3 == s4));      // false
    System.out.println("内容相同? " + s3.equals(s4));      // true

(4)String常量自己会变是错觉——你拿到的已经是另一个全新的 String 对象,只是变量名 reuse 了。

代码看真相:

 String s = "Java";          // 常量池对象 A
 s = s + "Script";           // 新建对象 B("JavaScript")
 System.out.println(s);      // 输出 "JavaScript"

过程拆解:

  • Java → 常量池对象 A(不可变)
  • + "Script" 新建对象 B("JavaScript")
  • s = ...  → 变量 s引用从 A 改成 B,A 本身从未改变

结论: 变量可以指向新对象,但原 String 对象内容永不变。

2.三种比较

(1)"=="(双等号):两个引用指向的对象是否是同一个

String s1="abc";
String s2="abc";
System.out.println(s1==s2);

结果为:true
String s1=new String("abc");
String s2=new String("abc");
System.out.println(s1==s2);

结果为:false(因为这里写法是新创建了两个对象,两个对象地址空间不同,所以两个引用自然不同)

(2)equals:两个引用对应的对象包含的字符内容是否相等

String s1=new String("abc");
String s2=new String("abc");
System.out.println(s1.equals(s2));
System.out.println("abc".equals(s2));

结果均为true(equals比较的是内容)

(3)compareTo字典序:String已经实现Comparable,可直接使用compareTo方法,compareToIgnoreCase方法忽略大小写,注意汉字排序按照汉字每个字节比较大小,并不符合我们的使用习惯(按照笔画数量排序)

String s1 = "abc";
String s2 = "def";
int cmp = s1.compareTo(s2);   // 字典序:a→d 差值
System.out.println(cmp);    

结果:输出 -3 (a 比 d 小 3)

解释:若s1小于s2返回小于0的数,至于什么值就是首字母a的ascll码减去首字母d的ascll码的值

3.查找与截取

(一)查找

(1)charAt:给定下标,获取对应字符

String s1=new String("abc");
System.out.println(s1.charAt(0));

结果:a

(2)indexOf:查找改字符串的某个子字符串,从左往右找,如果是,是处于哪个位置(子串第一个字符下标)

String s = "hello world hello bit";
int idx1 = s.indexOf("world");      // 6
int idx2 = s.indexOf("hello");      // 0
int idx3 = s.indexOf("hello", 5);   // 12(从第5位开始找)
System.out.println("indexOf结果:"+idx1+", "+idx2+", "+idx3);

indexOf结果:6, 0, 12

(3)lastIndexOf:也是查找字符串,从右往左找(返回的也是子串第一个字符下标)

String s = "hello world hello bit";
int last1 = s.lastIndexOf("hello");     // 12
int last2 = s.lastIndexOf("hello", 10); // 0(从右往左,但不超过第10位)
System.out.println("lastIndexOf结果:"+last1+", "+last2);

lastIndexOf结果:12, 0

(二)截取

substring方法:

String str="abcdefg";
String s1=str.substring(0,4);//下标范围左闭右开
System.out.println(s1);

结果:abcd

4.转换与格式化

(一)字符串转换

(1)String.valueOf:数字转字符串

String s1 = String.valueOf(123);

结果:“123”

(2)Integer.parseInt:字符串转整型

int n = Integer.parseInt("456");          

结果:456

(3)若直接数字+字符串,则结果是字符串,数字的类型(如int型)隐式转换为String类型

String s3 = 789 + "abc";    

结果:"789abc"

(4)小写转大写和大写转小写:toUpperCase和toLowerCase,注意这两个方法都是生成新的字符串,而不是在上一个toUpperCase或toLowerCase基础上进行转换,因为String本身是常量不可修改

String s4 = "Hello".toUpperCase();        
String s5 = "Hello".toLowerCase();      

结果:
"HELLO"
"hello"

(5)toCharArray:将字符串转成字符数组

char[] ch = "abc".toCharArray();

结果:['a','b','c']

(6)getBytes:转为字节数组,经常十六进制形式打印(方便看),原本一个汉字占2个字节,若以三个字节为标准打印,结果展现的是一个汉字,这个和编码方式有关,所以一个汉字可能2个或3个字节,utf-8是世界通用编码也是变长编码,长度可改变所以可支持各种国家语言,因为不同语言的字符的字节数可能不同

(二)格式化

格式化字符串:

int x=10;
System.out.printf("x=%d\n",x);
String s=String.format("x=%d\n",x);
System.out.println(s);

结果都是:x=10


String s=String.format("%d-%d-%d",2026,1,27);
System.out.println(s);

结果:2026-1-27

5.拆分与替换

(一)拆分

(1)split方法:

String s="java,python,c++";
String []str=s.split(",");
for(String s1:str){
    System.out.println(s1);
}

结果:
java
python
c++

(2)拆分IP地址:有些特殊字符作为分割符可能无法正确切分, 需要加上转义

String str = "192.168.1.1" ;
String[] result = str.split("\\.") ;
for(String s: result) {
System.out.println(s);
    }

结果:
192
168
1
1

注意:

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

(二)替换

(1)repalce方法,可替换所有的字符,替换的结果是新的字符串,不会改变原来的

String s="abcd efgh";
String str=s.replace("abcd","ABCD");
System.out.println(s);
System.out.println(str);

结果:
abcd efgh
ABCD efgh

    6.StringBuilder vs StringBuffer

    (1)StringBuilder定义的字符串可以修改,不会创建新的对象,都是对同一个对象操作的,StringBuilder效率高

    (2)StringBuffer(旧)和StringBuilder(新)相似,也是可变的,优先StringBuilder

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

    • String变为StringBuilder: 利用StringBuilder的构造方法或append()方法
    • StringBuilder变为String: 调用toString()方法。
    StringBuilder stringBuilder=new StringBuilder("abcd");
    String str=stringBuilder.substring(0,2);
    System.out.println("append前:"+stringBuilder);
    stringBuilder.append('e');
    System.out.println("append后:"+stringBuilder);
    
    结果:
    append前:abcd
    append后:abcde

    (4)注意:substring() 方法不修改原对象,虽然 StringBuilder可变的(mutable),但 substring() 方法并不会修改原对象,而是返回一个新的 String

    方法 是否修改原 StringBuilder 返回值
    append() ,insert() ,delete() ,reverse() 修改自身 StringBuilder(支持链式调用)
    substring() 不修改,原对象保持不变 String(新的字符串)

    7.其他操作方法

    (1)trim方法:只是去左右两侧的,不会去中间的

    String str="   sddaf   afse     ";
    str=str.trim();
    System.out.println(str);

    运行结果:

    (2)intern方法:手动把字符串添加到常量池

    String s=new String("hello");
    String s1=s.intern();
    System.out.println(s=="hello");
    System.out.println(s1=="hello");
    
    结果:
    false
    true

    解释:

    步骤 动作 位置
    字面量 "hello" 被放入字符串常量池(如果不在的话) 常量池
    new String()堆内存中创建一个新的 String 对象
    表达式 结果 原因
    s == "hello" false s 指向对象,"hello" 指向常量池
    s1 == "hello" true 两者都指向常量池的同一对象

    而 “==” 比较的是引用地址,不是内容,内容用equals方法比较

    Logo

    汇聚全球AI编程工具,助力开发者即刻编程。

    更多推荐