首页  >  文章  >  Java  >  为何Java中String类是不可变的(详解)

为何Java中String类是不可变的(详解)

烟雨青岚
烟雨青岚转载
2020-06-24 13:27:146617浏览

为何Java中String类是不可变的(详解)

为何Java中String类是不可变的(详解)

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

String类的值是保存在value数组中的,并且是被private final修饰的

1、private修饰,表明外部的类是访问不到value的,同时子类也访问不到,当然String类不可能有子类,因为类被final修饰了
2、final修饰,表明value的引用是不会被改变的,而value只会在String的构造函数中被初始化,而且并没有其他方法可以修改value数组中的值,保证了value的引用和值都不会发生变化

所以我们说String类是不可变的。

而很多方法,如substring并不是在原来的String类上进行操作,而是生成了新的String类

public String substring(int beginIndex) {
	if (beginIndex < 0) {
		throw new StringIndexOutOfBoundsException(beginIndex);
	}
	int subLen = value.length - beginIndex;
	if (subLen < 0) {
		throw new StringIndexOutOfBoundsException(subLen);
	}
	return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);}

为什么String被设置为不可变的?

字符串常量池

Java有8种基本数据类型

整数类型:byte,short,int,long。包装类型为Byte,Short,Integer,Long
浮点类型:float、double。包装类型为Float,Double
字符类型:char。包装类型为Character
布尔类型:boolean。包装类型为Boolean

8种包装类型中除了Float,Double没有实现常量池,剩下的都实现了,当然都是通过享元模式实现的

String类的常量池是在JVM层面实现的。

为什么要有常量池?

常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。

例如字符串常量池,在编译阶段就把所有的字符串文字放到一个常量池中。

  • 节省内存空间:常量池中所有相同的字符串常量被合并,只占用一个空间。

  • 节省运行时间:比较字符串时,== 比equals()快。对于两个引用变量,只用==判断引用是否相等,也就可以判断实际值是否相等。

字符串常量池放在哪?

jdk1.7之前的不讨论,从jdk1.7开始,字符串常量池就开始放在堆中,然后本文的所有内容都是基于jdk1.8的

下面这个代码还是经常被问到的

String str1 = "abc";
String str2 = "abc";
String str3 = new String("abc");
String str4 = new String("abc");
// trueSystem.out.println(str1 == str2);
// falseSystem.out.println(str1 == str3);
// falseSystem.out.println(str3 == str4);

内存中的结构如下
在这里插入图片描述
其中常量池中存的是引用

解释一下上面代码的输出,Java中有2种创建字符串对象的方式

String str1 = "abc";
String str2 = "abc";
// trueSystem.out.println(str1 == str2);

采用字面值的方式创建一个字符串时,JVM首先会去字符串池中查找是否存在"abc"这个对象

如果不存在,则在字符串池中创建"abc"这个对象,然后将池中"abc"这个对象的地址赋给str1,这样str1会指向池中"abc"这个字符串对象

如果存在,则不创建任何对象,直接将池中"abc"这个对象的地址返回,赋给str2。因为str1、str2指向同一个字符串池中的"abc"对象,所以结果为true。

String str3 = new String("abc");
String str4 = new String("abc");
// falseSystem.out.println(str3 == str4);

采用new关键字新建一个字符串对象时,JVM首先在字符串池中查找有没有"abc"这个字符串对象,

如果没有,则首先在字符串池中创建一个"abc"字符串对象,然后再在堆中创建一个"abc"字符串对象,然后将堆中这个"abc"字符串对象的地址赋给str3

如果有,则不在池中再去创建"abc"这个对象了,直接在堆中创建一个"abc"字符串对象,然后将堆中的这个"abc"对象的地址赋给str4。这样,str4就指向了堆中创建的这个"abc"字符串对象;

因为str3和str4指向的是不同的字符串对象,结果为false。

缓存HashCode

String类在被创建的时候,hashcode就被缓存到hash成员变量中,因为String类是不可变的,所以hashcode是不会改变的。这样每次想使用hashcode的时候直接取就行了,而不用重新计算,提高了效率

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {

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

可以用作HashMap的key

由于String类不可变的特性,所以经常被用作HashMap的key,如果String类是可变的,内容改变,hashCode也会改变,当根据这个key从HashMap中取的时候有可能取不到value,或者取到错的value

线程安全

不可变对象天生就是线程安全的,这样可以避免在多线程环境下对String做同步操作。

感谢大家的阅读,希望大家收益多多。

本文转自:https://blog.csdn.net/zzti_erlie/article/details/106872673

推荐教程:《java教程

以上是为何Java中String类是不可变的(详解)的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文转载于:csdn.net。如有侵权,请联系admin@php.cn删除