java中的String的不可变性,网上已经有了大把大把的文章证明了。一般都是都是通过代码证明的,当然,我也不能免俗,我先列一段代码:

 public static void main(String[]args){String one=new String("abc");String two=new String("abc");System.out.println("第一步one==two:"+(one==two));System.out.println("第二步one.equals(two):"+one.equals(two));System.out.println();String three=one;System.out.println("第三步one==three:"+(one==three));System.out.println("第四步one.equals(three):"+one.equals(three));System.out.println();one="abcdefg";System.out.println("第五步one==three:"+(one==three));System.out.println("第六步one.equals(three):"+one.equals(three));System.out.println();String seven="abc";String eight="abc";System.out.println("第七步seven==eight:"+(seven==eight));System.out.println("第八步seven.equals(eight):"+seven.equals(eight));
}
复制代码

答案为:

    第一步one==two:false第二步one.equals(two):true第三步one==three:true第四步one.equals(three):true第五步one==three:false第六步one.equals(three):false第七步seven==eight:true第八步seven.equals(eight):true
复制代码

我们一步一步来看代码。

第一步:第一步的答案是为false,这当然是没有疑问的,是new出的两个不同对象,通过“==”来比较的话,比较的是两个对象之间的引用,当然是不相同的,所以为false。

第二步:第二步的答案是true。这是对的,虽然是两个不同的对象,但是两个对象引用指向的值都是一样的,而通过“equals”来进行比较的话,比较的是两个对象的引用所指向的值,所以为true。

第三步第四步:第三步和第四步的答案都为true。在看第三步之前,我们要看到之前有一个代码:

 String three=one;
复制代码

这个代码,意味着将one这个字符串的引用值赋给three,也就是说,one和three指向的是同一个对象,那么第三步和第四步的值当然都为true了。

第五步第六步:到了这一步之前,先看之前的代码:

    String one=new String("abc");String three=one;one="abcdefg";System.out.println("第五步one==three:"+(one==three));System.out.println("第六步one.equals(three):"+one.equals(three));
复制代码

我把之前的和第五步和第六步相关的代码提炼出来。因为刚开始one和three是指向的同一个对象,但是后面one又改变了,one="abcdefg",于是得出的第五步和第六步的值都是false。这是为什么呢?

如果one和three指向的都是同一个对象,那么对one的修改应该是完全同步到three上面才对啊?

其实,在one=“abcdefg”这段代码,因为在现在的jdk版本中,String常量池的存在于堆中,当发现在常量池里面没有“abcdefg”这个字符串,那么就会生成一个新的字符串对象。 那么,我们就能理解另外为什么第五步和第六步的值都为false了,因为one已经实际上是一个新new出来的对象,和three是完全不同的两个对象了。

这个地方,我们引入的正是java中的String的不可变性

第七步第八步:到这一步,我们先把代码提炼出来。

    String seven="abc";String eight="abc";System.out.println("第七步seven==eight:"+(seven==eight));System.out.println("第八步seven.equals(eight):"+seven.equals(eight));
复制代码

答案都是true,我们不免产生了迷惑,这和第五步,第六步说的似乎不一样啊?这个时候我们深入的去了解一下在java中,String类型的到底是怎么生成对象的。

先说new String("abc")的方法创建的字符串,这种方法是不管什么时候,都是new一个对象出来。

而另外一种是String seven="abc";这种类型的,这种类型的方法呢,先在栈中创建一个对String类的对象引用变量str,然后查找栈中(也是我们所说的字符串常量池,jdk版本为1.6及之前,常量池是存在Pern Gen区,也就是方法区。1.7版本后常量池就存在与堆中了)有没有存放"abc",如果没有,则将"abc"存放进栈,并令str指向”abc”,如果已经有”abc” 则直接令str指向“abc”。 也就是说,这种方法,可能不会new一个对象出来,可能只是指向了同一个引用而已。

这样的话,我们便能理解第五步第六步,也能理解第七步和第八步了。seven是生成了一个新的对象,但是eight并没有生成一个新的对象,它只是在栈中发现了它需要的而且是由seven生成的“abc”,于是它便直接指向了这个对象。

当然,仅仅通过我们的代码来验证,还是不够的,通常我们需要去了解一下String类型的源码,才能更加深刻的理解java中的String的不可变这个特性。

String类型的源码如下:

从这一段代码我们可以发现,String类型的底层其实是char类型的数组,而且是由final修饰的。于是我们可以得出两个结论:

    第一:String类型的长度是不可改变的。(因为底层是数组)第二:String类型的值是不可改变的。(因为是final修饰的)
复制代码

当然,实际上在java中因为反射的原因,我们可以对String类型的值进行修改,真正能坚持不变的可能是String类型的长度。

通过反射修改String类型的值的代码如下:

    static final Unsafe unsafe = getUnsafe();static final boolean is64bit = true;public static void main(String[]args) throws NoSuchFieldException, IllegalAccessException {String s = "Hello World";Double[] ascending = new Double[16];for(int i=0;i<ascending.length;i++){ascending[i] = (double) i;}System.out.println("未通过反射修改字符串的值:");printAddresses(s, ascending);//获取String类中的value属性Field valueField = String.class.getDeclaredField("value");//改变value属性的访问权限valueField.setAccessible(true);//获取s对象上的value属性的值char[] value = (char[]) valueField.get(s);//改变value所引用的数组中的第6个字符value[5] = '_';System.out.println("通过反射修改字符串的值:");printAddresses(s, ascending);
}/*** 获取对象的引用值* @param label* @param objects*/
public static void printAddresses(String label, Object... objects) {System.out.print("s="+label + "  引用值为: 0x");long last = 0;int offset = unsafe.arrayBaseOffset(objects.getClass());int scale = unsafe.arrayIndexScale(objects.getClass());switch (scale) {case 4:long factor = is64bit ? 8 : 1;final long i1 = (unsafe.getInt(objects, offset) & 0xFFFFFFFFL) * factor;System.out.print(Long.toHexString(i1));for (int i = 1; i < objects.length; i++) {final long i2 = (unsafe.getInt(objects, offset + i * 4) & 0xFFFFFFFFL) * factor;}break;case 8:throw new AssertionError("Not supported");}System.out.println();
}private static Unsafe getUnsafe() {try {Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);return (Unsafe) theUnsafe.get(null);} catch (Exception e) {throw new AssertionError(e);}
}
复制代码

打印出的结果为:

    未通过反射修改字符串的值:s=Hello World  引用值为: 0x76be43a18通过反射修改字符串的值:s=Hello_World  引用值为: 0x76be43a18
复制代码

我们发现,对象的值变了,但是引用没有变。

结论:

所以,实际上,String类型的不可变,是长度的不可变,它的值确实是可以通过反射进行改变的。

java中的字符串String的不可变性相关推荐

  1. Java 中的字符串(String)与C# 中字符串(string)的异同

    1. C# 中比较两个字符串字面量是否相等,可以使用 "=="比较运算符,是因为string 类型重写(override)了"==" 和 "!=&qu ...

  2. 一篇让你读懂java中的字符串(String)

    目录 创建字符串 方式1 方式2 方式3 三种方式的内存图 方式1 方式2 方式3 总结 理解池的概念 回忆引用 字符串判断相等 判断字符串引用是否相等 代码1 代码2 代码3 代码4 总结 判断字符 ...

  3. 在java中除去字符串(String)中的换行字符(\r \n \t)

    我们先来看几个例子: 例1: public class Test { public static void main(String[] args) { String s = "'sds gd ...

  4. java里面string什么意思_「Java基础知识」Java中的字符串是什么

    原标题:「Java基础知识」Java中的字符串是什么 字符串顾名思义就是一些字符组合在一起组成的一串数据,称作字符串,在Java中字符串用双引号包围起来,格式为String string = &quo ...

  5. 检查Java中的字符串是空还是空[重复]

    本文翻译自:Checking if a string is empty or null in Java [duplicate] This question already has an answer ...

  6. 如何从Java中的字符串值获取枚举值?

    说我有一个枚举 public enum Blah {A, B, C, D } 我想找到一个字符串的枚举值,例如"A"就是Blah.A 怎么可能做到这一点? Enum.valueOf ...

  7. Java中的字符串驻留

    转自:http://www.cdtarena.com/javapx/201307/9088.html 最近在工作的时候,一句再正常不过的代码String a = "hello" + ...

  8. java中的字符串相关知识整理

    字符串为什么这么重要 写了多年java的开发应该对String不陌生,但是我却越发觉得它陌生.每学一门编程语言就会与字符串这个关键词打不少交道.看来它真的很重要. 字符串就是一系列的字符组合的串,如果 ...

  9. java oracle 连接字符串函数_通过shell来比较oracle和java中的字符串使用

    这些准备工作齐了之后,我们来从Java中的字符串使用入手来比较一下oracle中对于字符串的处理. java中有如下的一些函数,我会依次来做比较. public char charAt(int ind ...

  10. 图说:为什么Java中的字符串被定义为不可变的

    转载自 图说:为什么Java中的字符串被定义为不可变的 字符串,想必大家最熟悉不过了,通常我们在代码中有几种方式可以创建字符串,比如:String s = "Hollis";这时, ...

最新文章

  1. Django模型 Q对象实现复杂查找
  2. 小工匠聊架构 - 分布式缓存技术
  3. SAP关于销售来自可选工厂的解决方案
  4. linux下重启weblogic(关闭和启动)
  5. 10853k1_领导学基础_21秋考试
  6. 移动端类似IOS的滚动年月控件(需要jQuery和iScroll)
  7. 语言inc c,汇编语言练习
  8. 卸载python的正确姿势
  9. 国外十大流行的服务器监控工具
  10. 当你拥有足够的经验时,自然就会想到的东西---面向对象的设计原则!
  11. 计算机系统AD转换,AD和DA转换是什么意思?
  12. 韩语在线翻译图片识别_超强的免费OCR文字扫描工具,网页视频PDF均可识别并翻译...
  13. 金园云化工园区智慧应急解决方案
  14. 关于嵌入式工程师需要知道的网站
  15. html支持es6,ie不支持es6语法 浏览器怎么使用ES6的Proxy
  16. Jenkins系列之——第五章 Jenkins编译一个Spring Boot项目并通过SSH推送到远程
  17. tensorflow 19: tflite 概念理解
  18. Git常规配置与用法(记录,git配置文件在系统用户文件夹下)
  19. 手游代理是怎么做的?
  20. python实现决策树-数据集如下图所示,根据我们对决策树的理解,设计一棵决策树,并输入{Age:36,Salary:H,STU:No,Credit:OK} 测试数据,是否与预期结果一致?

热门文章

  1. 以Graphicslayer为管理组来管理Element.
  2. java核心知识点学习----创建线程的第三种方式Callable和Future CompletionService
  3. 婚姻是一堂需要认真学习的课程
  4. 记录:txt文本分割命令,用于notepad++无法打开情况下分割文件
  5. OpenCV之图像二值化
  6. freeSWITCH中动态加载模块
  7. linux下交叉编译ffmpeg,并加入H264编码支持
  8. linux缺页异常处理--内核空间
  9. mysql日志文件的类型和作用_Mysql日志文件和日志类型介绍
  10. python 嵌套list的一些小结