Java-深入String原理解析

字符串存储结构:

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
}

知识点总结

Gitee https://gitee.com/Xing_zhuyeye/knowledge_points/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值