前言:java.lang.String类在日常编码中使用的非常频繁,可是对于String类的了解,真的很深入吗?有没有正确的使用String?
简介:String代表的是字符串常量,他们的值在创建之后是不能改变的,所以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 /** use serialVersionUID from JDK 1.0.2 for interoperability */ private static final long serialVersionUID = -6849794470754667710L;
可见String类继承了序列化,所以String类是可以被序列化的,另外,他的底层组成其实是个char 类型的数组,并且是final 类型的,这也进一步解释了为什么String的值一旦创建了之后是不能被修改的,String类中所有方法返回的都是一个新的String对象
字符串创建的两种方式:
1. 直接赋值的方式:String str = "test"; //保存在jvm内存方法区中的字符串常量
2. 通过构造函数方式创建字符串对象 String str = new String("test"); //保存在jvm内存堆中的字符串对象
现在来分析一下,上图中的运行结果:
str1 == str2的结果为false的原因是:str1 和 str2各自在jvm堆内存中开辟了一块内存空间,而==比较的是内存地址,所以结果是false
str2 == str3的结果为true的原因是:将str3在内存中的地址直接指向了str2,其实str2和str3在内存中的地址是同一个,所以返回结果是true
str1 == str4的结果为true的原因是:str1是通过字符序列的方式创建对象,会将数据存储在字符串常量池中,之后再通过这种方式创建字符串的时候,会首先去常量池中查看是否已经存在这个字符串,如果存在的话,直接引用,没有的话,会创建一个。
str4 == str2的结果为false的原因是:按照上面的讲解,其实比较过程是str1 == str2,所以返回false
下面从内存的角度来分析一下字符串常量池的问题:
jvm内存中有一块区域,叫做方法区,用于存储类加载的信息,方法体和各种符号表
方法区中有一块区域,称为常量池,用于存储编译时和运行时的字符串常量
jvm中还有堆区域和栈区域,堆区域主要存放实例化的对象,栈区域主要存放实例的引用。
具体的关于jvm的内存模型,可以查看我的另外一篇文章,详解jvm 内存模型。
前面说到,字符串是不可变的,下面介绍一下,String 不可变所带来的好处:
1. 字符串常量池的需要
通过直接赋值的方式创建的多个相同的字符串,将会指向堆内存中同一个地址
2. 容许String对象将hash码缓存起来,下次就不需要重新计算,如果String对象具有可变性,那么hash码也是可变的
java中的String的hash码在其他地方经常被使用到,例如HashMap中的槽的定位。
3. 安全性
如果String对象是可变的,那么将会有很多未知严重bug产生,例如jdbc配置文件中的username和password,改变字符串指向的对象的值,造成安全漏洞