博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java中的字符串String的不可变性
阅读量:6229 次
发布时间:2019-06-21

本文共 3420 字,大约阅读时间需要 11 分钟。

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

打印出的结果为:

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

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

结论:

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

转载地址:http://ytina.baihongyu.com/

你可能感兴趣的文章
Lsyncd搭建同步镜像-用Lsyncd实现本地和远程服务器之间实时同步
查看>>
.NET平台MongoDB下使用JobStore存储Quartz.Net的Job,Trigger数据
查看>>
Java多线程编程—锁优化
查看>>
python文本 字符与字符值转换
查看>>
Linux虚拟化技术KVM、QEMU与libvirt的关系(转)
查看>>
Ceph分布式存储-原理介绍及简单部署
查看>>
MYSQL数据库设计规范与原则
查看>>
UWP: 实现 UWP 应用自启动
查看>>
Windows内核之进程的终止和子进程
查看>>
Python 文件 readline() 方法
查看>>
String,到底创建了多少个对象?
查看>>
linux查找目录下的所有文件中是否含有某个字符串
查看>>
UWP 手绘视频创作工具技术分享系列 - 有 AI 的手绘视频
查看>>
各行业最受欢迎的编程语言,硬件最青睐C和C++
查看>>
监听用户的后退键,解决部分浏览器回退的bug
查看>>
Vivado+FPGA:如何使用Debug Cores(ILA)在线调试(烧录到flash里可以直接启动)
查看>>
[Preference] How to avoid Forced Synchronous Layout or FSL to improve site preference
查看>>
【laravel5.4】php artisan migrate报错:Specified key was too long; max key length is 767 bytes
查看>>
[转]外贸出口流程图
查看>>
微信小程序onLaunch修改globalData的值
查看>>