你是否真正吃透了 Java 的 String 类?本文从基础到进阶,梳理关键知识点,帮你夯实 String 类使用能力。
1.String类
在JAVA中被双括号括起来的就是String类,在JAVA中没有'\0'的说法,String类是一个引用类型,本文主要是在Sting类的内部方法的学习以及使用。
2.比较两个字符串的大小比较
2.1 Comparable
Comparable 是一个接口,用于定义对象的自然排序规则。实现了 Comparable 接口的类,可以通过其compareTo 方法来比较两个对象的大小,从而支持排序操作(如:Arrays.sort())
2.1.1 compareTo
compareTo 方法通过返回值定义两个对象(this 和参数 o)的大小关系:
返回负数:this < o(当前对象小于参数对象)
返回 0:this == o(当前对象等于参数对象)
返回正数:this > o(当前对象大于参数对象)
Comparable 接口的典型用途是:
- 让类的对象支持自然排序(如数字从小到大、字符串按字典序)。
- 使对象可以直接作为 TreeSet(有序集合)的元素或 TreeMap 的键(自动按排序规则存储)。
- 支持通过 Arrays.sort() 对对象集合 / 数组进行排序。
示例:
java
String s1 = "apple";
String s2 = "banana";
String s3 = "apple";
System.out.println(s1.compareTo(s2)); // -1(a在b前)
System.out.println(s2.compareTo(s1)); // 1(b在a后)
System.out.println(s1.compareTo(s3)); // 0(内容相同)
2.2 Comparator
在 Java 中,Comparator 是一个用于定义对象排序规则的函数式接口(位于 java.util 包),它与 Comparable 不同:Comparable 是类自身实现的 "内部 比较器"(定义自然排序),而 Comparator 是 "外部 比较器",允许在不修改原类的情况下,为类定义额外或临时的排序规则。核心方法为compare。
2.2.1 compare
与 Comparable 的 compareTo 类似,compare 方法的返回值定义排序逻辑:
返回负数:o1 < o2(o1 应排在o2 前面)
返回 0:o1 == o2(o1 和 o2 排序位置相同)
返回正数:o1 > o2(o1 应排在 o2 后面)
Comparator 适用于以下场景:
- 为未实现 Comparable 接口的类定义排序规则。
- 为已实现 Comparable的类提供额外的排序方式(不改变其默认的自然排序)。
- 作为参数传递给排序方法(如Collections.sort()、Arrays.sort())或有序集合(如 TreeSet、TreeMap),指定排序逻辑。
示例:
java
Comparator<String> comp = String::compareToIgnoreCase;
System.out.println(comp.compare("Apple", "apple")); // 0(忽略大小写相等)
System.out.println(comp.compare("Banana", "apple")); // 1(B的ASCII大于A)
System.out.println(comp.compare("cat", "Dog")); // -1(c的ASCII小于D)
2.3 equals
在 Java 中,String的equals()方法用于比较两个字符串的内容是否相等 ,而==比较的是对象的内存地址 。
equals()是Object类的方法,String重写了它,实现逻辑为:先判断是否为同一对象,再比较长度和每个字符是否一致
示例:
java
String s1 = "hello";
String s2 = new String("hello");
String s3 = "hello";
// 比较内容是否相同
System.out.println(s1.equals(s2)); // true(内容一致)
System.out.println(s1.equals(s3)); // true(常量池同一对象)
System.out.println(s1.equals("hello")); // true(字面量比较)
2.4 Comparable、Comparator 、 equals和==的关系
| 类型 | 作用 | 核心方法 / 逻辑 | 所属范畴 |
|---|---|---|---|
| Comparable | 定义类的自然排序规则 | int compareTo(T o) | 类自身实现(内部比较) |
| Comparator | 定义类的外部 / 临时排序规则 | int compare(T o1, T o2) | 外部实现(外部比较) |
| equals | 判断两个对象是否逻辑相等 | boolean equals(Object obj) | 对象的逻辑相等性判断 |
| == | 判断两个对象的内存地址是否一致 | == | / |
3.字符串的查找
3.1 字符串转字符
java
String str = "Hello";
char ch = str.charAt(0); // 输出H
上述代码是从字符串str中获取第0个下标的字符赋值给ch。
3.2 在字符串中找字符或字符串并返回下标
java
int i1 = str.indexOf('l',4); // 返回-1 找不到就返回-1
int i2 = str.indexOf('l',3); // 返回3
int i3 = str.indexOf("ll"); // 返回2
int i4 = str.indexOf("ll",3); // 返回-1
i1是在str中找l字符,从第四个下标开始找,找不到就返回-1.
i2找到了就返回找到的下标
i3是可以在字符串中找字符串,并返回其下标
i4是从字符串str的3下标开始找子字符串,找不到就返回-1
java
int i5 = str.lastIndexOf('l'); // 3
int i6 = str.lastIndexOf('l',2); // 2
int i7 = str.lastIndexOf("ll"); // 2
int i8 = str.lastIndexOf("ll",3); // 2
i5是在str中从后向前 找l字符,从最后一个下标开始找,找不到就返回-1.
i6是在str中从3下标开始向前 找l字符,
i7是可以在字符串中从后向前 找字符串,并返回 其下标
i8是从字符串str的3下标开始从后向前 找子字符串,找不到就返回-1.
但是如果是从2开始的话,也就是从2下标开始向前走的,但是这个子串ll的起始位置 是2,是可以找到这个ll子串的所以返回的是2.
4.字符串的转换
顾名思义就是将对象转换成字符串
java
String str = String.valueOf("abc");// abc
String str1 = String.valueOf(1); // 1
String str2 = String.valueOf(19); // 19
将对象转换成字符串,str1这里发生了装箱,将int->Integer.
在输出的时候使用"..." + 变量就是调用了valueOf方法
转换 char[] 有特殊重载:String.valueOf(char[] arr) 会将字符数组拼接为字符串(如 valueOf(new char[]{'a','b'}) → "ab")
4.1 包装类.valueOf () ------ 基本类型 / 字符串转包装类对象
Integer.valueOf(int i) 会缓存 -128~127 之间的整数对象 ,重复调用时返回同一个对象(而非新建),这是面试高频考点!
java
// 1. 基本类型 → 包装类(带缓存)
Integer i1 = Integer.valueOf(100);
Integer i2 = Integer.valueOf(100);
System.out.println(i1 == i2); // true(缓存命中,同一对象)
Integer i3 = Integer.valueOf(200);
Integer i4 = Integer.valueOf(200);
System.out.println(i3 == i4); // false(超出缓存范围,新建对象)
// 2. 字符串 → 包装类(需字符串格式匹配,否则抛异常)
Integer i5 = Integer.valueOf("123"); // 字符串"123"→Integer对象123
Long l1 = Long.valueOf("456"); // 字符串"456"→Long对象456
Double d1 = Double.valueOf("3.14"); // 字符串"3.14"→Double对象3.14
- Double.valueOf():无缓存(因为浮点数范围太大,无法缓存),每次调用都会新建对象;
- 与 new Integer(100)的区别:new 关键字强制新建对象,不使用缓存;valueOf() 优先使用缓存,效率更高。
4.2 BigDecimal.valueOf () ------ 浮点数转 BigDecimal(推荐)
- BigDecimal 有两个常用构造:new BigDecimal(double d) 可能因浮点数精度问题出错,而BigDecimal.valueOf(double d) 会先将浮点数转为字符串,避免精度丢失,是推荐用法。
java
// 错误示例:new BigDecimal(double) 精度丢失
BigDecimal bd1 = new BigDecimal(0.1);
System.out.println(bd1); // 输出:0.1000000000000000055511151231257827021181583404541015625
// 正确示例:valueOf(double) 避免精度丢失
BigDecimal bd2 = BigDecimal.valueOf(0.1);
System.out.println(bd2); // 输出:0.1(本质是调用 new BigDecimal("0.1"))
4.3 高频面试考点
- String.valueOf(null) 和 null.toString() 的区别:前者返回 "null",后者抛空指针;
- Integer.valueOf(100) == Integer.valueOf(100) 的结果:true(缓存命中);
- 为什么推荐BigDecimal.valueOf(0.1) 而非 new BigDecimal(0.1):避免浮点数精度丢失。
5.字符串的大小写转换
java
String str = "hello";
String str1 = "HELLO";
str = str.toUpperCase();// HELLO 小写转大写
str1 = str1.toLowerCase();// hello 大写转小写
6.字符串和字符数组的转换
java
String str = "hello";
char[] ch = str.toCharArray();
for(char ch1 : ch){
System.out.print(ch1 + " "); // h e l l o
}
System.out.println();
String str1 = new String(ch);
System.out.println(str1.toString()); //hello
7.字符串替换
java
String str = "hello";
str = str.replace('h','b');
System.out.println(str.toString()); //bello
str = str.replace("ll","s");
System.out.println(str.toString()); //heso
replace是String类的内置方法.
replaceFirst()方法是只替换第一个
replaceAll()方法是全部都替换
8.字符串拆分
java
String str = " I_am_a_teacher";
String[] str1 = str.split("_");
System.out.println(str.toString()); // I_am_a_teacher
for(String s : str1){
System.out.print(s.toString() + " "); // I am a teacher
}
按照_来拆分字符串,返回的是一个String[]数组。
9.字符串的截取
java
String str = "helloworld";
str = str.substring(2);
System.out.println(str.toString());// lloworld
str = str.substring(2,4);
System.out.println(str.toString());// ow
将字符串的前两个进行截取掉。也可以是两个参数,一般是左闭右开的区间
10.删除字符串首尾的空格
java
String str = " hello world ";
str = str.trim();
System.out.println(str.toString()); //hello world
11.字符串的不可变性
- String类在设计的时候就是不可以被修改的,所有涉及到可能修改String内容的都是重新创建新对象,对新对象进行修改。
- 被final修饰的类声明该类不可以被继承,final修饰引用类型表明了该引用变量不能再引用其他对象,但是其引用对象的内容可以修改。
12.StringBuilder和StringBuffer
逆制字符串:StringBuilder.reverse();
- String不可以被修改,但是StringBuilder和StringBuffer可以被修改。
- 但是StringBuilder一般是在单线程下,StringBuffer一般是在多线程下,可以保证线程安全。
- StringBuilder和StringBuffer的方法大多相同.