String的那些事

253 阅读5分钟

字符串的不可变性

字符串的不可变性是指字符串一旦被创建,就会在堆上生成这个字符串的实例,并且不可被改变,任何方法都不会改变字符串本身,而只会创建一个新的字符串(包括重新赋值)。 如果我们需要一个可以修改的字符串,我们将需要使用StringBuffer 或 StringBuilder(快但线程不安全). 否则,垃圾收集会浪费大量时间,因为每次创建一个新的字符串。

String的长度限制

  • String实际存储数据的是char value[],数组的长度是int类型,最大值为2的31次方-1= 2147483647,所以String最多存储2的31次方-1个字符
  • 当字符串为常量时,会放入字符串常量池,字符串常量池对字符串的长度做了限制,最大为65535
  • 当字符串为变量时,排除内存的问题,最大值就是2的31次方-1=2147483647

JDK 6和JDK 7中substring的原理及区别

  • 都是String字符串截取的方法,入参为要截取字符串开始的下标(offset这里用X表示)和结束的下标+1(count这里用Y表示).
  • 上面我们了解到String的构造为一个装字符串的数组,开始下标和字符数量.

JDK 6 原理是 引用原来的字符串,改变String的构造,offset = offset + X , count = Y - X.

JDK 7 原理是 重新构造一个String,offset = X , count = Y - X. 以前不用的字符串得以回收.

replaceFirst、replaceAll、replace区别

  • 都是String字符串字符串替换方法,入参为 (目标字符串 ,替换后的字符串)
  • replaceFirst: 替换首次出现的目标字符串
  • replace: 替换所有目标字符串
  • replaceAll: 替换所有字符串,支持正则表达式(java解析右斜杠,正则表达式解析右斜杠)

String、StringBuilder和StingBuffer之间的区别与联系

  • 都是用来构造字符串类型对象
  • String: 不可变序列(被final修饰),字符串一旦被创建就不可变任何方法包括重新赋值都不会改变字符串本身的值,而是创建一个新的
  • StringBuffer: 可变的字符序列,线程安全
  • StringBuilder: 可变的字符序列,线程不安全
  • 执行效率: StringBuilder > StringBuffer > String

String对“+”的重载

! String s = "string"; //这种赋值方式称之为字符串字面量

  • 如果是两个字符串字面量做 + 处理,会合并成新的字符串保存在常量池中。
  • 如果其中有字符串变量做 + 处理,先是创建一个StringBuilder对象,然后调用append()进行拼接,再调toString()生成一个新的String对象,引用返回给拼接后的对象。

字符串拼接的几种方式和区别

  • +: 上面提了 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
  • concat(): 每两个字符串拼接,都会新建一个char[]接受拼接后的字符串,再生成一个新的字符串返回出去。
  • StringBuffer: 该对象调用append()进行拼接,每次执行append()都使用syncrhoized加锁,所以线程安全,最后toString()返回。
  • StringBuilder: 该对象调用append()进行拼接,线程不安全,最后toString()返回。
  • 执行效率: StringBuilder > StringBuffer > String

! 不同情况下的拼接方法选用:

  • 两个字符串字面量拼接用: +
  • 包含字符串变量多线程拼接用: StringBuffer
  • 包含字符串变量单线程拼接用: StringBuilder

String.valueOf和Integer.toString的区别

  • String.valueOf()的入参为Object,所以他可以转换所有数据类型为String,而且有效避免了空指针,传int类型进去的话,其实也是调用了Integer.toString()
  • Integer.toString()的入参只能为int类型,先将int转成Integer,再转成String
  • Integer.toString()还能用来做进制转换

switch对String的支持

  • JDK 7 switch开始支持String
  • 看编译后文件可知,原理是先将String转hashcode,再通过equals()比较hashcode来匹配 ! swith的char,byte,short,int,String,Enum匹配机制都是将目标值和匹配值转成整型(Enum是给每个实例生成一个整型的序号来匹配)

字符串池

  • 每一个字符串常量都指向字符串池中或堆内存中的一个字符串实例
  • 字符串的值是固定的,一旦被创建则不能被修改
  • 字符串常量或常量表达式中字符串都被使用String.intern(),在字符串常量池中保存了唯一实例 ! 字符串池中维护了共享的字符串对象,这些字符串不会被垃圾回收器回收

常量池(运行时常量池、Class常量池)

Class文件中出了有类,属性,方法,接口等描述信息外,还有一项是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后放到常量池中。运行时常量相对于常量而言,他具备一个重要的特性就是动态性,Java语言并不要求常量一定要在编译时期产生,运行期也可以产生新的常量,故运行期产生的常量放在运行时常量池中,这里的常量包括基本类型的包装类型和String.intern().

intern()

String.intern()方法是一种手动将字符串加入常量池中的方法,原理如下:如果在常量池中存在与调用intern()方法的字符串等值的字符串,就直接返回常量池中相应字符串的引用,否则在常量池中复制一份该字符串,并将其引用返回String.intern()方法主要适用于程序中需要保存有限个会被反复使用的值的场景,这样可以减少内存消耗,同时在进行比较操作时减少时耗,提高程序性能。