Java核心技术之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的不可变性

好处

  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 and StringBuilder

  1. 可变性

    • String不可变
    • StringBuffer和StringBuilder可变
  2. 线程安全

    • String不可变,因此是线程安全的
    • StringBuilder不是线程安全的,效率高
    • StringBuffer是线程安全的,内部使用synchronized进行同步,效率低
  3. 内存解析

 StringBuffer sb = new StringBuffer();//new char[16];底层创建了一个长度是16的数组
 StringBuffer sb2 = new StringBuffer("abc");//char[] value = new char["abc".length() + 16];
 

扩容问题:如果要添加的数据底层char数组装不下,那就需要进行扩容。

  • 默认情况下,扩容为原来容量的2倍+2,同时将原有的数组重点元素复制到新的数组中。
  • 指导意义,开发中建议使用StringBuffer(int capacity)或StringBuilder(int capacity)。

String Pool

字符串常量池保存着字符串字面量,这些字面量在编译时期就确定。还可以使用String的intern()方法在与逆行过程将字符串添加到String Pool中。

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

代码示例:

其中s1和s2采用new的方式创建了两个字符串。s1和s2会在堆中创建两个地址,两个地址指向同一个常量池中对象。

s3和s4采用intern()方法,返回的是同一个常量池中的地址。

s5和s6这种字面量的形式创建的字符串,会自动地将字符串放入常量池中。

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

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

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

在Java6时,String Pool在方法区(永久区)

在Java7时,String Pool在堆空间,因为永久区的空间有限

在Java8时,String Pool在方法区(元空间)

new String(“abc”)

这种方式会创建两个对象

  • "abc"属于字面量,因此会在编译期会在String Pool中创建一个字符串对象。
  • 使用new的方式会在堆中创建一个对象,该对象指向String Pool中的“abc”。

类型转换

  1. 与基本数据类型、包装类的转换

    String —>> 基本数据类型、包装类:调用包装类的静态方法:parseXxx(str)

    String s1 = "123";
    int num = Integer.parseInt(s1);
    

    基本数据类型、包装类 —>> String:调用String重载的valueOf(xxx)

    int num = 123;
    String s2 = String.valueOf(num);
    
  2. 与字符数组之间的转换

    String —>> char[]:调用String的toCharArray()

    String s1 = "abc123";
    char[] charArray = s1.toCharArray();
    

    char[] —>> String:调用String构造器

    char[] arr = new char[]{'h', 'e', 'l', 'l', 'o'};
    String str = new String(arr);
    
  3. 与字节数组之间的转换

    String —>> byte[]:调用String的getBytes()

    又称为编码

    String s1 = "abc123中国";
    byte[] bytes1 = s1.getBytes();//使用默认的字符集UTF-8,进行转换
    
    byte[] bytes2 = s1.getBytes("gbk");//指定字符集标准gbk
    

    byte[] —>> String

    又称为解码

    String s3 = new String(bytes1);//使用默认的字符集UTF-8,进行解码
    
    String s4 = new String(bytes2);//编码(gbk)与解码(UTF-8)不同,出现乱码
    
    String s5 = new String(bytes2, "gbk");//指定解码方式(gbk)
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值