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

目录

什么是不可变

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

先看一个例子

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

也许你会说这是可变的

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

也许你会说这是可变的

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

分析源码

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

怎么实现不可变

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

不可变的好处

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

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

真的不可变吗

  1. public static void main(String[] args) throws Exception {
  2. String s1="jiajun";
  3. Field field=String.class.getDeclaredField("value");
  4. field.setAccessible(true);
  5. char [] value=(char[])field.get(s1);
  6. 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. STS安装

    在eclipse中安装spring tool Suite插件需要根据eclipse版本找到对应的spring tool Suite安装包. spring tool Suite 官网地址:http:// ...

  2. ng-file-upload(在单文件选择,并且通过点击“上传”按钮上传文件的情况下,如何在真正选择文件之前保留上一文件信息?)

    文章前面研究ng-file-upload可能涉及指令: You can use ng-model or ngf-change instead of specifying function for ng ...

  3. Linux Expect自动化交互脚本简介

    相关资料 维基百科:Expect SourceForge:The Expect Home Page TCL脚本言语简介 由于Expect是建立在TCL语言基础上的一个工具,因此首先检查一些TCL常见语 ...

  4. Sql Server——数据的增删改

    所谓数据的增删改就是在创建好数据库和表后向表中添加数据.删除表中的数据.更改表中的一些数据. 新增数据: 语法一: insert into 表名 values (数据内容)        --这里需要 ...

  5. 国外支付PayPal

    PayPal官网https://www.paypal.com/ PayPal沙箱https://www.sandbox.paypal.com/signin?country.x=US&local ...

  6. 第6章 Overlapped I/O, 在你身后变戏法 ---被激发的 Event 对象 -4

    以文件 handle 作为激发机制,有一个明显的限制,那就是没办法说出到底是哪一个 overlapped 操作完成了.如果每个文件 handle 只有一个操作等待决定,上述问题其实并不成为问题.但是如 ...

  7. BCB中AnsiString类方法小结

    AnsiString类是BCB中最常见类之一,了解它对以后深入学习BCB大有帮助. 介绍AnsiString类之前,先要介绍一些背景知识.VCL(Visual Component Library 可视 ...

  8. java中需要注意的小细节

    很早以前就打算写博客,但是总是因为不知道写什么,或是觉得博客里其他人已经把我要写的整理很好了而迟迟没有动笔,现在决定把自己平时的记录的一些笔记拿出来,希望大神们可以批评指导,并且希望能够帮助一些刚刚入 ...

  9. 详解m4文件

    最近在分析speex代码,发现编译过程中需要的一个speex.m4文件不知道是何方神圣,怀着对未知知识的渴望,跑到 某哥和某基问了一下,算是认识了,为了方便以后经常见面,这里就做个记录吧. M4实际上 ...

  10. git 忽略文件夹

    $ vim .gitignore 添加要忽略的文件或文件夹 esc + :wq 退出vim命令行