字符串是编程中应用到最多的一个数据类型,简单实用,但是深入理解它们,会给我们带来更多好处。
字符串缓存池:
为了节省内存,提高资源的复用,java中引入了字符串缓存池的概念。
缓存池中的字符串是不可回收的:
在缓存池中的字符串是不会被垃圾回收机制回收的,基本都是常驻内存,所以过多食用String类,可能会出现内存溢出(下面会讲)。
怎么样才会存进缓存池:
- 直接量赋值创建对象
在Java,基本包装类型String,Long,Float,Boolean.......都可以利用直接量创建对象。
String用直接量进行创建对象的时候,会先在缓存池找到字符串相同的对象,然后指向缓存池中该对象,这样就避免了重新为该对象分配内存,从而提高了复用,所以建议多用直接量去创建对象,这个也是上篇博文所推荐的。 - 调用构造器创建对象
这种方法调用构造器,凡是new出来的对象都要进行内存分配,所以是不会指向缓存池之前已有的对象,这样就导致缓存池里面可能有多个值相同的String对象。
package biaodashi; /** * 字符串关于缓存池 * * @author ccf * */ public class StringHuanCunChi { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub StringHuanCunChi huanCunChi = new StringHuanCunChi(); huanCunChi.test1(); } public void test1() { String str1 = "ccf"; String str2 = "ccf"; String str3 = new String("ccc"); // new 会有新的地址分配,所以不会指向缓存池的"ccc" String str4 = new String("ccc"); System.out.println("str1==str2?" + (str1 == str2)); System.out.println("str3==str4?" + (str3 == str4)); } } 运行结果 str1==str2?true str3==str4?false
结果:看出str1 和str2都是指向同一个对象 而str3和str4就不是同一个对象了。
怎么样才能指向缓存池已有的对象:
要指向缓存池对象,改对象必须是直接赋直接量,也可以是多个直接量的运算值,而不能调用方法或者其他变量,但是被final修饰的可以进行宏替换的常量也可以看成直接赋值,因为以上这一些都能在加在类的时候,就被虚拟机计算出来其值。所以能指向缓存池。
public void test1() { String str1 = "ccf"; String str2 = "c" + "c" + "f";//三个直接量进行运算 System.out.println("str1==str2?" + (str1 == str2)); 结果:str1==str2?true 多个直接量进行连接,也可以指向缓存池
上面的代码中 String str2 = "c" + "c" + "f";一共创建了几个对象?????
可能有人会说n个,但是其实只有一个,因为虚拟机对他们进行直接的计算,不需要创建对象,这也是使用直接量的好处。
public void test1() { String str1 = "ccf" + "长度为:" + "3"; String str2 = "ccf" + "长度为:" + str1.length(); System.out.println("str1==str2?" + (str1 == str2)); // 结果: str1==str2?false 结论:str2调用了方法str1.length(),故不能再虚拟机加在类的时候就计算确定str的值,所以打印结果如下 } public void test1() { final String length1 = "3"; String length2 = "3"; String str1 = "ccf" + "长度为:" + "3"; String str2 = "ccf" + "长度为:" + length1;//调用final变量 String str3 = "ccf" + "长度为:" + length2;//调用普通变量 System.out.println("str1==str2?" + (str1 == str2)); System.out.println("str1==str3?" + (str1 == str3)); 结果: str1==str2?true str1==str3?false 可以得出结论:被final修饰的 length1效果是和直接量一样的,在虚拟机加在时候,length1是直接替换"3",之后就跟length1没关系了,效果可以说跟str1中一摸一样,这也是final变量和其他变量的不同之处,称为宏替换。
关于String的不可变问题:
String
String
} 类是个不可变的类,创建对象后,不可以改变对象,但是日常代码经常对String进行操作,这对于String不可变这一说法是不是相悖了?看下这段代码:
public void testString() { String str1 = "Hello"; int OldId = System.identityHashCode(str1); str1 = str1 + " ccf!";//对字符串进行操作 int NewId = System.identityHashCode(str1); System.out.println("OldId=" + OldId + "\nNewId=" + NewId); } 结果: OldId=854453928 NewId=584020407 其中System.identityHashCode(str1);函数是打印出对象的HashCode,每个对象都有一个唯一的HashCode ,是对象的唯一标识,很明显,前后两个HashCode是不同的,
得出一些结论:对String对象进行操作后,其返回的是一个新的对象,之前那个对象是没有改变的,改变的是str这个引用所指的对象,这时候的对象已经是新的对象,然而之前那个对象被废弃了,但是他存在缓存池,因此不会被垃圾回收机制回收,所以这里会出现内存泄漏,所以操作字符串,尽量不用String。
但是StringBuffer和StringBuilder进行字符串操作的时候,就不会去new出现对象,引用的都是同一个对象,就可以减少String带来的弊端。
public void testStringBuilder() { StringBuilder builder = new StringBuilder("Hello"); System.out.println("ID=" + System.identityHashCode(builder)); builder.append(" ccf!"); System.out.println("ID=" + System.identityHashCode(builder)); System.out.println(builder); } 结果: ID=854453928 ID=854453928 Hello ccf!
相关推荐
总结一下,Java中创建字符串缓存类的主要目的是减少内存消耗和提高性能,特别是在频繁字符串操作的场景下。通过使用`StringBuffer`或`StringBuilder`以及自定义缓存类,我们可以有效地管理字符串对象,避免不必要的...
在Java中,我们有三种主要的字符串类:String、StringBuffer和StringBuilder,它们各自具有不同的特性和适用场景。 首先,`String`类是Java中的一个基础且不可变的类,被声明为`final`,其内部属性也是`final`的。...
根据不同的应用场景和需求,Java提供了多种处理字符串的方式,其中最为常见且重要的三种方式分别为:`String`(字符串常量)、`StringBuffer`(线程安全的字符串变量)以及`StringBuilder`(非线程安全的字符串变量...
在Java中,有几种不同的方式可以用来创建和修改字符串,其中最常用的是`String`、`StringBuffer`和`StringBuilder`。本文将详细介绍这三种类型的字符串及其区别,特别是`StringBuffer`和`StringBuilder`之间的差异。...
2. **缓存效率**:当创建相同的字符串时,如果该字符串已经存在于字符串池(String Pool)中,则不会创建新的实例,而是返回已存在的引用。这避免了内存中存在多个相同字符串副本的情况,提高了空间效率。 3. **性能...
字符串在Java中是不可变的对象,也就是说一旦创建了一个`String`对象,它的内容就不能被改变。这使得`String`对象非常适合用于缓存和其他需要保持数据不变性的场景。 #### 二、Java中的包(Package) Java中的包是...
在实际编程中,字符串的拼接经常使用`+`运算符或者`StringBuilder`/`StringBuffer`类,因为`+`在多次操作时会产生新的字符串对象,效率较低,而`StringBuilder`和`StringBuffer`则提供可变的字符序列,更适合于字符...
- **字符串池** 是 Java 虚拟机 (JVM) 内部的一个特殊缓存区域,用于存储字符串常量。当创建一个新的字符串时,JVM 首先检查字符串池中是否存在相同的字符串,如果存在,则直接返回该字符串的引用;如果不存在,则会...
如果字符串池中已存在这个字符串,则返回字符串池中的引用,否则在字符串池中创建一个新的字符串,并返回它的引用。 示例代码: ```java String hello = "Hello"; System.out.print((hello == ("Hel" + "lo")) + "...
Java中的字符串处理是编程中常见的任务,涉及到三个主要的类:String、StringBuffer和StringBuilder。它们之间的主要区别在于线程安全、性能和可变性。 首先,`String`类是不可变的,这意味着一旦创建了一个`String...
首先,字符串 `"abc"` 会被检查是否已经存在于字符串常量池中。如果不存在,则在常量池中创建一个字符串 `"abc"` 对象。之后,在堆内存中通过 `new String("abc")` 创建一个新的字符串对象。如果 `"abc"` 已经存在于...
利用 `StringBuilder` 或 `StringBuffer` 修改字符串: ```java String s = "abc"; StringBuilder sb = new StringBuilder(s); sb.setCharAt(0, 'd'); s = sb.toString(); ``` 这段代码首先使用 `StringBuilder` ...
此外,编译器和JIT(Just-In-Time)编译器可以进行更多的优化,如字符串连接操作(+操作符)可以通过StringBuilder或StringBuffer的append方法在后台优化,避免了每次连接时创建新的String对象。 2. **线程安全**:...
- `String c = "A"`会直接从字符串常量池中获取或创建"A"的对象,`String a = new String("A")`则会先在常量池创建"A",然后在堆中创建一个新的`String`对象,其值与常量池中的"A"共享。 - `String s = "a" + "b" ...
字符串缓冲区,通常称为“字符串缓冲”或“字符串缓存”,是一个内存区域,用于存储和管理字符串数据。它的主要目标是避免频繁地创建和销毁字符串对象,因为在许多编程语言中,字符串是不可变的,每次修改都会生成一...
本文将深入探讨Java中的三个主要字符串类:String、StringBuilder和StringBuffer,以及它们各自的特点和常用方法。 首先,`String`类是最基础的字符串类,它代表不可变的字符序列。这意味着一旦创建了`String`对象...
在Java编程语言中,String、StringBuilder和StringBuffer都是用来处理字符串的重要类,它们各自具有不同的特性和使用场景。本文将对这三个类进行源代码分析,探讨它们的主要数据组织、功能实现以及性能差异。 1.1 ...