gaarakseven

导航

java 之 String 类浅析

String

以下内容基于 jdk8 分析

String 对象类定义

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage.真正存储数据的结构 */
private final char value[];

/** Cache the hash code for the string */
private int hash; // Default to 0
......
}

String底层存储方式

使用 private final 修饰的char[] 数组存储

String创建过程

  • 创建方式一
String aString = "abc";

  • 创建方式二
String aString = new String("abc");

如何保证 String 对象的不可变性

通过以下两点:

  • final 修饰保证了 String 类不能被继承, 排除通过继承实现父类变量修改的可能性;
  • 同时, 将真正存储字符串信息的字符数组以及声明为 private final, 保证新建 String 对象后不能直接操作 String 实例的内容.

保证了 String 类一经实例化就不可变(不可变就是第二次给一个 String 实例赋新内容的的时候,不是在原内存地址上修改数据,而是重新指向一个新对象).

String 对象不可变的意义

代码运行效率提升

一旦保证了 String 实例的不可变, 就能使用字符串常量池, 达到减少创建字符串带来的系统开销. 具体来说就是:

  • 在一个 java 程序中,字符串的定义很频繁, 如果对于相同字符串内容的多个不同变量的定义,都在堆中开辟空间存储对象, 会很浪费时间和空间; 如果让这些 String 变量定义最终都直接或间接引用字符串常量池内同一个地址的字符串内容, 都不需要重新在怼中开辟空间存储数据, 就能减少时间和空间浪费.
  • 另外, 由于不可变, 故可以保证 String 实例的 hashCode 一直不变, 配合基于 hashcode 散列的集合使用时不需要重新计算 hashCode,提升这些集合的运行效率。
// hashCode 生成方式
public int hashCode() {    
    int h = hash;    
    if (h == 0 && value.length > 0) {        
        char val[] = value;        
        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];        
        }        
        hash = h;    
    }   
    return h;
}

安全性

  • 由于 String 实例的不可变(不可写), 故其在多个线程中能自由共享, 无需考虑线程安全问题.
  • String 被广泛用作许多java类的参数,例如网络连接、打开文件等。如果对 string 的某一处改变一不小心就影响了该变量所有引用的表现,则连接或文件将被更改,这可能导致严重的安全威胁。

关于字符串常量池

在 JDK6.0 及以前版本, 字符串常量池存放在方法区中, 在 JDK7.0 - 11, 字符串常量池被移到堆中.

写在最后

文章内容皆是基于笔者个人理解,欢迎读者在评论区留言指出不对的地方。

posted on 2021-02-24 17:25  gaarakseven  阅读(42)  评论(0编辑  收藏  举报