文章目录
字符串存储结构:
char[]数组
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LZRozosa-1618628689448)(Untitled.assets/image-20210416142002972.png)]
字符串AB
与字符数组 char[] {'A', 'B'}
值是一致的
final修饰
String
类是final修饰,意味不允许继承
特殊的构造函数
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
String(char[] value, boolean share) {
// assert share : "unshared not supported";
this.value = value;
}
第一个构造,拷贝了一份char[]
值,因为字符不可变,外部访问安全,用public
修饰。
第二个构造赋值char[]
,外部访问不安全,构造的value
与入参同一个引用,可以外部修改,故default
修饰。
字符串不一定不可变,利用反射修改字符串
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Constructor<String> constructor = String.class.getDeclaredConstructor(char[].class, boolean.class);
constructor.setAccessible(true);
char[] value = {'A', 'A'};
String str = constructor.newInstance(value, true);
System.out.println(str); // AA
value[1] = 'B';
System.out.println(str); // AB
}
通过反射,获取该字符串的构造函数,通过setAccessible
设置操作是信任操作,不会进行access check
,构造出来的str
内部的 value
与 入参value
是同一个引用,可以修改(不能替换对象,因为是 final
修饰)。
字符串连接:+
public static void main(String[] args) {
String s3 = "A";
String s4 = "B";
String s5 = "A" + "B";
String s6 = s3 + s4;
}
查看jvm字节码指令 javap -c xxx.java
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String A
2: astore_1
3: ldc #3 // String B
5: astore_2
6: ldc #4 // String AB
8: astore_3
9: new #5 // class java/lang/StringBuilder
12: dup
13: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V
16: aload_1
17: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
20: aload_2
21: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
27: astore 4
29: return
其中String s5 = “A” + “B”;会合并成"AB"加入常量池
而s3 + s4,则aload_x去读取变量,自动(第9行)new StringBuilder,通过append,再toString返回赋值
如果将s3+s4放入循环:
public static void main(String[] args) {
String s3 = "A";
String s4 = "B";
String s5 = "A" + "B";
for (int i = 0; i < 10; i++) {
String s6 = s3 + s4;
}
}
查看jvm字节码指令 javap -c xxx.java
12: iload 4
14: bipush 10
16: if_icmpge 45
19: new #5 // class java/lang/StringBuilder
22: dup
23: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V
26: aload_1
27: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
30: aload_2
31: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
34: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
37: astore 5
39: iinc 4, 1
42: goto 12
会在循环体内不断创建StringBuilder对象,如果大循环,则应该手动在循环外创建StringBuilder。
字符串常量池:
(StringTable的Hash表组成:-XX:StringTableSize指定大小)
public static void main(String[] args) {
String s1 = "AB"; // 方法区(常量池)
String s2 = new String("AB"); // s2 堆
String s3 = "A"; // 方法区(常量池)
String s4 = "B"; // 方法区(常量池)
String s5 = "A" + "B"; // 方法区(常量池)
String s6 = s3 + s4; // s3+s4 堆
System.out.println(s1 == s2); // false
System.out.println(s1 == s5); // true
System.out.println(s1 == s6); // false
System.out.println(s1 == s6.intern()); // true
System.out.println(s2 == s2.intern()); // false
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GBd4mpha-1618628689451)(F:\学习\知识点\Java\Java\Untitled.assets\20180628211423787.png)]
字符串函数:
- equals、startsWith、endsWith、compareTo、equalsIgnoreCase等比较两个字符串的操作函数
- substring
- intern
equals、startsWith、endsWith、compareTo、equalsIgnoreCase等
原理都是循环比较两个字符串的 char[]
的单个值,如equals,取出两个字符串的 char[]
在while中循环比较
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
substring
由于字符串是存在被 final
修饰的 char[]
内,所以会创建新对象存储新的字符串
private final char value[];
public String substring(int beginIndex, int endIndex) {
// ..省略..
return ((beginIndex == 0) && (endIndex == value.length)) ? this
: new String(value, beginIndex, subLen);
}
intern
如果字符串常量池里存在一个和当前字符串对象等价的字符串,那么返回字符串常量池里那个对象,如果不存在,把当前字符串对象存进常量池,返回当前字符串对象。
public static void main(String[] args) {
String a = "A";
String b = "B";
String ab = "AB"; // 常量池中
String ab1 = a + b; // 堆中
System.out.println(ab == ab1); // false
String ab1 = (a + b).intern(); // 对象在堆中,返回常量池的引用
System.out.println(ab == ab1); // true
}