String的不可变性、以及涉及到的字符串常量池和字符串拼接内部实现的剖析

1.String a="a"+"b"+"c"在内存中创建几个对象?

这个问题涉及到了字符串常量池和字符串拼接
String a="a"+"b"+"c"
通过编译器优化后,得到的效果是
String a="abc"

此时,如果字符串常量池中存在abc,则该语句并不会创建对象,只是讲字符串常量池中的引用返回而已

如果字符串常量池中不存在abc,则会创建并放入字符串常量池,并返回引用,此时会有一个对象进行创建


2.Java中的字符串常量池

Java中字符串对象创建有两种形式,一种为字面量形式,如String str = "droid";另一种就是使用new这种标准的构造对象的方法,如String str = new String("droid");,这两种方式我们在代码编写时都经常使用,尤其是字面量的方式。然而这两种实现其实存在着一些性能和内存占用的差别。
这一切都是源于JVM为了减少字符串对象的重复创建,其维护了一个特殊的内存,这段内存被成为字符串常量池或者字符串字面量池

工作原理当代码中出现字面量形式创建字符串对象时,JVM首先会对这个字面量进行检查,如果字符串常量池中存在相同内容的字符串对象的引用,则将这个引用返回,否则新的字符串对象被创建,然后将这个引用放入字符串常量池,并返回该引用

使用new创建当我们使用了new来构造字符串对象的时候,不管字符串常量池中有没有相同内容的对象的引用,新的字符串对象都会创建。

注意:1.字符串常量池实现的前提条件就是Java中String对象是不可变的,这样可以安全保证多个变量共享同一个对象。如果Java中的String对象可变的话,一个引用操作改变了对象的值,那么其他的变量也会受到影响,显然这样是不合理的
2.字符串常量池中存放的是引用还是对象,这个问题是最常见的。字符串常量池存放的是对象引用,不是对象。在Java中,对象都创建在堆内存中。

优缺点

字符串常量池的好处就是减少相同内容字符串的创建,节省内存空间。

如果硬要说弊端的话,就是牺牲了CPU计算时间来换空间。CPU计算时间主要用于在字符串常量池中查找是否有内容相同对象的引用。不过其内部实现为HashTable,所以计算成本较低。



3.Java细节:字符串的拼接

Java中仅有的重载运算符:在Java中,唯一被重载的运算符就是字符串的拼接相关的:+,+=。除此之外,Java设计者不允许重载其他的运算符。

拼接剖析-编译器的优化处理

既然Sting对象不可变,那么多个(三个及以上)字符串拼接必然产生多余的中间String对象。真的会有上面的性能代价么,字符串拼接这么常用,没有特殊的处理优化么,答案是有的,这个优化进行在编译器编译.java到bytecode时。
一个Java程序如果想运行起来,需要经过两个时期,编译时和运行时。在编译时,Java 编译器(Compiler)将java文件转换成字节码。在运行时,Java虚拟机(JVM)运行编译时生成的字节码。通过这样两个时期,Java做到了所谓的一处编译,处处运行。
Java编译器做的优化,当Java编译器遇到字符串拼接的时候,会创建一个StringBuilder对象,后面的拼接,实际上是调用StringBuilder对象的append方法。


<think>好的,我现在要仔细分析用户关于Java字符串常量池new String()的区别以及工作机制的问题。首先,用户提到了引用1、2、3,这些资料里解释了字符串常量池、intern方法以及不同创建方式的区别。我需要结合这些引用内容,系统地整理出两者的差异工作原理,并给出实际使用场景的建议。 首先,字符串常量池是什么?根据引用3,字符串常量池位于方法区,用于存储字符串字面量。当使用字面量创建字符串时,比如String str1 = "hello",JVM会检查常量池中是否存在该字符串,如果存在则直接引用,否则创建一个新的放入池中。而使用new String("abc")时,不管常量池是否存在,都会在堆中创建一个新对象,同时可能也会在常量池中创建(如果不存在的话)。 接下来,我需要理清两种创建方式的内存分配机制。根据引用2的例子,直接赋值的多个变量会指向同一个常量池对象,所以==比较返回true。而new出来的对象即使内容相同,也会在堆中生成不同实例,所以==比较为false。例如引用1中的例子,str1是直接赋值,str2是new出来的,比较它们的引用结果为false,但使用intern方法后,str3会指向常量池中的对象,所以str1 == str3变为true。 然后,工作机制的差异需要分点说明,包括内存位置、对象数量、比较方式等。比如,直接赋值可能不创建新对象,而new一定会创建至少一个对象。同时,intern方法的作用是将字符串对象添加到常量池中,并返回其引用,这在需要重用字符串时有用。 适用场景方面,直接赋值适合需要重用字符串的情况,如常量或频繁使用的字符串,可以减少内存占用。而new String()可能用在需要不同实例的情况,比如需要修改字符串内容而不影响其他引用的情况,但要注意字符串不可变性。另外,intern方法可以用于优化内存,但过度使用可能导致常量池过大,影响性能。 还需要考虑字符串不可变性,无论哪种方式创建的字符串,一旦创建内容不可变,任何修改都会生成新对象。这点可能影响性能,比如在频繁拼接字符串时,使用StringBuilder更高效。 最后,用户可能的问题包括内存泄漏、intern的使用注意事项、性能优化方法等,可以生成相关问题供用户参考。需要确保回答结构清晰,正确引用提供的资料,并符合LaTeX格式要求,行内公式用$...$,独立公式用$$...$$,但在这个问题中可能涉及较少数学公式,但如果有的话需要正确应用。</think>### Java字符串常量池与new String()工作机制分析 #### 一、核心机制对比 1. **字符串常量池的直接赋值** - 语法:$String\ str = "abc";$ - **内存行为**: - 检查常量池是否存在该字符串 - 存在则直接引用(不创建新对象) - 不存在则在常量池创建对象并引用 - 例如: ```java String a = "hello"; String b = "hello"; System.out.println(a == b); // true [^2] ``` - **适用场景**:需要重复使用相同字符串时(如配置参数、枚举值) 2. **new String()的堆内存分配** - 语法:$String\ str = new\ String("abc");$ - **内存行为**: - 强制在堆中创建新对象 - 仅在常量池不存在该字符串时,额外在常量池创建对象(总计最多2个对象) - 例如: ```java String c = new String("hello"); String d = new String("hello"); System.out.println(c == d); // false [^3] ``` - **适用场景**:需要独立实例时(如避免外部修改影响当前对象) #### 二、关键差异总结 | 维度 | 字符串常量池 | new String() | |--------------|---------------------------|--------------------------| | 内存位置 | 方法区(JDK7后移至堆) | 堆内存 | | 对象复用 | 自动复用 | 每次创建新实例 | | ==比较结果 | 内容相同则引用相同 | 内容相同引用仍不同 | | 内存效率 | 高 | 低 | #### 三、intern()方法的桥梁作用 通过$intern()$方法可将堆中对象存入常量池并返回其引用: ```java String str1 = "Hello"; String str2 = new String("Hello").intern(); System.out.println(str1 == str2); // true [^1] ``` **适用场景**:需要将动态生成的字符串加入常量池实现复用(如数据库查询结果去重) #### 四、实际使用建议 1. **优先选择常量池赋值** - 减少内存占用(避免重复对象) - 提升比较效率(直接使用==替代equals) 2. **谨慎使用new String()** - 需要明确对象独立性时使用 - 注意避免因大量创建导致内存压力 3. **intern()方法的权衡** - 优点:减少重复对象 - 风险:过度使用可能导致常量池膨胀(建议仅对高频重复字符串使用) #### 五、典型案例解析 **场景:读取10万条日志数据过滤重复IP** - **错误做法**:直接使用$new\ String(ip)$存储 → 产生10万个堆对象,内存占用高 - **优化方案**:调用$intern()$方法 ```java String canonicalIP = new String(rawIP).intern(); ``` → 相同IP指向常量池的单一实例,内存降低90%+ $$内存节省量 = \frac{N - K}{N} \times 100\%\quad (N:总数据量,\ K:唯一IP数)$$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值