`

String,到底创建了多少个对象?

    博客分类:
  • Java
阅读更多
String str=new String("aaa");

这行代码究竟创建了几个String对象呢?答案是2个,而不是3个。由于new String("aaa")相当于"aaa"与一个就是创建出来的放在堆时原实例对象,而另一个就是放在常量池中的 "aaa" 对象,当然这里的str本身只是一个引用,放在栈里,用来指向堆中创建出来的对象。

常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。

String str="aaa"; 

只创建1个对象。这里涉及到字符串常量池,在JVM中有一个字符串池,它用来保存很多可以被共享的String对象,这样如果我们在使用同样字面字符串时,它就使用字符串池中同字面的字符串。当然我们可以使用String对象的intern()方法来访问String对象在字符串池中所对应的常量对象。

上面这行代码被执行的时候,JVM先到字符串池中查找,看是否已经存在值为"aaa"的对象,如果存在,则不再创建新的对象,直接返回已存在对象的引用;如果不存在,则先创建这个对象,然后把它加入到字符串池中,再将它的引用返回。

 

String str1="aaa"; 
String str2="aaa";  

也只创建1个对象。能过上面的解释这个就更清楚了,在执行第二行代码时,aaa字符串对象在池中已存在,所以直接返回池中已存在的那个字符串对象。

String str="aaa"+"bbb"; 

还是只创建1个对象。由于常量字符串是在编译的时候就也被确定的,又因"aaa"和"bbb"都是常量,因此变量str的值在编译时就可以确定。这行代码编译后的与String str="aaabbb";是一样的,这与我们平时好像不太一样啊?一般使用“+”连接两个字符串都会产生另一个新的字符对象。下面我们看一下例子就明白了:

String str1 = "aaa";
String str2 = "bbb";
String str3 = "aaabbb";

String str4 = "aaa" + "bbb";//不会产生新的字符串对象
System.out.println(str3 == str4);//true

str4 = str1 + "bbb";//会产生新的字符串对象
System.out.println(str3 == str4);//false

str4 = str1 + str2;//会产生新的字符串对象
System.out.println(str3 == str4);//false

从上面例子我们就可以得出:使用“+”连接的两个字符串本身就是字面常量字符串时,如果池中存在这样连接后的字符串,则是不会重新创建对象,而是直接引用池中的字符串对象;如果“+”连接的两字符串中只要有一个不是字面常量串(即定义过的),是会产生新的字符串对象。
凡事也有例外,这个也不例外:如果“+”连接的字符串中两个或一个不是“字面常量”,但如果定义成常量字符串时,情况又有变化:

final String str1 = "aaa";
final String str2 = "bbb";
String str3 = "aaabbb";

/*
 * 因为str1与str2都定义成了常量,所以编译时就能确定,编译时就会将常量替换,等同于
 * str4 = "aaa"+"bbb",因此不产生新对象
 */
String str4 = str1 + str2;
System.out.println(str3 == str4);//true

但如果先定义final字符串,但未在定义处初始化,而初始化在块中,如下:
  

 //此时str1与str2相当于变量,而不是常,因为块是在运行时才能确定,在编译时不能确定
final static String str1;  
    final static String str2;  
    static {  
        str1 ="aaa";  
        str2 ="bbb";  
    }
    public static void main(String[] args){  
        String str3 = str1 + str2;
        String str4 ="aaabbb";  
        System.out.println(str3==str4);   //输出为false
    }  
String str=" ";与String str=new String();
str=" "会放入池中,但new String()不会放入池中。


String的intern()方法
“当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(该对象由 equals(Object) 方法确定),则返回池中的字符串;否则,将此 String 对象添加到池中,并且返回此 String 对象的引用。它遵循对于任何两个字符串 s 和 t,当且仅当 s.equals(t) 为 true 时,s.intern() == t.intern() 才为 true”,这是jdk文档原文注解。

现在我们回到最开头的那个例子,为什么String str=new String("aaa");会产生2个对象?一个是"aaa"又作为字符串构造函数的参数,但"aaa"自己就是一个字符串,在传进构造函数前就已创建了一个字符对象,实质上与就好比是第二个实例:String str="aaa"; ,它创建的字符串对象会放入到池中,并且引用的也是池中的那个字符串对象;另一个就是通过new String()构造函数创建的。所以new String("aaa")会产生两个对象,也就是说通过此种方式创建字符串对象时,会先将字符串参数对象放入对象池,然后另外创建一个字符串对象。

理解完new String("aaa")为什么会产生两个对象,我们再来看看new String(char value[])这样的方式创建时,又会不会把字符串对象放入到池中呢?答案是不会的。因为传递给构造函数的是一个字符数组,而不是像前面传递的是一个字面常量字符串参数那样,将字符串参数本身放入池中。那么我们现在如果证明new String(char value[])未将字符串对象放入池中,我们可以写一个简单的测试,运行时打开XP的任务管理器,查看操作系统的内存使用情况就可以初步确认:

int size = 10000000;
char c[] = new char[size];
for (int i = 0; i < size; i++) {
    c[i] = 'a';
}
//使用带字符数组参数构造函数创建字符串时,字符串对象不会放入字符串池
String str1 = new String(c);
System.out.println("String字符串对象创建完毕...");
Thread.sleep(5000);
str1.intern();//到里会看见内存增加
System.out.println("第一次调用intern()完毕...");
Thread.sleep(5000);
str1.intern();//再过5秒将看不到内存增长,因为池中有了,不会再放入,所以内存无变化
System.out.println("第二次调用intern()完毕...");
Thread.sleep(5000);

所以创建字符串对象放入并放入池中有二种方式:第一种就是直接使用字面常量定义字符串时,如 String str="aaa"; ,str会引用放入池中的对象;第二种就是使用带字符串参数的字符串构造函数,并且此时传入的参数值要是字符串常量形式,而不能是变量的形式,也就是说只能是 String str=new String("aaa");形式,而不能是先定义 String s = "aaa",然后再使用 String str=new String(s);来创建对象,new String(s);此时只创建一个对象,但如果池中不存在时我们可以使用intern方法将它放入池中。当然上面放入池中的前提是池中还不存在这些字符串对象。
其实,当我们仔细研究时,发现放入池中只实质上只存在一种时机,那就是:直接使用字面常量字符串时。上面所说的两种时机实质上就是直接使用了字面常的字符串而将其放入池中的。

上面多处提到了JVM中的堆栈,下面小结一下各自原作用:
栈用来保存基本类型与对象的引用的,基本型在创建前会查看Stack中是否已经有, 有则指向, 没有则创建。
String内部是以字符串数组来存储字符串的,因此可以认为与char[]等同, String a= "abc",首先在Heap中创一个对象,再到Stack中找char[]是否存在,有则指向该地址, 无则在Stack中创建数组。
new出来的都是在Heap中,堆是用于存储对象的。

分享到:
评论
5 楼 heluping000000 2016-09-28  
引用
String a= "abc",首先在Heap中创一个对象,再到Stack中找char[]是否存在,有则指向该地址, 无则在Stack中创建数组。
个人认为这句话不对。String a= "abc";是在常量池中创建字符串对象(如果在常量池中未找到"abc"的话),另外,java的数组是在heap中创建的。
4 楼 wzt3309 2016-07-28  
完全理解,比网上其他资料都要详细
3 楼 xuetuyic 2015-08-13  
确实写得很全
2 楼 comrd 2013-08-05  
对,真的不错。
1 楼 hanyu332 2010-12-08  
非常好,竟然没人收藏

相关推荐

    String s = new String(” a “) 到底产生几个对象?

    上图红色的这3个箭头,对于通过new产生一个字符串(”宜春”)时,会先去常量池中查找是否已经有了”宜春”对象,如果没有则在常量池中创建一个此字符串对象,然后堆中再创建一个常量池中此”宜春”对象的拷贝对象。...

    String类创建对象问题

    ### String类创建对象问题 #### 一、String 类与常量池、字符串池的基本概念 在 Java 中,`String` 类并不属于八种基本数据类型之一,而是作为一个对象存在。这意味着 `String` 对象默认值为 `null`。尽管如此,`...

    String创建对象

    以下是对`String`类创建对象的一些关键知识点的详细说明: 1. **字面量方式创建**: 在Java中,我们最常用的方法是通过字面量直接创建`String`对象,如`String str = "Hello"`。这种方式创建的`String`对象会存储...

    String对象创建问题

    当使用`new String()`构造函数创建`String`时,会在堆内存中创建一个新的对象。即使两个`new`操作的字符串值相同,它们也会分别创建独立的对象。例如: ```java String s3 = new String("hello"); String s4 = new ...

    创建string对象过程的内存分配:

    这种方式下,无论创建多少次相同的字符串字面量,JVM 只会在 **常量池** 中创建一个实际的对象,从而避免重复创建相同的对象,节省内存空间。示例如下: ```java String s1 = "abc"; String s2 = "abc"; System.out...

    c++中由类名(string)创建这个类对象(RTTI)

    总结来说,C++的RTTI机制提供了在运行时检查和转换类型的能力,但直接根据类名字符串创建对象通常需要借助设计模式(如工厂模式)或其他高级技术(如动态链接库和元类系统)。在实际开发中,合理利用这些工具可以...

    JAVA面试题解惑系列(二)——到底创建了几个String对象-JAVA程序员JAVA工程师面试必看.pdf,这是一份不错的文件

    当我们创建一个 String 对象时,JAVA 虚拟机(JVM)首先会在字符串池中查找是否已经存在了该对象,如果存在,则不再创建新的对象,直接返回已存在对象的引用;如果不存在,则创建新的对象,并将其加入字符串池中。 ...

    new String(\"abc\")创建几个对象的解释

    java面试的疑惑剪辑 博文链接:https://z-jq1015.iteye.com/blog/248599

    JNI处理hashmap,string等对象的操作

    对于String对象的处理,JNI提供了`NewStringUTF`函数来创建一个新的Java String对象,以及`GetStringUTFChars`和`ReleaseStringUTFChars`来获取和释放字符串的UTF-8表示。例如,你可能需要在本地代码中解析或修改...

    String对象详解(附代码)

    对String创建几个对象代码详解,包括字符串相加,对象相加等等

    Java中颜色的String和Color对象之间的互相转换

    例如,我们可以使用 `Method` 对象的 `invoke` 方法来调用颜色对象的 getter 方法,获取颜色对象的红、绿、蓝三个分量的值,然后根据这些值创建字符串形式的颜色。例如: ```java public static String Color2String...

    java中Object对象String对象的解析.pdf

    使用字面值赋值方式创建的 String 对象会被存储在 String 池中, String 池是一个特殊的存储区域,用于存储字符串常量。使用 new 关键字创建的 String 对象则会被存储在堆中。 在比较 String 对象时,需要使用 ...

    Java String对象的经典问题

    - **直接赋值**:这种方式会在**字符串常量池**(String Pool)中查找或创建一个对应的`String`对象,并将变量指向该对象。字符串常量池是Java堆的一部分,专门用来存储字符串字面量。如果字符串常量池中已经存在...

    String型的不可变性

    第一种形式用 new() 来创建对象的,它会存放在堆中,每调用一次就会创建一个新的对象。第二种形式是先在栈中创建一个对 String 类的对象引用变量 str,然后查找栈中有没有存放"abc",如果没有,则将"abc"存放进栈,...

    Java问题宝典2012版

    37、下面这条语句一共创建了多少个对象:String s="a"+"b"+"c"+"d"; 26 38、try {}里有一个return语句,那么紧跟在这个try后的finally {}里的code会不会被执行,什么时候被执行,在return前还是后? 27 39、下面的...

    10个Java经典的String面试题!.pdf

    创建了多少个对象?** 这行代码会创建两个对象。"abc"首先作为字面量在常量池中创建,然后通过`new`关键字在堆内存中创建一个新的String对象,指向"abc"的副本。 6. **String、StringBuffer、StringBuilder的区别...

    String对象的内存分析

    对于`c`,`new String("xyz")`会创建一个新的`String`对象在堆中。同样,`d`也会在堆中创建一个新的`String`对象,尽管常量池中已有"xyz"。 3. `String s1 = new String("xyz"); String s2 = new String("xyz"); ...

    StringtoList和StringtoMap和StringtoObject和StringtoArray

    如果JSON字符串代表的是一个自定义Java对象,你可以创建一个对应的类并使用Gson的`fromJson`方法将其转换: ```java class Fruit { private String name; private String color; // getters and setters... }...

    java四种创建对象的方式

    克隆方法创建对象是指利用对象自身的clone方法创建一个与原来对象内容相同的对象。这种方式不会调用原对象的构造方法。为了让一个类支持克隆操作,该类需要实现Cloneable接口。实现Cloneable接口的clone方法需要抛出...

Global site tag (gtag.js) - Google Analytics