字符串常量和变量

写代码的时候,发现对字符串常量改变的问题。网上找了一下资料,明白了

出处:http://blog.youkuaiyun.com/andylyc/archive/2008/01/16/2046225.aspx

 

请在(且只能在TC2.0)中运行下面代码,先不要看结果,想想会得到什么:   
  #include<stdio.h>   
  #include<stdlib.h>   
    
  int   
  main(   int   argn,   char*   argv[]   )   
  {   
          char*   szStringA   =   "Hello,world!";   
          char*   szStringB   =   "Hello,world!";   
    
          *szStringA   =   '-';   
          puts(   szStringB   );   
    
          return   0;   
  }   
  输出结果是:"-ello,world!";。是否和你想象的结果不一样呢?   
  ANSI   C明确说明:修改字符串常量,效果是未定义的  
  首先我们得清楚,如何才能得到字符串常量?只有一种方式:char*   szString   =   “Hello,world!”;这个声明得到一个字符串常量。那么char   szString[]   =   “Hello,world!”;可以吗?不可以!这样得到的是字符串变量。好了,回过来,由于ANSI   C没有要求编译器实现者如何具体对字符串常量进行处理,所以有些编译器会把相同的多个字符串常量看成一个(注意:这种优化仅有可能出现在字符串常量中,不 要把泛泛的用于其他类型的常量。如:int   num1   =   11;int   num2   =   11;虽然是两个相同的常量,但是修改num1不会影响num2),目的是为了节约内存空间,所以上面的例子中,当修改字符串a时,b也被修改了。从这里 可以看出,TC2.0有对字符串常量进行优化,而其他的编译器(如:DEVC++5.0)不一定也会优化,结果可能是无法通过编译,或者通过编译但是结果 是垃圾值。由于这些种种不确定因素,我们应尽量避免修改字符串常量。   
  如果必须修改字符串就没有办法呢?有!一定有!请注意:我们要的效果是修改字符。因此,只需要定义字符串变量,而不是字符串常量。前面说过,可以用数组的 方式定义char   szString[]   =   “Hello,world!”;;这样,在程序中就可以修改字符串了。   
  试试下面代码:   
  #include<stdio.h>   
  #include<stdlib.h>   
    
  int   
  main(   int   argn,   char*   argv[]   )   
  {   
          char   szStringA[]   =   "1234567890";   
          char   szStringB[]   =   "1234567890";  //sizeof(szStringB) != 10 ,sizeof(szStringB= 11
    
          szStringA[0]   =   '-';   
          puts(   szStringB   );   
    
          return   0;   
  }   
  没问题了吧。   
  OK,最后请记住:需要修改字符串,那么用char   szString[]   =   “Hello,world!”;进行声名!

<think>好的,用户的问题是关于Java字符串常量池中变量拼接的行为实现机制。我需要先回顾一下字符串常量池的基本知识,然后结合变量拼接的具体情况来分析。 首先,字符串常量池(String Constant Pool)是JVM为了优化字符串内存管理而设计的。根据引用[1][2],字符串常量池存储的是字符串对象的引用,底层使用StringTable实现,类似于哈希表。对于字面量字符串,比如String s = "abc",JVM会直接检查常量池中是否存在该字符串,如果存在则复用,否则创建新对象并放入池中。这部分用户可能已经了解,但问题在于变量拼接时的处理。 接下来,变量拼接的情况,比如String s1 = "a"; String s2 = s1 + "b";。这里s1是变量,与字面量"b"拼接。根据JVM的处理,这种拼接在编译时无法确定结果,所以会在运行时通过StringBuilder的append方法生成新字符串。因此,s2指向的是堆中的新对象,而不是常量池中的。这时候需要解释清楚,为什么这种情况下不会放入常量池,除非调用intern()方法。 然后,用户可能想知道如何让变量拼接的结果进入常量池。这时候需要提到intern()方法的作用,它会将字符串添加到常量池(如果池中没有),并返回池中的引用。例如,String s3 = (s1 + "b").intern(); 这样s3就会指向常量池中的对象。 同时,需要注意编译期优化的情况。如果拼接的都是字面量,比如"a" + "b",编译器会直接合并成"ab",并放入常量池。这时候生成的字符串在编译期就确定了,所以会复用常量池中的对象。这里需要区分编译时运行时的处理差异。 另外,可能需要解释不同JDK版本中的差异,比如JDK7之后字符串常量池的位置从永久代移到堆内存,但这对变量拼接的机制影响不大,主要影响内存分配的位置。 最后,总结变量拼接的机制,强调编译期不可确定的拼接会在堆中创建新对象,而字面量拼接会被优化。同时,提醒使用intern()方法的风险,如可能增加常量池负担,需谨慎使用。 用户可能还会有相关问题,比如如何避免重复创建字符串、不同拼接方式的性能差异、intern方法的使用场景等,可以生成这些问题供用户进一步学习。</think>### Java字符串常量池中的变量拼接机制 #### 1. **字符串常量池的基本规则** - **字面量直接赋值**:使用双引号直接创建的字符串(如`String s = "abc"`)会直接进入字符串常量池。JVM会检查池中是否存在该字符串,存在则复用,否则创建新对象并存入池中[^1][^2]。 - **`intern()`方法**:调用`intern()`时,若字符串不在池中,则将其加入池并返回引用;若已存在,则直接返回池中引用。 #### 2. **变量拼接的运行时行为** - **编译期不可确定性**:当拼接操作包含变量时(如`String s = s1 + s2`),编译器无法直接推断最终结果,因此会在运行时通过`StringBuilder`动态生成新字符串对象。 ```java String s1 = "Hello"; String s2 = s1 + "World"; // 等同于 new StringBuilder().append(s1).append("World").toString(); ``` - **新对象分配在堆中**:上述拼接生成的字符串不会自动进入常量池,而是直接在堆中创建新对象。因此,`s2 == "HelloWorld"`返回`false`(除非显式调用`intern()`)。 #### 3. **字面量拼接的编译期优化** - **常量折叠**:若拼接操作均为字面量(如`String s = "a" + "b"`),编译器会直接合并为`"ab"`,并存入常量池: ```java String s = "ab"; // 等同于直接赋值,s指向常量池中的"ab" ``` #### 4. **强制入池的方法** - 通过`intern()`方法可将运行时生成的字符串加入常量池: ```java String s3 = (s1 + "World").intern(); // 结果存入常量池(若池中不存在) ``` #### 5. **示例对比** | 场景 | 对象位置 | 是否入池 | |---------------------|---------------|-------------------| | `String s = "a"+"b"`| 常量池 | 是(编译期优化) | | `String s = s1+"b"` | 堆 | 否(需显式`intern()`)| | `s.intern()` | 常量池(若新) | 是 | --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值