做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. 安装证书服务:为Web站点启用HTTPS
  2. Unity 游戏开发技巧集锦之创建透明的材质
  3. cannot import name 'imresize'. The package prm under namespace prm could not be imported
  4. oracle启动服务和监听命令
  5. Linux查看本机外网ip
  6. Vmware中安装Ubuntu的步骤
  7. 机器学习算法Python实现:tfidf 特征词提取及文本相似度分类
  8. vue摸板 大数据_Vue和DataV强强联合,这个大数据可视化模板你一定要拥有
  9. java bitset_Java BitSet length()方法与示例
  10. 如何在Win11重置系统中保留个人文件 Win11重置系统保留个人文件方法
  11. java中有ClockPane类吗_Java程序设计教程 冶金工业出版社第9章
  12. MySQL怎么查询课程信息_mysql 查询没有学全所有课程的同学的信息
  13. 数据分析师 vs 算法工程师,Python 出身的程序员如何抉择?
  14. apache php 分离mysql_Apache、php、mysql单独安装配置
  15. hibernate理解
  16. nginx_lua vs nginx+php 应用场景
  17. 现场看女排vs日本了
  18. 神经网络学习 之 BP神经网络
  19. java中英文字幕和_为了边看美剧边学英语,我写了个字幕处理脚本
  20. 【第12天】给定一个X进制数字A,请你把它转换为十进制打印 | 进制转换

热门文章

  1. MySQL笔试题练习及答案(一)
  2. 微软称RealNetworks对其投诉实属无理取闹
  3. 用U盘做一个即插即用的unbuntu20系统
  4. VSCode插件打包迁移与指定位置
  5. java.lang.NoClassDefFoundError: javax/transaction/TransactionManager
  6. 湖北档案管理要求,发现档案管理系统也是很重要部分
  7. xingtai -究极炸弹
  8. 【raise JSONDecodeError(“Extra data“, s, end)】
  9. 使用go-ethereum创建私有网络
  10. SQL 注入漏洞(十四)xff 注入攻击