在所有编程语言领域,我想字符串应该是地球上最常用的表达手段了吧。


在java的世界里,String是作为类出现的,核心的一个域就是一个char数组,内部就是通过维护一个不可变的char数组,来向外部输出的。

这是jdk一段String类定义,首先类是final,表明类不可被继承;核心域是private final的,final表明这个引用所指向的内存地址不会改变,但这还不足说明value[]是不可变的;因为引用所指向的内存的值有可能发生变化,但是jdk是不会让这样的事情发生的。private 保证这个域对外部来说是不可见的,这还不够,对value还要进行 保护性拷贝 。

举一个简单的例子:

这是一个String的构造函数,参数是一个char数组引用,它并没有把这个数组引用直接赋值给实例对象的value成员变量,而是通过一个Arrays.copyOf的方式拷贝一个数组再给到对象的成员变量。为什么呢?假设它这里是直接一个赋值,那String的不可变性就彻底被破坏了,因为如此一来,存在一个外部引用与实例对象value引用指向相同的内存地址,通过外部引用就可以改变这个char数组对象,最终导致的结果就是String不再不可变。幸好JDK中所有的对value的操作都是保护性拷贝操作,不管是被赋值,还是赋值给其它外部引用。

说了这么多,为什么JAVA要String保持一个不可变的状态呢?原因其实很简单,因为String太太太太常用了,地球上没有能比这个更常用的对象了,设计成不可变的,是为了减少大量的同步锁的开销。但是要注意 并不是声明成final的类一定是不可变的 。

根据effective java一书中提到,类不变需遵循五条规则:

1.不提供任何机会修改对象状态的方法

2.保证类不被扩展

3.所有域都是final

4.所有域都是私有的

5.确保对于任何可变组件的互斥访问

有兴趣的同学可以参考 effective 第十五条,这里就不展开讲了。作了那么久的铺垫,接下来可以谈谈avoid getfield opcode了,按翻译来说就是防止"调用访问域的操作码",这段tip来自一段注释。

十分常用的replace方法,内部算法大概是这样一个过程:先找到第一个oldChar的下标i,拷贝小标i之前的旧数组的内容到新的数组,新数组[i]='newChar',遍历i之后的内容,若旧数组出现为oldChar则在新数组中替换为newChar,若没有出现,则拷贝旧值到新数组。

起初我很奇怪,到底为什么,一定要找到第一个出现oldChar的下标,为什么不直接遍历数组中每一个char 若为旧值,替换为新值。我从时间复杂度,空间复杂度去考虑这个算法,始终没有得到结果。我还是太年轻啊,后来才发觉其实还是为了维护一个String的设计原则:"对于拥有相同的字符字面量的情况下,String的构造还是优先返回原字符串对象"。这么做应该是为了解决堆内存吧。

那么,这一句注释到底是什么意思呢?要理解这句话,需要对JVM有一定的了解。

JVM在运行中的数据区,分为五个部分:方法区,堆区,虚拟机栈,本地方法栈,程序计数器。

首先类相关的信息肯定是放在方法区的,堆中放一些实例对象,程序计数器始终指向下一条将要执行的指令,虚拟机栈和本地方法栈分别是用来于普通方法和本地方法的。

着重说一下虚拟机栈,它是线程私有的,描述的是java方法执行的内存模型:每个方法在执行的同时创建一个栈帧,用于存放局部变量表,操作数栈,方法入口,动态链接等。

局部变量表用来存放一些基本数据类,和引用。操作数栈的话,是用来作运算用的,打个比方

int a=1;
int b =2;
int c =a+b;

JVM会先把a,b的值压入到操作数栈保存起来,等到程序计数器执行加法指令的时候,再把a,b从栈中pop出来。重点就在于a,b值是从哪里压入到栈中的,如果没有那么接下来要遍历的就是value数组了,value毫无疑问是堆中的数据,也就是每一次遍历会经历,数据由堆中取出,进入栈帧,再由栈帧压入到操作数栈,最后pop出来进行运算。

如果执行了上述代码,情况就大不相同了,val就是一个局部变量,它会被存放在局部变量表中,接来下的运算,就是局部变量表到操作数栈了,属于一个栈帧内的数据转移。JVM为了避免频繁进行堆栈数据转移,将值复制到本地变量一次,以避免在接下来的几行中循环的每一次迭代,从堆中多次取下的字段值。

除了我提到的这些,String源码中还有许多值得学习的算法,设计方式,代码优化,比如,还有常量池的实现。

最后我想求教一个问题啊,在JDK7点版本中,String引入了一段静态代码块,

我甚是不解啊,不知道有没有大神帮我解读一下这段代码的含义?

浅析String不可变性的更多相关文章

  1. 浅析String

    浅析String String的设计结构: 首先我们看一下 String的源码 public final class String     implements java.io.Serializabl ...

  2. java基础解析系列(九)---String不可变性分析

    java基础解析系列(九)---String不可变性分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---In ...

  3. 记一次愚蠢的经历--String不可变性

    前言 只有光头才能变强. 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y 记录一次在写代码时愚蠢的操作,本文涉及到的知识点:S ...

  4. 浅析String、StringBuilder、StringBuffer

    谈谈我对 String.StringBuilder.StringBuffer 的理解 StringBuilder.StringBuffer 和 String 一样,都是用于存储字符串的. 1.那既然有 ...

  5. 一、图解Java中String不可变性

    这里有一堆例子来说明Java的String的不可变性. 1.声明一个String String s = "abcd"; s 变量保存string对象的引用,下面的箭头解释成保存了哪 ...

  6. Java基础:String不可变性和final修饰

    转载请注明出处: jiq•钦's technical Blog - 季义钦 String的不可变性 Java规定String是不可变的(immutable).事实上这个不可变具备两层含义: 1 内容不 ...

  7. String不可变性

    今天分析一下String,String有很多实用的特性,比如说“不可变性”,是工程师精心设计的艺术品.用final就是拒绝继承,防止内部属性或方法被破坏. 一,什么是不可变? String不可变很简单 ...

  8. 浅析String、StringBuffer、StringBuilder的区别以及性能区别

    前奏: 比较三者之间的区别在与区别他们做相同的事情的时候的区别,那就是在我们常见的拼接字符串的时候,StringBuffer.StringBuilder调用的是appende()方法,而String很 ...

  9. 浅析String类

    这是对于String类的一些总结,我将会从几个方面并且结合着字符串池等相关知识进行总结 进程如下:                1.对于String类基本知识的一些总结 2.简要介绍字符串池 3.分 ...

随机推荐

  1. 如何识别一个字符串是否Json格式

    前言: 距离上一篇文章,又过去一个多月了,近些时间,工作依旧很忙碌,除了管理方面的事,代码方面主要折腾三个事: 1:开发框架(一整套基于配置型的开发体系框架) 2:CYQ.Data 数据层框架(持续的 ...

  2. MongoDB 2.6.2 发布

    NoSQL数据库MongoDB推出了全新一代产品MongoDB 2.6.2,该版本全面强化核心服务器,提供全新的自动化工具与重要的企业功能,宣称是MongoDB问世5年来最大的一次版本发布,主要改善开 ...

  3. .NET WEB程序员需要掌握的技能

    本来这个是我给我们公司入职的新人做一个参考,由于 @张善友 老师在他的微信号转了我的这篇文章<<.Net WEB 程序员需要掌握的技能>>,很多人觉得比较有用,说是看了后知道一 ...

  4. 让linux好用起来--操作使用技巧

    让linux好用起来--操作使用技巧 1   概述 在一个初学者眼里,linux的 CLI 界面没有图形界面那样多彩和友好,会让人产生畏难心理,但是作为一个稍微进阶的linux玩家,自然会积累不少经验 ...

  5. 剑指Offer面试题:9.二进制中1的个数

    一.题目:二进制中1的个数 题目:请实现一个函数,输入一个整数,输出该数二进制表示中1的个数.例如把9表示成二进制是1001,有2位是1.因此如果输入9,该函数输出2. 二.可能引起死循环的解法 一个 ...

  6. [Voice communications] 声音的滤波

    本系列文章主要是介绍 Web Audio API 的相关知识,以及 web语音通信 中会遇到的一些问题,阐述可能存在错误,还请多多斧正! 通过设备获取音频流会不可避免的渗入一些杂音,这些杂音可能来自你 ...

  7. linux添加自定义的命令!

    修改了/root/下的.bashrc -bash-4.1# vi .bashrc # .bashrc # User specific aliases and functions alias rm='r ...

  8. php语言

    <?php//单行注释/*多行注释*///弱类型语言//var a=10;//php定义变量/*$a =10; //变量名前加$$b="hello";var_dump($a) ...

  9. Vue脚手架工具vue-cli和调试组件vue-devtools

    https://github.com/vuejs/vue-cli npm install vue-cli -g vue init webpack my-project cd my-project // ...

  10. java轻量级IOC框架Guice

    Google-Guice入门介绍(较为清晰的说明了流程):http://blog.csdn.net/derekjiang/article/details/7231490 使用Guice,需要添加第三方 ...