我的编程空间,编程开发者的网络收藏夹
学习永远不晚

分析JDK中String的存储区与不可变性

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

分析JDK中String的存储区与不可变性

这篇文章主要介绍“分析JDK中String的存储区与不可变性”,在日常操作中,相信很多人在分析JDK中String的存储区与不可变性问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”分析JDK中String的存储区与不可变性”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

1. 数据存储区

String是一个比较特殊的类,除了new之外,还可以用字面常量来定义。为了弄清楚这二者间的区别,首先我们得明白JVM运行时数据存储区,这里有一张图对此有清晰的描述:
分析JDK中String的存储区与不可变性

非共享数据存储区

非共享数据存储区是在线程启动时被创建的,包括:

  • 程序计数器(program counter register)控制线程的执行;

  • 栈(JVM Stack, Native Method Stack)存储方法调用与对象的引用等。

  • 共享数据存储区

    该存储区被所有线程所共享,可分为:

  • 堆(Heap)存储所有的Java对象,当执行new对象时,会在堆里自动进行内存分配。

  • 方法区(Method Area)存储常量池(run-time constant pool)、字段与方法的数据、方法与构造器的代码。

  • 2. 两种实例化

    实例化String对象:

    public class StringLiterals {    public static void main(String[] args) {        String one = "Test";        String two = "Test";        String three = "T" + "e" + "s" + "t";        String four = new String("Test");    }}

    javap -c StringLiterals反编译生成字节码,我们选取感兴趣的部分如下:

      public static void main(java.lang.String[]);    Code:       0: ldc           #2                  // String Test       2: astore_1       3: ldc           #2                  // String Test       5: astore_2       6: ldc           #2                  // String Test       8: astore_3       9: new           #3                  // class java/lang/String      12: dup      13: ldc           #2                  // String Test      15: invokespecial #4                  // Method java/lang/String."": (Ljava/lang/String;)V      18: astore        4      20: return}

    ldc #2表示从常量池中取#2的常量入栈,astore_1表示将引用存在本地变量1中。因此,我们可以看出:对象one、two、three均指向常量池中的字面常量"Test";对象four是在堆中new的新对象;如下图所示:
    分析JDK中String的存储区与不可变性
    总结如下:

  • 当用字面常量实例化时,String对象存储在常量池;

  • 当用new实例化时,String对象存储在堆中;

  • 操作符==比较的是对象的引用,当其指向的对象不同时,则为false。因此,开篇中的代码会出现通过new所创建String对象不一样。

    3. 不可变String

    String源码

    JDK7的String类:

    public final class String    implements java.io.Serializable, Comparable, CharSequence {        private final char value[];        private int hash; // Default to 0}

    String类被声明为final,不可以被继承,所有的方法隐式地指定为final,因为无法被覆盖。字段char value[]表示String类所对应的字符串,被声明为private final;即初始化后不能被修改。常用的new实例化对象String s1 = new String("abcd");的构造器:

    public String(String original) {    this.value = original.value;    this.hash = original.hash;}

    只需将value与hash的字段值进行传递即可。

    不可变性

    所谓不可变性(immutability)指类不可以通过常用的API被修改。为了更好地理解不可变性,我们先来看《Thinking in Java》中的一段代码:

    //: operators/Assignment.java// Assignment with objects is a bit tricky.import static net.mindview.util.Print.*;class Tank {  int level;}   public class Assignment {  public static void main(String[] args) {    Tank t1 = new Tank();    Tank t2 = new Tank();    t1.level = 9;    t2.level = 47;    print("1: t1.level: " + t1.level +          ", t2.level: " + t2.level);    t1 = t2;    print("2: t1.level: " + t1.level +          ", t2.level: " + t2.level);    t1.level = 27;    print("3: t1.level: " + t1.level +          ", t2.level: " + t2.level);  }} //:~

    上述代码中,在赋值操作t1 = t2;之后,t1、t2包含的是相同的引用,指向同一个对象。因此对t1对象的修改,直接影响了t2对象的字段改变。显然,Tank类是可变的。

    也许,有人会说s = s.concat("ef");不是修改了对象s么?而事实上,我们去看concat的实现,会发现其返回的是新String对象(return new String(buf, true););改变的只是s1引用所指向的对象,如下图所示:
    分析JDK中String的存储区与不可变性

    4. 反射

    String的value字段是final的,可不可以通过过某种方式修改呢?答案是反射。在stackoverflow上有这样一段修改value字段的代码:

    String s1 = "Hello World";  String s2 = "Hello World";  String s3 = s1.substring(6);  System.out.println(s1); // Hello World  System.out.println(s2); // Hello World  System.out.println(s3); // World  Field field = String.class.getDeclaredField("value");  field.setAccessible(true);  char[] value = (char[])field.get(s1);  value[6] = 'J';  value[7] = 'a';  value[8] = 'v';  value[9] = 'a';  value[10] = '!';  System.out.println(s1); // Hello Java!  System.out.println(s2); // Hello Java!  System.out.println(s3); // World

    在上述代码中,为什么对象s2的值也会被修改,而对象s3的值却不会呢?根据前面的介绍,s1与s2指向同一个对象;所以当s1被修改后,s2也会对应地被修改。至于s3对象为什么不会?我们来看看substring()的实现:

    public String substring(int beginIndex) {    if (beginIndex < 0) {        throw new StringIndexOutOfBoundsException(beginIndex);    }    int subLen = value.length - beginIndex;    if (subLen < 0) {        throw new StringIndexOutOfBoundsException(subLen);    }    return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);}

    当beginIndex不为0时,返回的是new的String对象;当beginIndex为0时,返回的是原对象本身。如果将String s3 = s1.substring(6);改为String s3 = s1.substring(0);,那么对象s3也会被修改了。

    如果仔细看java.lang.String.java,我们会发现:当需要改变字符串内容时,String类的方法返回的是新String对象;如果没有改变,String类的方法则返回原对象引用。这节省了存储空间与额外的开销。

到此,关于“分析JDK中String的存储区与不可变性”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

分析JDK中String的存储区与不可变性

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

分析JDK中String的存储区与不可变性

这篇文章主要介绍“分析JDK中String的存储区与不可变性”,在日常操作中,相信很多人在分析JDK中String的存储区与不可变性问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”分析JDK中String的存储
2023-06-02

JS中内存与变量存储的示例分析

这篇文章将为大家详细讲解有关JS中内存与变量存储的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。JS神奇的Number案例一:金额的计算与传递18.9 * 100=1889.99999999999
2023-06-20

探索Java中的静态变量与实例变量:存储区域、生命周期以及内存分配方式的区别

文章目录 静态变量实例变量不可变对象静态变量和实例变量有什么区别?静态变量实例变量 Object 类都有哪些公共方法?Java 创建对象有哪几种方式?a==b 与 a.equals(b) 有什么区别?总结 &#
2023-08-17

.Net中字符串不变性与相等判断的特殊应用场景分析

这篇文章主要介绍了.Net中字符串不变性与相等判断的特殊应用场景分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇.Net中字符串不变性与相等判断的特殊应用场景分析文章都会有所收获,下面我们一起来看看吧。问题请
2023-06-30

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录