Skip to content

Java基础——String

更新: 5/9/2025 字数: 0 字 时长: 0 分钟

String是Java基础类型中较为特殊的一个类,我们这里着重探讨一下这个类。

String

String底层使用一个char类型的数组来存储字符串,且该数组使用了private和final修饰

为什么说String是不可变的

  1. final与private修饰该字符数组且未暴露修改方法,杜绝了从外部修改该对象的可能性
  2. 由于final的使用导致子类无法继承该对象,进而导致子类不可能修改该数组

因而String是不可变的

线程安全性

由于不可变,进而可以理解为常量,因此所以线程安全

StringBuffer与StringBuilder

StringBuffer与StringBuilder均继承自AbstractStringBuilder(构造器模式),因此我们先来介绍一下AbstractStringBuilder类。

AbstractStringBuilder类中使用一个char类型的数组来存储字符串,同时为我们提供了大量字符串修改操作(insert,append....)

线程安全性

  • StringBuffer使用了锁机制,因此线程安全。
  • StringBuilder由于未使用锁机制导致线程不安全。

性能

StringBuilder的效率高于StringBuffer。

比较String,StringBuilder,StringBuffer

  • 常量使用String
  • 修改+多线程使用StringBuffer
  • 修改+单线程使用StringBuilder

Java9的改动

Java9将上面三个类的底层char数组改成了byte数组

修改的底层

String的+与+=是Java唯二两个重载的运算符,我们通过观察字节码可以发现String的+和+=是创建了一个StringBuilder类来完成这一操作。

但是在for循环中,字节码无法优化成自创建一个StringBuilder类(即for每进行一次循环就要新创建一个StringBuilder),因此我们仍然建议存在大量修改的情况下使用StringBuilder或StringBuffer

字符串常量池

JVM为了提升性能减少内存消耗而专门为字符串开辟了一块区域,我们称呼为字符串常量池。

  • 直接使用双引号声明出来的String对象会直接存储在常量池中。
  • 如果不是用双引号声明的String对象,可以使用String提供的intern方法。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中
java
String s1 = "Java";
String s2 = "Java";

s1赋值时,JVM会先看常量池中是否有这个对象,如果没有则创建该对象在常量池中,并返回常量池中的这个对象。

s2赋值时,JVM发现常量池中已经有这个对象,因此直接常量池中这个对象的引用赋值给s2。

因此s1==s2

构造方法和等于赋值的区别

使用构造方法创建字符串变量时,一般不会和常量池有关系。

但特别的,如果使用了以下形式创建字符串

java
String a=new String("123");

由于使用了双引号变量,因此会使用常量池。

一:

  • 若双引号变量不存在,则会创建该变量到常量池中并返回他的引用
  • 若双引号变量存在,则直接返回该变量的引用

二:

使用从常量池中得到的引用去初始化一个新的对象并放到堆中(浅拷贝)

intern方法

intern方法的意义是通过调用该方法的String变量的值去获取常量池中该与该变量值相同的对象并将其引用返回。

这又分为以下两种情况

  • 若常量池中没有该与该变量值相同的对象,则将该变量丢入常量池中,并返回该变量的引用(与直接使用等于逻辑一致)
  • 若常量池中存在与该变量值相同的对象,则直接返回常量池中对象的引用

注意:使用intern的过程并不会改变调用intern方法的对象

intern的意义

说了这么多,我们究竟会在什么场景使用intern呢?

我们这里带来一段fastjson的源码

java
public Entry(char[] ch, int offset, int length, int hash, Entry next){
    characters = new char[length];
    System.arraycopy(ch, offset, characters, 0, length);
    symbol = new String(characters).intern(); //核心
    this.next = next;
    this.hashCode = hash;
    this.bytes = null;
}

通过使用intern方法,将字符串缓存到了字符串常量池中,同时给到变量的是常量池中的对象而不是构造方法创建的对象,

这样一来后续凡是使用到逻辑相同的变量均是常量池中的同一变量,因此加快了速度。

本站访客数 人次      本站总访问量