String类
Java中使用的字符串值都是此类的对象。
创建方式有两种:
public static void main(String[] args) {
// 创建方式有两种
String s1 = "hello";
String s2 = new String("hello");
}
原理:
- 是一个final类,不能被继承。
- 存储字符串的方式是采用字符数组。
- 该数组是final的,表示不能改变该数组的地址。
- String的值是不可变的
- String也是使用常量池的,如果直接赋值,是在常量池中创建对象,如果使用new,是在堆中创建对象。
// 部分源码
public final class String // final类
implements java.io.Serializable, Comparable<String>, CharSequence {
// final的字符数组
private final char value[];
}
public class Demo3 {
public static void main(String[] args) {
// 创建方式有两种
String s1 = "hello";
String s2 = new String("hello");
String s3 = "hello";
String s4 = new String("hello");
System.out.println(s3==s4); // false
}
}
常见的字符串类中的方法:
public class Demo4 {
public static void main(String[] args) {
String s = "hello, world! hello, world";
// 根据下标获取字符
char ch = s.charAt(0);
System.out.println(ch);
// 判定一个字符串是否包含另一个字符串
boolean b = s.contains("llo");
System.out.println(b);
// 将字符串转换成字符数组
char[] array = s.toCharArray();
System.out.println(Arrays.toString(array));
// 查找一个字符串在另一个字符串中首次出现的位置,得到相应的下标,如果不存在返回-1
int index = s.indexOf("world");
System.out.println(index);
// 查找一个字符串在另一个字符串中出现的位置,从最后开始查找,得到相应的下标,如果不存在返回-1
int index1 = s.lastIndexOf("world");
System.out.println(index1);
// 返回字符串的长度
int length = s.length();
System.out.println(length);
// 去掉字符串前后的空格
String s1 = " hello, world ";
s1 = s1.trim(); // 字符串的值是不可变的,所有改变字符串的结果的方法一定有返回值,应该接收返回值
System.out.println(s1);
// 将小写字母转成大写
s = s.toUpperCase();
System.out.println(s);
// 将大写字母转成小写
s = s.toLowerCase();
System.out.println(s);
// 判定一个字符串是否以另一个字符串结尾
boolean b1 = s.endsWith("rld");
System.out.println(b1);
// 判定一个字符串是否以另一个字符串开头
boolean b2 = s.startsWith("rld");
System.out.println(b2);
// 将一个字符串中的包含的字符串全部替换成其他字符串
s = s.replace("hello", "a");
System.out.println(s);
// 从指定位置开始截取部分字符串,截取后面所有的
System.out.println(s.substring(4));
// 从指定位置开始截取部分字符串,截取到另一个位置,从4到8
System.out.println(s.substring(4, 8));
// 将字符串根据指定的内容进行切割,成一个数组
String[] strings = s.split("!");
System.out.println(Arrays.toString(strings));
// 将两个字符串进行拼接
String s2 = "hello";
String s3 = "world";
s2 = s2.concat(s3);
System.out.println(s2);
}
}
在使用字符串时,可能会发生编译器优化,例如下面的变量s3和s4:
public class Demo5 {
public static void main(String[] args) {
String a = "a";
String b = "b";
String ab = "ab";
String s1 = a + b; // 使用StringBuilder创建出来的结果,相当于new一个对象
System.out.println(s1 == ab); // false
String s2 = a + "b"; // 使用StringBuilder创建出来的结果,相当于new一个对象
System.out.println(s2 == ab); // false
// 两个常量,结果绝对不会发生其他的变化,就是ab,所以编译器会进行优化,直接将代码优化为String s3 = "ab";
String s3 = "a" + "b";
System.out.println(s3 == ab); // true
// 两个常量,结果绝对不会发生其他的变化,就是ab,所以编译器会进行优化,直接将代码优化为String s4 = "ab";
final String a1 = "a";
String s4 = a1 + "b";
System.out.println(s4 == ab); // true
String s5 = a.concat(b); // 拼接字符串,结果是new的
System.out.println(s5 == ab); // false
String s6 = ab.concat(""); // 当拼接的内容为null或者长度为0时,直接返回原字符串
System.out.println(s6 == ab); // true
String s7 = s2.intern(); // 直接得到常量池中的地址,相当于String s7 = "ab";
System.out.println(s7 == ab); // true
}
}
可变字符串
当需要频繁改变字符串时,应该使用可变字符串。
StringBuffer:JDK1.0提供,安全性高,性能较低。
StringBuilder:JDK1.5提供,性能较高,不安全。
基本用法:
public class Demo6 {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
// 往最后追加内容
sb.append("hello");
sb.append("world");
// 修改
sb.replace(3, 5, "aaaaaaa");
// 插入内容
sb.insert(1, "bbb");
// 删除内容
sb.delete(1, 4);
System.out.println(sb);
}
}
原理:
- 使用无参构造时,默认创建的字符数组大小为16个char
- 可以在创建时指定大小。
- 如果指定字符串,会设置大小为字符串的长度加上16
- 当空间不够时,需要扩容,扩容的大小是原本的大小的2倍+2
// 部分源码
// 默认大小为16
public StringBuffer() {
super(16);
}
// 指定大小
public StringBuffer(int capacity) {
super(capacity);
}
// 如果指定字符串,会设置大小为字符串的长度加上16
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
void expandCapacity(int minimumCapacity) {
// 当空间不够时,需要扩容,扩容的大小是原本的大小的2倍+2
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
value = Arrays.copyOf(value, newCapacity);
}