做Java开发的都知道,String具有不可变性,这个不可变性,怎么理解?因为用String时大家都有一个常识性的认识,那就是String初始化后,我是可以对其重新赋值的,比如:

String str1 = "123";
str1 = "1234";

明明是可以“变”的啊,什么叫不可变?下面我们来分析一下:

一、首先我们来分析下什么是String的不可变性

我们来看看String类的源代码:

public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence {/** The value is used for character storage. */private final char value[];

从源码中我们可以看到3个信息:

1、String 类是final的,其不可被继承

2、value[]数组是私有的,且是final的,其引用指向的对象不可被修改

3、String类的方法中,没有任何方法对value[]数组做修改

以上3个信息,能得出以下结论:

String对象一旦被初始化,其value[]数组的值就不会被修改了,没有子类,也没有成员方法可以修改其值。这就是String对象的不可变性。

二、不可变性的作用

不可变性保证了String对象的线程安全,所以在高并发多线程情况下可以放心使用String对象。

三、对String值“可变”这一常识的解释

有人问了,那这段代码代表什么?

String str1 = "123";
str1 = "1234";

我们用一代测试代码来分析一下:

System.out.println("***************************************");String str1 = "123";
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
char[] value = (char[]) field.get(str1);
value[0] = '0';System.out.println("str1========>>>>"+str1);String str2 = "123";
System.out.println("str2========>>>>"+str2);String str3="023";
System.out.println("023"==str1);
System.out.println("023".equals(str1));System.out.println("123" == str1);
System.out.println("123".equals(str1));System.out.println("***************************************");

运行结果是:

***************************************
str1========>>>>023
str2========>>>>023
false
true
true
true
***************************************

这是不是很违反我们的常识?

1、str1已经被重新赋值为“023”,而str2是被赋值为“123"而且没有被改过,为什么str2的值是023?

2、str3被初始化为"023”,为什么用“==str1”得到的是false,用“equals(str1)"得到的是true?

3、str1的值不是被重新赋值为”023“了吗,为什么”"123" == str1”和“"123".equals(str1)”都是true?

我们现在来分析下为什么会是这样的结果:

1、先来看“==”号和“equals"在String比较中的含义有何不同:

我们都知道”==”号在进行String类型比较时,比较的是引用指向的对象的地址,而equals方法做了什么呢,我们来看源码:

   public boolean equals(Object anObject) {if (this == anObject) {return true;}if (anObject instanceof String) {String anotherString = (String)anObject;int n = value.length;if (n == anotherString.value.length) {char v1[] = value;char v2[] = anotherString.value;int i = 0;while (n-- != 0) {if (v1[i] != v2[i])return false;i++;}return true;}}return false;}

可以看到,equals方法先是比较对象地址,然后再比较对象的value值。通过前文中我们的分析,可以知道,String对象一旦被初始化,其对象的地址是不会被改变的,所以,通过==号来比较,如果相同,则一定是同一个对象。但是用equals方法不同,equals方法先对对象地址比较,如果地址不同,会再进行值的比较。

那我们再对前文中的测试代码进行分析,就不难知道为什么看上去会出现一些违反我们常识的结果了。

String str1="123";

String str2="123";

这其中str1和str2其实只是2个引用指向了同一个对象(Java是怎么让String的多个引用指向同一个对象地址,后面再讲)。所以:

1、当我们用反射修改了str1指向的对象的值为“023”,那当我们想输出str2指向的对象的值时,当然显示的也是“023",而不是我们常识性地认为是初始化的”123“。

2、当我们明明修改了str1对象的值为"023”,而我们不管用”123“==str1还是“123”equals(str1),得到的还是true,是因为==号和equals都是先判断两个对象地址是否相同,当地址相同了,即便value值被修改了,得到的还是true的结果。

而下面这句:

String str3="023",则是创建了一个和”123“不同的对象,str3指向的对象的地址和str1、str2是不同的。所以:

1、当我们用==号比较str1和str3时,得到的是false的结果,因为这2个引用指向的对象地址是不一样的。

2、当我们用str3.equals(str1)时,得到的是true的结果,是因为这2个引用指向对象的值是一样的。

四、总结

通过前面三部分的分析,我们可以得出以下结论:

1、String对象一旦被初始化,如果不通过反射,其对象地址和值是不可变的,是线程安全的。

2、String对象的引用被重新赋值时,如果值与初始化时的值不同,则其实是新创建了一个对象,引用指向的对象已经被改变。

3、如果想要比较2个String对象是否为同一个,用==才正确,用equals不一定得到正确的结果。

4、如果想要比较2个String对象的值,最好不要用==,因为==号不一定能反映对象真实的值,要用equals。

关于Java String 不可变性的分析相关推荐

  1. Java String split方法性能分析

    近期笔者分析一段在线代码的性能时,发现Java String类中split()方法的性能和网上一些帖子的描述并不相同,是以记录澄清. Java String split方法到底有没有使用正则表达式? ...

  2. Java String的不可变性

    String是Java中比较特殊的一个类,特殊就在于它具有不可变性.这篇文章主要讲Sring的不可变性的具体体现.实现原理.原因以及与之相关的编程实践. 1.不可变性与可变性 有不可变性那么肯定就有可 ...

  3. Java String类源码阅读笔记

    文章目录 一.前置 二.String类源码解析 1.String类继承关系 2.成员变量 3.构造方法 4.长度/判空 5.取字符 6.比较 7.包含 8.hashCode 9.查询索引 10.获取子 ...

  4. 画图说明Java String的不变性!可修改字符串不要轻易使用String!

    这里用一组图表来说明Java字符串的不可变性. 1. 声明一个字符串 以下代码初始化字符串s 变量s存储字符串对象的引用,如下所示.箭头可以解释为"存储的引用". 2. 将一个字符 ...

  5. java.util.ServiceLoader源码分析

    java.util.ServiceLoader源码分析 回顾: ServiceLoader类的使用(具体参考博客http://blog.csdn.net/liangyihuai/article/det ...

  6. Java - String源码解析及常见面试问题

    文章目录 Pre Q1: String 是如何实现的? Q2: String 有哪些重要的方法? 构造函数 equals() compareTo() [equals() vs compareTo() ...

  7. JAVA源码优化、分析工具

    JAVA源码优化.分析工具 一.11款用于优化.分析源代码的Java工具 1. PMD from http://pmd.sourceforge.net/ PMD能够扫描Java 源代码,查找类似以下的 ...

  8. Day 20: 斯坦福CoreNLP —— 用Java给Twitter进行情感分析

    今天学习如何使用斯坦福CoreNLP Java API来进行情感分析(sentiment analysis).前几天,我还写了一篇关于如何使用TextBlob API在Python里做情感分析,我已经 ...

  9. 深入理解Java虚拟机-常用vm参数分析

    Java虚拟机深入理解系列全部文章更新中- 深入理解Java虚拟机-Java内存区域透彻分析 深入理解Java虚拟机-常用vm参数分析 深入理解Java虚拟机-JVM内存分配与回收策略原理,从此告别J ...

最新文章

  1. 如何利用自组织竞争网络进行患者癌症发病预测
  2. 2.1.3 码元、波特、速率、带宽
  3. maven jetty/tomcat/wildfly plugin部署应用到本地容器
  4. python 3读网页文件及保存成本地文件,遇到的编码问题
  5. 运维基础(5)Ansible
  6. Oleans集群之Consul再解释
  7. 有关ACM学习的博客链接
  8. 计算机信息处理技术的基础知识列举出一些易错易混淆知识点,计算机考证实训报告指导书.doc...
  9. isp mpls专线
  10. shell基础入门1.1shell特性
  11. openGL ES进阶教程(六)美颜滤镜之美白,磨皮,红润
  12. Python五角星绘制
  13. 研发漫画之五:一切尽不在掌握,文山会海,累死我了
  14. 微信小程序按钮添加背景
  15. 嘿,妈妈,我是设计师
  16. 第八篇order订单专题(2)订单通知及属性
  17. SpringCloud微服务项目实战 - 2.App登录及网关
  18. Qt on Android 核心编程
  19. cordova-plugin-unionpay
  20. 科技论文的图题翻译,图例,图坐标轴

热门文章

  1. 【好文收藏】基于OpenStack和Kubernetes构建组合云平台——网络集成方案综述
  2. 建议74:警惕线程的IsBackground
  3. 将后台数据存放入Application域中
  4. 国际结算期末模拟试题A及参考答案
  5. Redis事务失效的三种场景
  6. 视频教程-顾比均线以及顾比熵指标的策略编写-Python
  7. linux vim 添加注释_服务器维护Linux Vim批量注释和自定义注释快捷键
  8. 注册skype的live名为直接邮箱地址的方法
  9. 一文详解假设检验、两类错误和p值
  10. 【Python】数据预处理之将类别数据转换为数值的方法(含Python代码分析)