您的位置 首页 java

Java经典语法:字符串是可变的吗?

Java经典语法系列

设计初衷

用过java的同学知道,大部分数据操作基本上是字符串(String)类型的。 比如我们设计表字段时,varchar类型字段居多。

设计HashMap的Key时基本上都是字符串类型。

我们知道如果字符串存储都放在堆(Heap)内存的话,频繁的字符串读写会导致GC压力巨大,程序的性能也会因此大打折扣。基于此,Java建立字符串类型的 字符串池 就势在必行了。

字符串池(String Pool)

Java虚拟机( JVM )为了提升程序的性能以及减少内存开销,避免字符串实例的重复创建,JVM会在 堆内存 中开辟了一块特殊的内存空间,即字符串池(String Pool)。

字符串池由String类私有维护。

字符串常量 (String Constant)

字符串池中的每个字符串实例只存在唯一一份。

字符串池原理

看过String源码的同学知道,String的类是final类型的。

核心V数组变量也是final类型的。

为什么要String要设计成final类型呢?我们先来看看final关键字的特性。

final关键字

由《 》文章得出final关键字的两个特性:

  1. 被final修饰的类不能被子类继承
  2. 被final修饰的字段值不可修改

而这个两个特性恰恰是设计字符串池的关键所在,也是设计字符串池三个特性的理论基础。

一、字符串的不可继承性

被final修饰的类不能被继承,即它不能拥有自己的子类,这样就防止其他类修改final类的成员变量值。

二、字符串的不可变性

我们知道被final修饰的变量是不可更改的,但其真正含义是 引用地址不能改变。

比如我们定义一个Int类型长度为3的数组,就不能再用ints变量指向一个Int类型长度为4的数组呢。

但是我们可以修改数组内部元素的值。

我们发现数组下标为2的值从2变成3,说明数组内部元素值是可以修改的。

那字符串是怎么解决这个问题的呢?

我们看看String内部源码可知: 发现String的内部成员变量都是私有类型(private)

我们知道 如果一个类的成员变量是私有的,那意味着该变量的访问权限只限于本类中,对子类和其他类不可见,这样就防止外部类修改其成员变量值

final的不可变性加上变量私有访问权限,从而保证了字符串的不可变性。

三、字符串的线程安全性

由于字符串是不可变的,这里包含两层含义:

第一,字符串常量初始化时会立即更新到主内存~ 写操作

第二,多线程从主内存读取字符串常量~ 读操作

所以我们说字符串是线程安全的。

字符串 初始化 方式

1)采用变量赋值

String str1 = "abc"
String str2 = "abc"
 

2)采用new关键字新建一个字符串对象。

String str1 = new String("abc")
String str2 = new String("abc")
 

这两种方式有什么差别呢?

编译期

采用变量赋值方式,其在编译期存储到字符串池里。所以用双引号“”赋值,str1和str2引用地址都是指向字符串常量“abc”;

str1 == str2 为true
 

运行期

采用new的方式是直接在堆内存开辟两块空间存储String对象。用str1和str2两个引用地址指向这个两块堆内存空间,所以str1 == str2为假。

str1 == str2 为 false
 

String赋值变种

1、字符串 “+” 连接符

String s = "11" + "22"
 

s创建的是字符串常量,属于编译期变量赋值方式,在字符串池值为“1122”。而像”11″,”22″,不会在字符串池创建。

2、包含变量的字符串”+” 连接符组合方式

String s1 = "11"
String s2 = "22" + s1 
 

s2 = “22” + s1创建的对象是运行期才创建的,存储在堆中。

java规定

只要s1是变量,不论s1指向池中的字符串常量还是堆中的字符串对象,运行期s1 + “22”操作实际上是编译器创建了 StringBuilder 对象进行了 append 操作后通过toString()返回了一个字符串对象存在堆上。
 

3、多变量赋值”+” 连接符组合方式

String s1 = "11" 
String s2 = "22" + s1
String s3 = "22" + s1
 

虽然s2,s3都包含变量s1并且指向s1 + “22”。但是却指向两个不同的对象,本质是s2/s3在堆上new出了两个StringBuilder对象来进行append操作。

4、final修饰的变量

final String s1 = "11"
String s2 = "22" + s1
 

s1被用final修饰,属于编译期变量赋值方式。所以“22” + s1等同于 “22” +“11”,也属于编译期变量赋值方式。

关于我

我是Wooola,擅长微服务,分布式,并发,工作流。请您多多关注我的头条号,我会持续更新干货给大家。

有需要Java资料的同学,可以关注之后私信哈,回复“资料”可以免费领取Java BAT面试宝典/微服务/SpringCloud/SpringBoot等视频和资料,亲记得要点赞转发哦!!!

文章来源:智云一二三科技

文章标题:Java经典语法:字符串是可变的吗?

文章地址:https://www.zhihuclub.com/196346.shtml

关于作者: 智云科技

热门文章

网站地图