zoukankan      html  css  js  c++  java
  • 【Java基础】String 相关知识点总结

    String 相关知识点总结

    字符串的不可变性

    概述

    String 被声明为 final,因此它不可继承

    在 Java8 中,String 内部使用 char 数组存储数据

    public final class String 
        implements java.io.Serializable, Comparable<String>, CharSequence {
        /** The value is used for character storage. */
        private final char value[];
    }
    

    在 Java9 之后,String 类的实现改用 byte 数组存储字符串,同时使用 coder 来表示使用了哪种编码。

    public final class String
        implements java.io.Serializable, Comparable<String>, CharSequence {
        /** The value is used for character storage. */
        private final byte[] value;
    
        /** The identifier of the encoding used to encode the bytes in {@code value}. */
        private final byte coder;
    }
    

    value 数组被声明为 final,这意味着 value 数组初始化后就不能引用其他数组。并且 String 内部没有改变 value 的方法,因此可以保证 String 不可变。

    字符串的连接

    String s = "abcd";
    s.concat("ef");
    

    s 中保存的是一个重新创建出来的string对象的引用。

    如果你需要一个可修改的字符串,应该使用StringBuffer 或者 StringBuilder。否则会有大量时间浪费在垃圾回收上,因为每次试图修改都有新的string对象被创建出来。

    不可变的好处

    1. 可以缓存 hash 值

    因为 String 的 hash 值经常被使用,例如 String 用作 HashMap 的 key。不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。

    2. String Pool 的需要

    如果一个 String 对象以及被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。

    3. 安全性

    String 经常作为参数,String 不可变性可以保证参数不可变。例如在作为网络连接参数的情况下如果 String 是可变的,那么在网络连接过程中,String 被改变,改变 String 对象的那一方以为现在连接的是其他主机,而实际情况却不一定。

    4. 线程安全

    String 不可变性天生具备线程安全,可以在多个线程中使用。

    String,StringBuffer,StringBuilder

    1. 可变性

    • String 不可变
    • StringBuffer 和 StringBuilder 可变

    2. 线程安全

    • String 不可变,因此是线程安全的
    • StringBuilder 不是线程安全的
    • StringBuffer 是线程安全的,内部使用 synchronized 进行同步

    String Pool

    字符串常量池(String Pool)保存着所有字符串的字面量(literal strings),这些字面量在编译时期就确定。不仅如此,还可以使用 String 的 intern() 方法在运行过程中将字符串添加到 String Pool 中。

    当一个字符串调用 intern() 方法时,如果 String Pool 中已经存在一个字符串和该字符串值相等(使用 equals() 方法进行确定),那么就会返回 String Pool 中字符串的引用;否则,就会在 String Pool 中添加一个新的字符串,并返回这个字符串的引用。

    下面实例中,s1 和 s2 采用 new String() 的方式新建了两个不同字符串,而 s3 和 s4 是通过 s1.intern() 方法取得一个字符串引用。intern() 首先把 s1 引用的字符串放到 String Pool 中,然后返回这个字符串引用。因此 s3 和 s4 引用的是同一个字符串。

    String s1 = new String("aaa");
    String s2 = new String("aaa");
    System.out.println(s1 == s2);           // false
    String s3 = s1.intern();
    String s4 = s1.intern();
    System.out.println(s3 == s4);           // true
    

    如果是采用 "bbb" 这种字面量的形式创建字符串,会自动地将字符串放入 String Pool 中。

    String s5 = "bbb";
    String s6 = "bbb";
    System.out.println(s5 == s6);  // true
    

    在 Java 7 之前,String Pool 被放在运行时常量池中,它属于永久代。而在 Java 7,String Pool 被移到堆中。这是因为永久代的空间有限,在大量使用字符串的场景下会导致 OutOfMemoryError 错误。

    new String("abc")

    使用这种方法会创建两个字符串对象(前提是 String Pool)中还没有 “abc” 字符串对象。

    • "abc" 属于字符串字面量,因此编译时期会在 String Pool 中创建一个字符串对象,指向这个 “abc” 字符串字面量;
    • 而使用 new 的方式会在堆中创建一个字符串对象。
      创建一个测试类,其 main 方法中使用这种方式来创建字符串对象。
    public class NewStringTest {
        public static void main(String[] args) {
            String s = new String("abc");
        }
    }
    

    使用 javap -verbose 进行反编译,得到以下内容:

    // ...
    Constant pool:
    // ...
       #2 = Class              #18            // java/lang/String
       #3 = String             #19            // abc
    // ...
      #18 = Utf8               java/lang/String
      #19 = Utf8               abc
    // ...
    
      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=3, locals=2, args_size=1
             0: new           #2                  // class java/lang/String
             3: dup
             4: ldc           #3                  // String abc
             6: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
             9: astore_1
    // ...
    

    在 Constant Pool 中,#19 存储这字符串字面量 “abc”,#3 是 String Pool 的字符串对象,它指向 #19 这个字符串字面量。在 main 方法中,0:行使用 new #2 在堆中创建了一个字符串对象,并且使用 ldc #3 将 String Pool 中的字符串对象作为 String 的构造函数的参数。

    以下是 String 构造函数的源码,可以看到,在将一个字符串对象作为另一个字符串对象的构造函数参数时,并不会完全复制 value 数组内容,而是都会指向同一个 value 数组。

    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }
    

    参考

    如有侵权,请联系删除!

  • 相关阅读:
    2018年最新整理ios APP审核被拒的常见原因
    在线一键生成安卓证书keystore文件
    iOS证书的类型功能和申请介绍
    【2018】ios app真机调试到上架App Store完整教程
    预防SQL注入
    Python模块——HashLib(摘要算法)与base64
    Python加密与解密
    PostgreSQL常用命令
    二级子目录(后台目录)设置二级域名
    积累
  • 原文地址:https://www.cnblogs.com/jojop/p/11317313.html
Copyright © 2011-2022 走看看