Java基础之String

String类

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

在 Java 8 中,String 内部使用 char 数组存储数据。

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

在 Java 9 之后,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 值也不可变,因此只需要进行一次计算。

  1. String Pool的需要

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

  1. 安全性

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

  1. 线程安全

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

String对象的两种创建方式

     //第一种方式是在常量池中拿对象
     String str1 = "abcd";
	 //第二种方式是直接在堆内存空间创建一个新的对象
     String str2 = new String("abcd");
     System.out.println(str1==str2);//false

在这里插入图片描述

只要使用new方法,便需要创建新的对象。

new String(“abc”)

创建了两个字符串对象:

  • 编译时期会再String Pool中创建一个字符串对象,指向这个“abc”字符串字面量

  • 使用new会在堆中创建一个字符串对象,将String Pool中的字符串对象作为String构造函数的参数

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

          public String(String original) {
              this.value = original.value;
              this.hash = original.hash;
          }
      
  • 字符串常量"abc"在编译期就已经确定放入常量池,而 Java 堆上的"abc"是在运行期初始化阶段才确定

字符串常量池

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

主要使用方法有两种:

  • 直接使用双引号声明出来的String对象会直接存储在常量池中
  • 如果不是用双引号声明的String对象,可以使用String提供的intern方法
    • String.intern() 是一个Native方法,它的作用是:
      • 如果运行时常量池中已经包含一个等于此String对象内容的字符串,则返回常量池中该字符串的引用
      • 如果没有,则在常量池中创建与此 String 内容相同的字符串,并返回常量池中创建的字符串的引用
          String s1 = new String("计算机");
          String s2 = s1.intern();
          String s3 = "计算机";
          System.out.println(s2);//计算机
          System.out.println(s1 == s2);//false,因为一个是堆内存中的String对象一个是常量池中的String对象
          System.out.println(s3 == s2);//true,因为两个都是常量池中的String对
  • 在 Java 7 之前,String Pool 被放在运行时常量池中,它属于永久代。
  • 而在 Java 7,String Pool 被移到堆中。这是因为永久代的空间有限,在大量使用字符串的场景下会导致 OutOfMemoryError 错误。

String字符串拼接

          String str1 = "str";
          String str2 = "ing";
          String str3 = "str" + "ing";//常量池中的对象
          String str4 = str1 + str2; //在堆上创建的新的对象
          String str5 = "string";//常量池中的对象
          System.out.println(str3 == str4);//false
          System.out.println(str3 == str5);//true
          System.out.println(str4 == str5);//false

String StringBuffer StringBuilder

  • String类中使用final关键字修饰字符数组保存字符串private final char value[],所以String对象时不可变的
  • StringBuilder、StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder使用char[] value保存字符串,所以可变
    • StringBuffer对方法加了同步锁或者对调用的方法加了同步锁

参考文献:《可能是把Java内存区域讲的最清楚的一篇文章》

《技术面试必备基础知识》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值