今天看到一篇文章中,写了关于java中的String。我看了后,是我从学java来觉得是最好的一篇关于String类的文章。看了这篇文章你就会对String的认识会提高一个层次。故将原作者的文章特意转载过来分享下。

作者: chenssy

出处: http://cmsblogs.com/?p=863

今天朋友问我String的内容是真的不可变吗?我肯定告诉他是的?因为在我的主观意识里String就是一个不可变的对象。于是他给我发了这段程序:

public class StringTest {
    public static void main(String[] args) throws Exception {
        String a = "chenssy";
        System.out.println("a = " + a);
        Field a_ = String.class.getDeclaredField("value");
        a_.setAccessible(true);
        char[] value=(char[])a_.get(a);
        value[4]='_';   //修改a所指向的值
        System.out.println("a = " + a);
    }
}

看到这个简单的程序,我笑了,你这不是从底层来修改String的值么?从这里来理解String的值肯定是可以改变的啦(我们应该始终相信String的不可变性)!接着他再给我一段程序:

public class StringTest {
    public static void main(String[] args) throws Exception {
        String a = "chenssy";
        String b = "chenssy";
        String c = new String("chenssy");
        System.out.println("--------------修改前值-------------------");
        System.out.println("a = " + a);
        System.out.println("b = " + b);
        System.out.println("c = " + c);
        //修改String的值
        Field a_ = String.class.getDeclaredField("value");
        a_.setAccessible(true);
        char[] value=(char[])a_.get(a);
        value[4]='_';   //修改a所指向的值

        System.out.println("--------------修改后值-------------------");
        System.out.println("a = " + a);
        System.out.println("b = " + b);
        System.out.println("chenssy");
        System.out.println("c = " + c);
    }
}

乍看这程序是异常的简单,无非就是赋值、改值、输出嘛!可能你现在就会毫不犹豫的说太简单了结果就是……。但是!!你的毫不犹豫会害死你,而且你的结果很可能错误。那么运行结果是什么呢?

--------------修改前值-------------------
a = chenssy
b = chenssy
c = chenssy
--------------修改后值-------------------
a = chen_sy
b = chen_sy
chen_sy
c = chen_sy

修改前值很容易理解,但是修改后值呢?是不是有点儿不理解呢?你可能会问:为什么System.out.println(“chenssy”);的结果会是chen_ssy,System.out.println(“c = ” + c);也是chen_ssy呢?

要明白这个其实也比较简单,掌握一个知识点:字符串常量池。

我们知道字符串的分配和其他对象分配一样,是需要消耗高昂的时间和空间的,而且字符串我们使用的非常多。JVM为了提高性能和减少内存的开销,在实例化字符串的时候进行了一些优化:使用字符串常量池。每当我们创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就直接返回常量池中的实例引用。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。由于String字符串的不可变性我们可以十分肯定常量池中一定不存在两个相同的字符串(这点对理解上面至关重要)。

我们再来理解上面的程序。

String a = “chenssy”;

String b = “chenssy”;

a、b和字面上的chenssy都是指向JVM字符串常量池中的”chenssy”对象,他们指向同一个对象。

String c = new String(“chenssy”);

new关键字一定会产生一个对象chenssy(注意这个chenssy和上面的chenssy不同),同时这个对象是存储在堆中。所以上面应该产生了两个对象:保存在栈中的c和保存堆中chenssy。但是在Java中根本就不存在两个完全一模一样的字符串对象。故堆中的chenssy应该是引用字符串常量池中chenssy。所以c、chenssy、池chenssy的关系应该是:c—>chenssy—>池chenssy。整个关系如下:

通过上面的图我们可以非常清晰的认识他们之间的关系。所以我们修改内存中的值,他变化的是所有。

总结:虽然a、b、c、chenssy是不同的对象,但是从String的内部结构我们是可以理解上面的。String c = new String(“chenssy”);虽然c的内容是创建在堆中,但是他的内部value还是指向JVM常量池的chenssy,它构造chenssy时所用的参数依然是chenssy字符串常量。

为了让各位充分理解常量池,特意准备了如下一个简单的题目:

String a = "chen";
String b = a + new String("ssy");

创建了几个String对象??

原作者给出的答案

应该只有两个或者三个!

对于String a = "chen";如果常量池中存在的话,它是不会产生String对象的,直接饮用常量池中的chen即可。

      对于String b = a + new String("ssy");而言,new String("ssy")一定会产生一个String对象,该对象的value指向常量池中"ssy"vlaue。但是到底有没有ssy呢?这要看常量池中是否存在这个字符串了。如果有的话是不会产生ssy的。没有的话才会产生。

       a + new String("ssy");这里也会产生一个。

       所以答案应该是:2、3、4。这是鄙人拙见!!

再解java中的String的更多相关文章

  1. java中String是对象还是类?详解java中的String

    有很多人搞不懂对象和类的定义.比如说java中String到底是对象还是类呢? 有人说String 既可以说是类,也可以说是对象. 其实他这么说也没问题, 类和对象其实都是一个抽象的概念. 我们可以把 ...

  2. 详解Java中的clone方法

    详解Java中的clone方法 参考:http://blog.csdn.net/zhangjg_blog/article/details/18369201/ 所谓的复制对象,首先要分配一个和源对象同样 ...

  3. 关于JAVA中的String的使用与连接(转)

    JAVA中的String连接性能 Java中的String是一个非常特殊的类,使它特殊的一个主要原因是:String是不可变的(immutable).           String的不可变性是Ja ...

  4. 详解Java中的clone方法:原型模式

    转:http://developer.51cto.com/art/201506/478985.htm clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的 ...

  5. Java中的String为什么是不可变的?

    转载:http://blog.csdn.net/zhangjg_blog/article/details/18319521 什么是不可变对象? 众所周知, 在Java中, String类是不可变的.那 ...

  6. Java中的String与常量池[转帖]

    string是java中的字符串.String类是不可变的,对String类的任何改变,都是返回一个新的String类对象.下面介绍java中的String与常量池. 1. 首先String不属于8种 ...

  7. Java中的String与常量池

    string是java中的字符串.String类是不可变的,对String类的任何改变,都是返回一个新的String类对象.下面介绍java中的String与常量池. 1. 首先String不属于8种 ...

  8. Java基础知识强化101:Java 中的 String对象真的不可变吗 ?

    1. 什么是不可变对象?       众所周知, 在Java中, String类是不可变的.那么到底什么是不可变的对象呢? 可以这样认为:如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对 ...

  9. Java中的String,StringBuilder,StringBuffer三者的区别

    最近在学习Java的时候,遇到了这样一个问题,就是String,StringBuilder以及StringBuffer这三个类之间有什么区别呢,自己从网上搜索了一些资料,有所了解了之后在这里整理一下, ...

随机推荐

  1. Quartz.net 开源job调度框架(一)

    Quartz.NET是一个开源的作业调度框架,非常适合在平时的工作中,定时轮询数据库同步,定时邮件通知,定时处理数据等. Quartz.NET允许开发人员根据时间间隔(或天)来调度作业.它实现了作业和 ...

  2. linux中的通配符与正则表达式

    在linux中,有通配符及正则表达式,那么什么是通配符和正则表达式,什么时候用?   通配符 它是由shell解析,并且一般用于匹配文件名,实际上就是shell解释器去解析的特殊符号,linux系统通 ...

  3. 7.6 数据注解特性--StringLength

    StringLength attribute can be applied to a string type property of a class. EF Code-First will set t ...

  4. TeamCity : 配置第一个工程

    前面我们已经创建了TeamCity Server 和一个 Agent,接下来我们为一个.net core 控制台程序配置自动化的编译. 创建 .net core 项目 我们在本地创建一个简单的 .ne ...

  5. 关系数据库SQL之高级数据查询:去重复、组合查询、连接查询、虚拟表

    前言 接上一篇关系数据库SQL之基本数据查询:子查询.分组查询.模糊查询,主要是关系型数据库基本数据查询.包括子查询.分组查询.聚合函数查询.模糊查询,本文是介绍一下关系型数据库几种高级数据查询SQL ...

  6. .NET Task揭秘(一)

    Task为.NET提供了基于任务的异步模式,它不是线程,它运行在线程池的线程上.本着开源的精神, 本文以解读基于.NET4.5 Task源码的方式来揭秘Task的实现原理.   Task的创建 Tas ...

  7. 面积(area)

    题目描述 编程计算由"*"号围成的下列图形的面积.面积计算方法是统计*号所围成的闭合曲线中点的数目.如图所示,在10*10的二维数组中,“*”围住了15个点,因此面积为15. 0 ...

  8. html 输入框 只能输入数字 只能输入字母数字组合

    JS判断只能是数字和小数点 1.文本框只能输入数字代码(小数点也不能输入) <input onkeyup="this.value=this.value.replace(/\D/g,'' ...

  9. 推荐15款制作 SVG 动画的 JavaScript 库

    在当今时代,SVG是最流行的和正在被众多的设计人员和开发人员使用,创建支持视网膜和响应式的网页设计.绘制SVG不是一个艰巨的任务,因为大量的 JavaScript 库可与 SVG 图像搭配使用.这些J ...

  10. Javascript对象

    这次的分享,主要还是想跟大家聊聊Javascript语言中很重要的概念之一,对象.为什么说之一呢?因为Javascript其他重要概念还包括:作用域 作用域链 继承 闭包 函数 继承 数组 ..... ...