String常用构造器
- public String(): 空构造
- public String(byte0 bytes): 把字节数组转成字符串
- public String(byte0 bytes,int index,intlength): 把字节数组的一部分转成字符串
- public String(char[]value): 把字符数组转成字符串
- public String(char[] value,int index,int count): 把字符数组的一部分转成字符串
- public String(String original): 把字符串常量值转成字符串
String常用API
判断方法
- boolean equals(Object obj): 比较字符串的内容是否相同,区分大小写
- boolean equalslgnoreCase(String str): 比较字符串的内容是否相同,忽略大小写
- boolean contains(String str): 判断大字符串中是否包含小字符串
- boolean startsWith(String str): 判断字符串是否以某个指定的字符串开头
- boolean endswith(String str): 判断字符串是否以某个指定的字符串结尾
- boolean isEmpty(): 判断字符串是否为空
获取方法
- int length(): 获取字符串的长度
- char charAt(int index): 获取指定索引位置的字符
- int indexOf(int ch): 返回指定字符在此字符串中第一次出现处的索引,ch是字符的码表值
- int indexOf(String str): 返回指定字符串在此字符串中第一次出现处的索引
- int indexOf(int ch,int fromIndex): 返回指定字符在此字符串中从指定位置后第一次出现处的索引
- int indexOf(String str,int fromIndex): 返回指定字符串在此字符串中从指定位置后第一次出现处的索引
- int lastindexOf 同上
- String substring(int start): 从指定位置开始截取字符串,默认到末尾
- String substring(int start,int end): 从指定位置开始到指定位置结束截取字符串
转换方法
- byte[] getBytes(): 把字符串转换为字节数组
- char[] toCharArray(): 把字符串转换为字符数组
- static String valueOf(char[]chs): 把字符数组转成字符串
- static String valueOf(inti): 把int类型的数据转成字符串
-
- 注意: String类的valueOf方法可以把任意类型的数据转成字符串
- String toLowerCase(): 把字符串转成小写(了解)
- String toUpperCase(): 把字符串转成大写
- String concat(String str): 把字符串拼接
替换方法
- String replace(char old,charnew): 将字符串中的一部分字符用新的代替
- String replace(String old,String new): 将字符串中的一部分字符串用新的代替
切割方法
- String[] split(Stringregex): 按照规则来切割字符串
- String[] split(Stringregex,int limit): 按照规则来切割字符串 limit 表示总共切成几段(也就是数组的长度)
其他常用方法
- trim(): 去除字符串前后的空格
- int compareTo(String anotherString): 按字典顺序比较两个字符串(大小写)
- int compareTolgnoreCase(String str): 按字典顺序比较两个字符串(忽路大小)
java对String的优化:
- 使用方式
- 性能
使用方式不用new
// 字面量
String s1 = "abc";
// new
String s2 = new String("123");
性能优化
问题
使用new方式创建对象时,即使对象属性完全一致也会创建多个对象,过于浪费资源
Person p1 = new Person(18);
Person p2 = new Person(18);
sout(p1 == p2); // false
解决方法
为了不浪费资源,我们可以声明一个static常量,但是String字符串的创建是无规律、大数量的,我们不可能声明那么多static常量
public static final Person P = new Person(18);
Person p1 = P;
Person p2 = P;
sout(p1 == p2); // true
将这些对象放进一个容器中(池化思想),用时判断是否存在后获取,及对象池,就像缓存
// 从对象池中获取
Person p1 = PersonPool.get(18);
当我们使用字面量创建字符串对象时,会从字符串常量池中寻找是否已创建,已创建则直接引用,反之在常量池中新建。
String不可变
String是什么
字符串就是一串连续的字符(char);
java面向对象所以简化了C语言中使用char[]数组表示字符串的复杂方法,将char[]进行封装,及String;
不可变是什么
解释一下不可变:
// 可变
Person p = new Person(18);
p.setAge(20);
// 不可变,无法修改String类中的属性
String s = "123";
有人会说,这不是可变的吗?
String s = "123";
s = "456"
sout(s); // 456
这样是变量重新引用了一个新的对象,类比到Person上如下:
Person p = new Person(18);
p = new Person(20);
String如何实现不可变
在java String源码中char数组private final char value[];
- 被private修饰
- 不暴露和提供任何修改字符数组value的方法,有的字符串操作都是返回新字符串对象,获取底层字符数组时都是复制一个新数组进行返回
- String类被final修饰,不可被继承,杜绝了继承后子类方法覆盖父类的可能
- value被final修饰,及value的引用不会被重新指向
String为什么要不可变
- 保护字符串常量池,如果String提供setValue()方法,调用该方法时,字符串常量池中对象的value属性也被改变,如果另一个变量指向该对象但这时该对象的属性已经改变,就会失去字符串常量池的作用。
- hash码不会变
- 线程安全
String StringBuilder StringBuffer StringJoiner
只有String重写了equals和hashCode方法
类 | 特点 | 适用场景 |
String | 不可变,线程安全 | 操作量少或不需要操作数据 |
StringBuilder | 可变,线程不安全 | 需要大量操作数据,且不用考虑线程安全 |
StringBuffer | 可变,线程安全,性能较低 | 需要大量操作数据,且考虑线程安全 |
StringJoiner | 可变,线程不安全 | 拼接字符串且有分隔符或前后缀 |
面试题
判断字符串地址是否
String s1 = "abc";
String s2 = "abc";
String s3 = new String("abc");
String s4 = "a" + "bc";// 编译时优化为 "abc"
String s5 = "a";
String s6 = s5 + "bc";// s5 是变量所以运行时才拿到,所以运行时拿到"a" + "bc",new一个StringBuilder调用append方法,toString后放入常量池
sout(s1 == s2);// true
sout(s1.equals(s2));// true
sout(s1 == s3);// false
sout(s1.equals(s3));// true
sout(s1 == s4);// true
sout(s1.equals(s4));// true
sout(s1 == s6);// false
new String("abc");创建了多少对象
- 字符串常量池中的对象:
- 当"abc"这个字符串字面量首次出现在代码中时,它会被放入字符串常量池(String Pool)。这个对象是由JVM自动创建的,用于避免相同字符串的重复创建和存储。
- 堆中的String对象:
- new String("abc")这一表达式会显式地创建一个新的String对象。即使"abc"已经存在于字符串常量池中,new关键字依然会在堆(Heap)中创建一个新的String对象,该对象的值是对常量池中"abc"的引用。
new String("a") + new String("b");
对象1: new StringBuilder()
对象2: new String(“a”)
对象3: 常量池中的"a"
对象4: new String(“b”)
对象5: 常量池中的"b"
深入剖析: StringBuilder 的 toString() 方法中有 new String(value, 0, count) ,
对象6 :new String(“ab”)
String str = "a" + "b";
"a" + "b" 在编译时,就已经编译为 ab, 被放到常量池中。
所以只有一个对象 :ab