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


在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. 【JSOI2007】【Bzoj1029】建筑抢修

    贪心... 按照T2来进行排序,用堆来进行维护.循环一遍,如果循环时间加上已用时间不超过截止时间,那就ANS++.否则,将它与堆顶判断,如果小于堆顶就把堆顶踢出,把它加入. #include<c ...

  2. HTML5- Canvas入门(四)

    前几章我们学习了矩形.多边形.圆形.曲线等图形的绘制,今天来学习下更简单一些的文本绘制及其各种功能方法. 在canvas中我们可以通过 strokeText() 和 fillText() 来绘制描边文 ...

  3. [.net 面向对象程序设计进阶] (20) 反射(Reflection)(上)利用反射技术实现动态编程

    [.net 面向对象程序设计进阶] (20) 反射(Reflection)(上)利用反射技术实现动态编程 本节导读:本节主要介绍什么是.NET反射特性,.NET反射能为我们做些什么,最后介绍几种常用的 ...

  4. 跟vczh看实例学编译原理——三:Tinymoe与无歧义语法分析

    文章中引用的代码均来自https://github.com/vczh/tinymoe.   看了前面的三篇文章,大家应该基本对Tinymoe的代码有一个初步的感觉了.在正确分析"print ...

  5. Atitit 边缘检测原理attilax总结

    Atitit 边缘检测原理attilax总结 1. 边缘检测的概念1 1.1. 边缘检测的用途1 2. 边缘检测方法分类1 3. 边缘检测的基本方法2 3.1. Roberts边缘检测算子2 3.2. ...

  6. Base64编码原理分析

    Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一,在了解Base64编码之前,先了解几个基本概念:位.字节. 位:"位(bit)"是计算机中最小的数据单位.每一位 ...

  7. 工作任务:模拟淘宝登录和购物车功能:使用cookie记录登录名,下次登录时能够记得上次的登录名,使用cookie模拟购物车功能,使用session记住登录信息并验证是否登录,防止利用url打开网站,并实现退出登录功能

    登入界面<% Cookie[] cks =request.getCookies(); String str=null; for(Cookie ck:cks) { if(ck.getName(). ...

  8. SQL Server DDL触发器运用

    一.本文所涉及的内容(Contents) 本文所涉及的内容(Contents) 背景(Contexts) 基础知识(Rudimentary Knowledge) DDL运用场景(DDL Scene) ...

  9. 【Win10应用开发】自适应磁贴中的分组

    老周在上一篇文章中介绍过了自适应磁贴的基本结构,以及给大家演示了一些例子. 本文老周给大伙伴们说说自适应磁贴的另一个特点——分组呈现. 当磁贴的内容被分组后,每个组中的内容就会被视为一个整体.比如某磁 ...

  10. hudson部署过程

    hudson部署过程: java安装 http://developer.51cto.com/art/201504/470683.htm tomcat安装 http://blog.csdn.net/hu ...