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

目录

什么是不可变

  • 一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的。不能改变状态的意思是,不能改变对象内的成员变量,包括基本数据类型的值不能改变,引用类型的变量不能指向其他的对象,引用类型指向的对象的状态也不能改变

先看一个例子

public static void main(String[] args) throws Exception {
String s=new String("jia");
String s2=s.concat("jun");
System.out.println(s);
StringBuffer sb=new StringBuffer("jia");
sb.append("jun");
System.out.println(sb);
}
输出jia和jiajun
  • 对字符串s的操作并没有改变他,而对StringBuffer sb进行apped,输出的时候却改变了,这就说明了String一个不可变性。

也许你会说这是可变的

public static void main(String[] args) {
String s1="jiajun";
String s2=s1;
s1="666";
System.out.println(s1);
}
输出:666
  • 实际上,"jiajun"字符串并没有改变,可以通过一个例子来证明
        String s3="jiajun";
System.out.println(s2==s3);
输出:true
  • 为什么会这样,因为实际上"jiajun"字符串存放在了常量池,此时s2和s3都指向了这个这个字符串,所以可以证明这个字符串是不改变的并存在的
  • 之所以会输出666,是因为此时s1指向的字符串是另一个了
  • 其实最本质的是这个改变是改变s1的引用

也许你会说这是可变的

public static void main(String[] args) {
String s1="jiajun";
s1=s1.replace("j","J");
System.out.println(s1);
s1=s1.toLowerCase();
System.out.println(s1);
}
JiaJun
jiajun
  • 实际上jiajun字符串还是没有改变的,看一下方法的源码
2047    public String More ...replace(char oldChar, char newChar) {
2048 if (oldChar != newChar) {
...
2069 return new String(0, len, buf);
2070 }
2071 }
2072 return this;
2073 }
  • 可以看到返回的时候是创建一个新的字符串
  • 实际上String的一些方法substring, replace, replaceAll, toLowerCase,返回的时候是创建一个新的String

分析源码

111 public final class String
112 implements java.io.Serializable, Comparable<String>, CharSequence { The value is used for character storage.
113
114 private final char value[]; Cache the hash code for the string
116
117 private int hash; // Default to 0
118 private static final long serialVersionUID = -6849794470754667710L; 136
137 public String() {
138 this.value = new char[0];
139 }
151 public String(String original) {
152 this.value = original.value;
153 this.hash = original.hash;
154 }
1913 public String substring(int beginIndex) {
1914 if (beginIndex < 0) {
1915 throw new StringIndexOutOfBoundsException(beginIndex);
1916 }
1917 int subLen = value.length - beginIndex;
1918 if (subLen < 0) {
1919 throw new StringIndexOutOfBoundsException(subLen);
1920 }
1921 return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
1922 }
  • 111行可以看到,String类是用final修饰的,说明这个类是无法被继承的
  • 114行可以String类里面维护一个value的char数组,这个数组是用final修饰的,说明这个value不能指向别的数组,但是并不说明这个value数组的内容不可变,而这个value是用private修饰的,说明只有在类里面可以修改访问他,在外部不能改变他,这是关键
  • 从1913行可以看到substring方法实际上返回的数组是新创建的数组

怎么实现不可变

  • String里面维护的value数组是用private final修饰的,无法改变引用,也无法访问这个数组修改数组的值,最关键的是private
  • 对Sting的操作,并没有修改数组的值,而是创建新的String
  • 类用final修饰,方法无法被子类重写,避免被其他人破坏

不可变的好处

  • 节省空间,大量使用相同的字符串,同时指向常量池的字符串就行,如果字符串是可变的话,那么常量池就没意义了
String s1="jiajun";
String s2="jiajun";
System.out.println(s1==s2);
  • 线程安全,出现线程安全的是在对共享变量写的时候,而因为不可变,所以Strig是线程安全的

  • 最重要的是安全,如果当一个String已经传给别人了,这个时候如果是可变,那么可以在后面进行修改,那么这是麻烦并不安全的。而且在hashmap中,如果作为key的String s1是可变的,那么这样是很危险的,比如说可能出现两个同样的键。

真的不可变吗

public static void main(String[] args) throws Exception {
String s1="jiajun";
Field field=String.class.getDeclaredField("value");
field.setAccessible(true);
char [] value=(char[])field.get(s1);
value[0]='Jiajun';
  • 实际上,通过反射可以修改value数组

为什么设置为不可变

  • 调用其他方法,比如调用一些系统级操作之前,可能会有一系列校验,如果是可变类的话,可能在你校验过后,其内部的值被改变了,可能引起严重的系统崩溃问题
  • 当你在传参的时候,使用不可变类不需要去考虑谁可能会修改其内部的值

我觉得分享是一种精神,分享是我的乐趣所在,不是说我觉得我讲得一定是对的,我讲得可能很多是不对的,但是我希望我讲的东西是我人生的体验和思考,是给很多人反思,也许给你一秒钟、半秒钟,哪怕说一句话有点道理,引发自己内心的感触,这就是我最大的价值。(这是我喜欢的一句话,也是我写博客的初衷)

作者:jiajun 出处: http://www.cnblogs.com/-new/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果觉得还有帮助的话,可以点一下右下角的【推荐】,希望能够持续的为大家带来好的技术文章!想跟我一起进步么?那就【关注】我吧。

java基础解析系列(九)---String不可变性分析的更多相关文章

  1. java基础解析系列(一)---String、StringBuffer、StringBuilder

    java基础解析系列(一)---String.StringBuffer.StringBuilder 前言:本系列的主题是平时容易疏忽的知识点,只有基础扎实,在编码的时候才能更注重规范和性能,在出现bu ...

  2. java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现

    java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析 ...

  3. java基础解析系列(五)---HashMap并发下的问题以及HashTable和CurrentHashMap的区别

    java基础解析系列(五)---HashMap并发下的问题以及HashTable和CurrentHashMap的区别 目录 java基础解析系列(一)---String.StringBuffer.St ...

  4. java基础解析系列(六)---深入注解原理及使用

    java基础解析系列(六)---注解原理及使用 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---Integer ja ...

  5. java基础解析系列(七)---ThreadLocal原理分析

    java基础解析系列(七)---ThreadLocal原理分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)-- ...

  6. java基础解析系列(八)---fail-fast机制及CopyOnWriteArrayList的原理

    fail-fast机制及CopyOnWriteArrayList的原理 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列( ...

  7. java基础解析系列(十)---ArrayList和LinkedList源码及使用分析

    java基础解析系列(十)---ArrayList和LinkedList源码及使用分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder jav ...

  8. java基础解析系列(十一)---equals、==和hashcode方法

    java基础解析系列(十一)---equals.==和hashcode方法 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系 ...

  9. java基础解析系列(二)---Integer

    java基础解析系列(二)---Integer 前言:本系列的主题是平时容易疏忽的知识点,只有基础扎实,在编码的时候才能更注重规范和性能,在出现bug的时候,才能处理更加从容. 目录 java基础解析 ...

随机推荐

  1. 实现一个简单的虚拟DOM

    现在的流行框架,无论React还是Vue,都采用虚拟DOM. 好处就是,当我们数据变化时,无需像Backbone那样整体重新渲染,而是局部刷新变化部分,如下组件模版: <ul class=&qu ...

  2. 网页端HTML使用MQTTJs订阅RabbitMQ数据

    最近在做一个公司的日志组件时有一个问题难住了我.今天问题终于解决了.由于在解决问题中,在网上也查了很多资料都没有一个完整的实例可以参考.所以本着无私分享的目的记录一下完整的解决过程和实例. 需求:做一 ...

  3. 九月 26, 2017 10:18:14 上午 com.sun.jersey.server.impl.application.RootResourceUriRules <init> 严重: The ResourceConfig instance does not contain any root resource classes.

    Tomcat启动错误:九月 26, 2017 10:18:14 上午 com.sun.jersey.server.impl.application.RootResourceUriRules <i ...

  4. Opengl4.5 中文手册—A

    因为opengl API 比较庞大,网络上还没有完整的.较新的opengl中文手册 这对很多人很不方便,所以整理了这一系列,用于帮助大家"快速浏览最新的opengl api" 为了 ...

  5. JavaScript简易计算器

    JavaScript一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言,内置支持类型.它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在HTML(标 ...

  6. TCP/IP(五)传输层(TCP的三次握手和四次挥手)

    前言 这一篇我将介绍的是大家面试经常被会问到的,三次握手四次挥手的过程.以前我听到这个是什么意思呀?听的我一脸蒙逼,但是学习之后就原来就那么回事! 一.运输层概述 1.1.运输层简介 这一层的功能也挺 ...

  7. 修改Form ->Top和Left 造成的莫名其妙的显示异常 “轴信息”

    相关代码: 运行程序: 要等待很久,或者把主窗体最小化,再最大化打开"轴信息" 才会恢复正常. 这个"不爽"很蛋蛋 ,网友亲亲们,有独到见解的亲亲们,期待得到你 ...

  8. U盘中毒无限蓝屏重启的解决办法

    开门见山,这个帖子只针对U盘中毒导致的以下两种症状: 1.win10系统无法进入并且要求初始化,卸载所有第三方应用 2.win7系统无限蓝屏重启): 其他的硬件故障不在本次讨论范围之内. 说明以下.上 ...

  9. Python实战之文件操作的详细简单练习

    ['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__' ...

  10. Spring ——简介及环境搭建跑通Hello

    Spring Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用.是为了解决企业应用程序开发复杂性而创建的.框架的主要优 ...