1. String str=new String("aaa");

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

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

  1. String str="aaa";

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

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

  1. String str1="aaa";
  2. String str2="aaa";

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

  1. String str="aaa"+"bbb";

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

  1. String str1 = "aaa";
  2. String str2 = "bbb";
  3. String str3 = "aaabbb";
  4. String str4 = "aaa" + "bbb";//不会产生新的字符串对象
  5. System.out.println(str3 == str4);//true
  6. str4 = str1 + "bbb";//会产生新的字符串对象
  7. System.out.println(str3 == str4);//false
  8. str4 = str1 + str2;//会产生新的字符串对象
  9. System.out.println(str3 == str4);//false

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

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

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

  1. //此时str1与str2相当于变量,而不是常,因为块是在运行时才能确定,在编译时不能确定
  2. final static String str1;
  3. final static String str2;
  4. static {
  5. str1 ="aaa";
  6. str2 ="bbb";
  7. }
  8. public static void main(String[] args){
  9. String str3 = str1 + str2;
  10. String str4 ="aaabbb";
  11. System.out.println(str3==str4);   //输出为false
  12. }
  13. String str=" ";与String str=new String();
  14. 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的任务管理器,查看操作系统的内存使用情况就可以初步确认:

  1. int size = 10000000;
  2. char c[] = new char[size];
  3. for (int i = 0; i < size; i++) {
  4. c[i] = 'a';
  5. }
  6. //使用带字符数组参数构造函数创建字符串时,字符串对象不会放入字符串池
  7. String str1 = new String(c);
  8. System.out.println("String字符串对象创建完毕...");
  9. Thread.sleep(5000);
  10. str1.intern();//到里会看见内存增加
  11. System.out.println("第一次调用intern()完毕...");
  12. Thread.sleep(5000);
  13. str1.intern();//再过5秒将看不到内存增长,因为池中有了,不会再放入,所以内存无变化
  14. System.out.println("第二次调用intern()完毕...");
  15. 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中,堆是用于存储对象的。

String,创建对象问题的更多相关文章

  1. 关于一段有趣代码引出的String创建对象的解释

    通常来说,我们认为hashCode不相同就为不同的对象.就这样由一段代码引发了一场讨论,代码如下: @Test public void stringCompare() { String s1 = &q ...

  2. java中String创建对象分析(转)

    String str=new String("abc");   紧接着这段代码之后的往往是这个问题,那就是这行代码究竟创建了几个String对象呢? 相信大家对这道题并不陌生,答案 ...

  3. String创建对象的个数--西北狼

    public class StringDemo { /** * @param args */ public static void main(String[] args) { // TODO Auto ...

  4. Java中string 创建对象时 “”和null的差别

    null和""的差别 问题一: null和""的差别 String s=null; string.trim()就会抛出为空的exception String s ...

  5. Java中String直接复制和new String创建对象的区别以及equals和==的区别和效率对比

    编程语言中的问题很多时候我们看似懂,实际不懂,因为很多时候看不到更多的情况,从而缺少对这些看不到的情况的认知和解释. 今天mark一下String和new String()的区别.其实很简单. Str ...

  6. java代码String创建对象数组,进行排序

    总结:String是一个类.对于字串大小比较用方法:compareTo() package com.da.ima2; public class gh { public static void main ...

  7. Javascript String类的属性及方法

    String 类 Attribute and method anchor()              创建一个<a>标签的实例,将其name属性设置为被传递给此方法的字符串 big()  ...

  8. String类详解

    看了很多文章事实证明之前的通过new创建String对象只有一个是错误的,实际上创建一个或者创建两个对象,一个在堆区,一个在常量池,当常量池中已经存在就不会创建.看了一篇非常好的文章http://ww ...

  9. String之常量池小结

    1.String 常量池 String使用private final char value[ ]实现字符串的存储,也就是说String创建对象之后不能够再次修改此对象中存储的字符串内容,因而Strin ...

  10. String对象常量池特性对synchronized对象的影响

    一 .什么是String的常量池特性 对于字符串对象有两种创建方法,如下: 直接赋值法: String str1="直接赋值创建字符串"; 创建对象法: String str2=n ...

随机推荐

  1. 默认ttl参考

    UNIX 及类 UNIX操作系统 ICMP 回显应答的 TTL 字段值为 255 Compaq Tru64 5.0 ICMP 回显应答的 TTL 字段值为 64 WINXP-32bit 回显应答的 T ...

  2. Django - Form和ModelForm

    [TOC] 一. form介绍 1.生成页面可用的HTML标签 2. 提供input可以提交数据 3. 对用户提交的数据进行校验 4. 保留上次输入内容 5. 提供错误信息 二. 普通方式书写注册功能 ...

  3. MySQL之SQL优化详解(一)

    目录 慢查询日志 1. 慢查询日志开启 2. 慢查询日志设置与查看 3.日志分析工具mysqldumpslow   序言: 在我面试很多人的过程中,很多人谈到SQL优化都头头是道,建索引,explai ...

  4. 关于PyQt5,在pycharm上的安装步骤及使用技巧

    前序 之前学习了一款GUI图形界面设计的Tkinter库,但是经大佬的介绍,PyQT5全宇宙最强,一脸的苦笑 毫不犹豫的选择转战PyQT5,在学习之前需要先安装一些必须程序,在一番查阅后,发现PyQt ...

  5. 【郑轻邀请赛 G】密室逃脱

    [题目链接]:https://acm.zzuli.edu.cn/zzuliacm/problem.php?id=2133 [题意] [题解] 考虑每一个二进制数的最高位->第i位; 肯定是1(这 ...

  6. Spring Boot开发HTTPS协议的REST接口

    Spring Boot开发HTTP的REST接口流程在前文中已经描述过,见<SpringBoot开发REST接口>. 如需要支持HTTPS,只需要在如上基础上进行设置.修改/resourc ...

  7. hdu_1020_Encoding_201310172120

    Encoding Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total S ...

  8. Linux排查java程序占用cpu过高的线程代码

    分几步骤: 1.通过top,查出占用CPU过高的java进程 ,比如: pid :6666 2.通过ps -mp 6666 -o THREAD,tid,time| sort -n -k1 -r 查看此 ...

  9. qt on android之GPS信号的获取

    0.      写在最前面 本人參考安晓辉大侠的一篇博文后.做了Qt on android的GSP相关的实验.为了后面不时之需.故而记录下来. 1.      Qt on Android GPS系统流 ...

  10. 初触Python,关于pyquery解析html(百度贴吧)

    一直听同事说Python是个奇妙的语言,上周在逛知乎的时候深受这个话题的启示. 能利用爬虫技术做到哪些非常酷非常有趣非常实用的事情? 先是说到IDE的选择,作为python新人,尽管知道mac终端自带 ...