JAVA String 学习

String 基本学习

1. String在 jdk1.8的源码:

String 被声明为 final,它不可被继承 (Integer 等包装类也不能被继承)。在 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[];

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

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;

    
    private static final ObjectStreamField[] serialPersistentFields =
        new ObjectStreamField[0];

    /**
     * Initializes a newly created {@code String} object so that it represents
     * an empty character sequence.  Note that use of this constructor is
     * unnecessary since Strings are immutable.
     */
    public String() {
        this.value = "".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 不可变。 语法需要注意 final修饰的对象属性,必须在构造函数中初始化!,如下:

public class Main {
    public final int[] a;		//final修饰的对象属性
    public Main(int[] b){
        a=b;					//必须在构造函数中初始化,不然报错。
    }
}

不可变的好处

1). 可以缓存 hash 值

因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。
不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。

2). String Pool 的需要

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

3). 线程安全

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

2. String:字符串常量池

字符串常量池的设计思想:

1.字符串和其他的对象一样,创建耗费高昂的时间与空间代价,作为最基础的数据类型,大量频繁的创建字符串,极大程度地影响程序的性能。
2.JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化
 	为字符串开辟一个字符串常量池,类似于缓存区
    创建字符串常量时,首先坚持字符串常量池是否存在该字符串
	存在该字符串,返回引用实例,不存在,实例化该字符串并放入池中

实现的基础:

1.实现该优化的基础是因为字符串是不可变的,可以不用担心数据冲突进行共享
2.运行时全局字符串常量池中有一个表,总是为池中每个唯一的字符串对象维护一个引用,这就意味着它们一直引用着字符串常量池中的对象,
  所以,在常量池中的这些字符串不会被垃圾收集器回收

众所周知,JDK1.8版本中,String常量池已经从方法区中的运行时常量池分离到堆中了,那么在堆中的String常量池里存的是String对象还是引用呢?
在这里插入图片描述
翻译:String类的 intern()方法: 返回常量池中的String对象。它由类String独自维护。当调用 intern方法时,如果池已经包含一个等于此 String对象的字符串(用equals(oject)方法确定),则返回池中的字符串对象。否则,将此String对象添加到池中,并返回此String对象(注意是常量池中的对象,不是堆中的对象)的引用。 对于任意两个字符串s和t,当且仅当s.equals(t)为true时,s.intern() == t.intern()才为true。所有字面值字符串和字符串赋值常量表达式都使用 intern方法进行操作。

总结:JDK1.8版本的字符串常量池中存的是字符串对象,以及字符串常量值。

String s1 = "abc";
String s2 = "abc";
System.out.println(s1==s2);  true

上面创建了1个对象。采用字面值的方式创建一个字符串时,JVM首先会去字符串池中查找是否存在"abc"这个对象,如果不存在,则在字符串池中创建"abc"这个对象,然后将池中"abc"这个对象的引用地址返回给"abc"对象的引用s1,而s2只是获取了引用。

String s3 = new String("xyz");
String s4 = new String("xyz");
System.out.println(s3==s4);    false

String s5 = s1.intern();
String s6 = s2.intern();
System.out.println(s3 == s4);    true  //s5 、s6都指向常量池中的"xyz"对象

上面创建了3个对象。采用new关键字新建一个字符串对象时,JVM首先在字符串池中查找有没有"xyz"这个字符串对象,如果有,则不在池中再去创建"xyz"这个对象了,直接在堆中创建一个"xyz"字符串对象,然后将堆中的这个"xyz"对象的字符数组地址返回赋给引用s3的字符数组,这样,s3就指向了堆中创建的这个"xyz"字符串对象;如果没有,则首先在字符串池中创建一个"xyz"字符串对象,然后再在堆中创建一个"xyz"字符串对象,这样,s3指向了堆中创建的这个"xyz"字符串对象。s4则指向了堆中创建的另一个"xyz"字符串对象。s3 、s4是两个指向不同对象的引用,结果当然是false。

3. StringBuffer and StringBuilder

1). 线程安全

  • String 不可变,因此是线程安全的
  • StringBuilder 不是线程安全的
  • StringBuffer 是线程安全的,内部使用 synchronized 进行同步
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值