String类
1.字符串对象
Java中的字符串属于对象,Java提供了 String 类来创建和操作字符串
String 类可被 final 修饰,但不能被继承和重写
(1).字面量
含义:直接写出来的数值
10 -> int 字面量
10.1 -> double 字面量
true -> boolean 字面量
“abc” -> String 字面量 -> 就是一个字符串对象
(2).创建字符串的四种方式
方法一:直接赋值
String str = "Bye 2022!";
方法二:通过构造方法产生对象
Sring str2 = new String("Hello 2023!");
方法三:通过字符串数组产生对象
char[] data = new char[] {'a' , 'b' , 'c'};
String str = new String(data);
方法四:通过 String 静态方法 valuesOf (任意数据类型) -> 转换为字符串
String str = String.valueOf(10);
2.字符串常量池
1.使用 String 创建字符串对象后,JVM 会维护一个字符串常量池,若该对象不存在,则产生一个新的字符串对象,并加入到字符串常量池中。
2.继续使用赋值法产生字符串对象时,JVM 发现该引用指向内容在常量池中已经存在,则此时不再新建字符串对象,而是复用已有对象。
String a = "1"; // 常量池加入 "1"
String b = a + a; // JVM 优化为 "11"
b.intern();
//intern: 若该对象在常量池中不存在,则将该对象加入常量池,且将引用 "b" 指向常量池内的 "11"
String c = "11"; // 指向常量池
String d = new String("11"); // 堆中开辟新的空间 "11",常量池字符串与堆中的普通字符串永远不相等
String d1 = d.intern; // 该对象常量池中存在,引用 "d1" 指向常量池内的 "11"
System.out.println(b == c); // true
System.out.println(d == c); // false
System.out.println(d1 == c); // true
System.out.println(d1 == d); // false
图示:
注:
(1).直接使用双引号声明的 String 对象会储存在常量池中
(2).String 对象的intern
方法会得到字符串在常量池中对应的引用,如果常量池中没有对应字符串,则该对象会被添加到常量池中
3. intern 关键字
1.若当前常量池中已经存在该对象,则不会产生新的对象,返回常量池中的 String 对象
2.若当前常量池中不存在该对象,则将该对象入池,返回池后地址
3.对于任意两个字符串 s
和 t
,当且仅当s.equals(t)
为 true
时,s.intern() == t.intern()
才为 true
语法:
public native String intern();
值得注意的是:intern 关键字直接打印输出没有什么效果,但其作用往往在 ==
比较字符串常量池地址时显现。
// 这个 new "猫咪" 的猫咪不入常量池
String a = new String("猫咪");
String b = new String("猫咪");
String c = "猫咪";
String d = "猫" + "咪";
String e = "咪";
String f = "猫" + e;
System.out.println(a.intern() == b); // false
System.out.println(a.intern() == c); // true
System.out.println(a.intern() == d); // true
System.out.println(a.intern() == f); // false
System.out.println(a.intern() == b.intern()); // true
代码图示
4.字符串的比较
(1).所有引用数据类型在比较是否相等时,使用equals
方法比较
(2).引用数据类型使用 ==
比较的是数值(地址是否相等)
(3).区分大小比较 .equals
,不区分大小写比较 .equalsIgnorCase
(4).延伸:用户输入判空处理
System.out.println(useName.equals("三三")); // ×,最好不要这样做
System.out.println("三三".equals(useName)); // √,要把比较的内容放在前面,避免输入值为空
5.扩展:匿名对象
(1).匿名对象:new Student();
匿名对象的创建只存在于堆中,没有名字,只能使用一次
(2).非匿名对象:Student stu = new Student();
非匿名对象创建的对象也存在于堆中,但其类变量却在栈中,栈中的类变量通过创建变量的内存地址指向相应的对象
6.字符串的不可变性
1.字符串对象的内容不可变,字符串引用可以改变
2.字符串其实就是一个字符数组 -> char[]
,字符串保存的值实际上在数组中保存。
3.char[] 在源码中被 final
修饰,权限为 private
,String 类的外部拿不到这个 value 数组 private final char value[];
4.运行时通过反射 (field
) 破坏value 数组的封装 (private
) 可以进行修改
7.常用方法
构建与修改字符串对象
类名 | 用法 | 描述* |
---|---|---|
StringBuilder() | 拼接字符串 | 非线程安全,性能较好 |
StringBuffer() | 拼接字符串 | 线程安全,性能较差 |
sb.reverse() | 反转字符串 | – |
sb.delete(int start,int end) | 删除指定范围的数据 | – |
sb.insert(int start,[all]) | 将新元素插入当前sb对象 | – |
(用到时补充)… | … | … |
常用关键字
关键字 | 用法 |
---|---|
str.charAt(“i”) | 将第 i 位字符转换为 char 类型 |
str.isEmpty() | 判断是否为空字符串 |
str.getBytes() | 按照GBK编码将字符串转换为字节数组 |
str.split(“c”,拆分长度) | 拆分字符串 |
str.replaceAll(“c”,“替换符号”) | 替换原字符串 |
str.replaceFirst(“c”,“替换符号”) | 替换第一个遇到的字符"c" |
str.trim() | 去除两边空格,保留中间位置空格 |
str.contanis(“c”) | 是否包含“c” |
str.startWith(“c”) | 初始字符是否为"c" |
str.endWith(“c”) | 结尾字符是否为"c" |
str.toUpperCase() | 转大写 |
str.toLowerCase() | 转小写 |
(用到时补充)… | … |
注:
(1).String 是不可变字符序列,StringBuilder 和 StringBuffer 是可变字符序列
(2).执行速度StringBuilder > StringBuffer > String
(3).StringBuilder是非线程安全的,StringBuffer 是线程安全的
8.附加练习
一、判断字符串是否是纯数字
public boolean isNumber(String str) {
// 转换为字符串数组
char[] data = str.toCharArray();
// 遍历循环
for (char c : data) {
// 判断是否是纯数字,找反例
// if (c < '0' || c > '9') {
//.isDigit() 如果字符为数字则返回 true
if (!Character.isDigit(c)) {
return false;
}
}
return true;
}
二、单词首字母大写操作
public String firstUpper(String str) {
// 判空
if (str == null || str.isEmpty()) {
return null;
}
// 边界条件
if (str.length() == 1) {
return str.toLowerCase();
}
// 截取 str 最大长度 1 ,截取首位大写 + 截取后半部分拼接
return str.substring(0,1).toUpperCase() + str.substring(1);
}