一、String 类常用方法
1、构造方法
常用的这4种构造方法:直接法,或者传参字符串字面量、字符数组、字节数组。
在 JDK1.8 中,String 类的字符串实际存储在 char 数组中:
String 类也重写了 toString 方法,所以可以直接打印 String 类,打印效果就是字符串,而非 className@hashCode 的形式:
可以看到源码中,重写的 toString 直接返回的 this,因为 this 本身就是字符串类,println 会对 String 类进行处理,打印出字符串:
length() 方法 可以获取字符串长度,这区别于数组 通过属性 length 获取长度。
2、字符串的比较(包含字符串常量池)
在前面学习 equals 和 compareTo 方法时知道,直接使用**==** 比较,比较的是对象的地址,而非对象的内容:
但通过两种构造方式得到的 String 类,比较的结果却不同,它们存在区别:
- 法1 :① 字符串常量池不存在 "hello"对象 >> 在常量池创建"hello"对象 >> 在堆上创建"hello"对象 >> 在栈上创建 s1 引用,存放堆上对象地址。② 字符串常量池存在"hello"对象 >> 直接在堆上创建"hello"对象 >> 在栈上创建 s2 引用,存放堆上对象地址。
- 法2 :① 字符串常量池不存在 "hello"对象 >> 在常量池创建"hello"对象 >> 在栈上创建 s3 引用,存放常量池上对象地址。② 字符串常量池存在"hello"对象 >> 在栈上创建 s4 引用,存放常量池上对象地址。
因此,法1得到 false,法2得到 true。由此可见,将频繁使用的字符串存到字符串常量池 ,能够提高性能 和减少内存开销 。参考博客:深入理解Java字符串常量池 | 二哥的Java进阶之路
如果想将 new String("hello") 也存放到字符串常量池中,可手动地使用intern 方法:
回到比较,如果想比较对象的内容,使用 equals 和 compareTo 方法。还有 equalsIgnoreCase 和 compareToIgnoreCase 方法,可忽略字母大小写:
3、字符串查找
查找失败时,除了 charAt 抛出异常,其它方法都是返回 -1。
4、字符串转换
(1) 整数、浮点数、布尔值、对象、数组 >> 字符串
(2)字符串 >> 整数、浮点数、数组
(3)大小写转换
(4)格式化转换
5、字符串替换
6、字符串拆分
注,正则表达式中:
- *、+、| 有特殊含义,需要加上 "\\"。
- \ 在 java 中要用 "\\" 表示,在正则表达式中也有特殊含义,最后为"\\\\"。
- 正则表达式中,| 表示连字符,可连接多个分隔符。
7、字符串截取
8、字符串的不可变性
题外话:Java 中的引用就是 C语言指针的改版,存的是对象的地址,但是它更安全,因为只允许 = 和 . 操作(除了数组,还有[ ] 操作),所以简单又不易出错。
从 String 类的注释中可以看到,字符串是常量,它的值不可修改:
String 类的内容是怎么做到不可修改的呢?有人说是因为 final,但并不是:
修饰类的 final 表示 String 类不可继承;修饰 value 的 final 表示这个数组引用不可修改,并不是引用指向的内容不可修改。
value 引用指向的字符串内容不可修改的原因:
- value 数组由 private 修饰。
- String 类没有向外部提供修改 value 指向的对象的方法。
字符串不可修改的缺点 :每次修改字符串 ,其实是创建一个新的对象 ,效率低。
字符串不可修改的优点:
- 在多线程 中使用 String 类更安全。(线程以后学习)
- 方便储存在常量池。如果常量池中的字符串可修改,则会造成多个引用的变化。
- 方便将固定 hashCode 存储在哈希表中。(哈希表以后学习)
二、可修改的字符串(StringBuilder和StringBuffer)
- String 不可修改字符串内容,而 StringBuilder 和 StringBuffer 可修改字符串内容。
- StringBuilder 和 StringBuffer 的功能大致相同,最大的不同 是:StringBuffer 提供了 synchronized 操作,能够保证线程安全。(详情以后学习)
1、String 和 StringBuilder 的对比以及 append 方法
可以看到当需要频繁修改字符串时 ,使用 StringBuilder 的效率要高很多,因为它不需要频繁创建新的对象。每次 +=,都会将旧的 s 对象自动销毁,所以大的不是空间开销,而是创建对象的时间开销。
2、capacity 和 ensureCapacity
当用 StringBuilder 创建可修改的字符串对象时,实际会申请比初始化的字符串更大的空间 ,capacity 方法就是获取实际的空间大小:
当字符串容量不够时,StringBuilder 会自动扩容,有时为了防止频繁扩容造成时间开销的增加 ,会使用 ensureCapacity 提前手动设置 好比较大的字符串容量:
三、String 类练习题
1、387. 字符串中的第一个唯一字符 - 力扣(LeetCode)
思路:统计每种字母的个数,找到第一个个数为 1 的字母。
java
class Solution {
public int firstUniqChar(String s) {
int[] cnt = new int[256];
// 统计每种字符个数
for(int i = 0; i < s.length(); i++)
cnt[s.charAt(i)]++;
// 找到第一个唯一的字符
for(int i = 0; i < s.length(); i++)
if(cnt[s.charAt(i)] == 1)
return i;
// 未找到
return -1;
}
}
2、字符串最后一个单词的长度_牛客题霸_牛客网
思路:找到最后一个空格位置,根据空格位置裁剪出最后一个单词,获取长度。
java
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNextLine()) { // 注意 while 处理多个 case
String s = in.nextLine();
int index = s.lastIndexOf(' '); // 找到最后一个空格
String lastWord = s.substring(index + 1, s.length()); // 裁剪出最后一个单词
System.out.println(lastWord.length());
}
}
}
3、125. 验证回文串 - 力扣(LeetCode)
思路:先大写转小写,前、后设置一个标记移动,跳过非字母数字字符,匹配。
java
class Solution {
// 判断小写字母和数字
public boolean isValidChar(char ch) {
if((ch >= 'a' && ch <='z') || (ch >= '0' && ch <= '9'))
return true;
else
return false;
}
public boolean isPalindrome(String s) {
// 大写转小写
s = s.toLowerCase();
int i = 0, j = s.length() - 1;
// 前、后移动
while(i < j){
// 前、后跳过所有其它字符,直到遇到小写字母和数字停止
while(!isValidChar(s.charAt(i)) && i < j) i++;
while(!isValidChar(s.charAt(j)) && i < j) j--;
// 存在不匹配,则否
if(s.charAt(i) != s.charAt(j)) return false;
// 匹配,则更新i、j
i++;
j--;
}
return true;
}
}
四、补充 -- FittenCode 插件
它是一个AI功能,可以预测 你要写的代码 ,还能根据项目 的内容对它提问: