转自:http://www.cdtarena.com/javapx/201307/9088.html

  最近在工作的时候,一句再正常不过的代码String a = “hello” + “world”;被改成了new StringBuilder().append(“hello”).append(“world”);当时就比较疑惑这样做的好处,后来到网上查找了一番之后才清楚这与Java中的字符串驻留机制有关,那么什么是驻留呢?

  顾名思义,驻留就是在内存中保留(在Java中,我们通常称驻留对象的地方为驻留池,不过它也是内存的一部分),它不仅存在于Java中,在C#中同样存在。那么我就写几个例子来讲解什么叫Java中字符串的驻留:

public class test {
  public static void main(String[] args) {
      String a = "abc";
      String b = "abc";
      System.out.println(a == b);  // true
      String c = new String("abc");
      System.out.println(a == c);  // false
      System.out.println(a.equals()); // true 
  }
}

  上面这段代码在执行完String a = “abc”这一句的时候会在内存中创建一个值为abc的String类型对象。当执行下一句代码,即String b = “abc”的时候,java会首先去驻留池里面查找是否有值为abc的字符串对象,如果有就让b引用执行那个对象,如果没有就新创建一个并且将其存放在驻留池中。所以,不难理解,当程序执行到第三句话的时候会返回true,我们知道==在java中比较的是对象的引用指向的对象的内存首地址是否一样,而a和b指向的是同一个对象,所以会返回true。继续往下走,当程序执行到String c = new String(“abc”)这句话的时候,java做的事包括: 检查abc这个字符串对象是否在驻留池中,如果存在就把它当做值,然后再在堆上创建一个String类型的对象放到堆中(我们都知道在java中对象是放在堆中,对象的引用是存放在栈中)。所以这句话其实可能创建了2个对象(如果abc已经在驻留池中了,就只是在堆中创建了一个对象)。同时通过new String()创建出来的字符串对象是不会被放到驻留池中的。你也许会想,有没有一种方法让我把在堆中创建的对象放到驻留池中去呢?答案是有的!java提供了一个方法叫做intern(),如果执行c.intern(),会首先把c指向的对象放到驻留池中,然后返回指向这个对象的引用。那么,以下代码会输出什么呢?

public class test {
  public static void main(String[] args) {
      String a = "abc";
      String b = new String("abc");
      System.out.println(a == b.intern());  // true
      System.out.println(a == b);  // false
  }
}

  当然是true!不过它的执行过程还是值得说一下的,重点在b.intern();这句话上。经过上面的讲解你也许会想过程应该是首先到堆中创建一个值为abc的String对象,然后将这个对象放到驻留池中。那么如果驻留池中已经存在值为abc的字符串对象了呢?那么b.intern会直接返回驻留池中的对象,所以这里会返回true。继续向下执行,System.out.println(a == b);会返回false,因为在执行b.intern();这句话的时候,实际上是直接返回了驻留池中的对象,所以对原本b指向的堆中的对象没有影响,所以a == b会返回false。

  我通过上面这个例子简单讲解了java中的字符串驻留,那么现在回到文章开始部分的疑惑去,为什么使用StringBuilder而不是简单地使用”+”来连接字符串呢?经过上面的讲解,你可能会猜测StringBuilder用了字符串驻留,而”+”不是。恭喜你,你答对了,加10分。但是你也许并不知道使用”+”的时候tricky的地方在哪里。继续往下看。

  原因在于使用+连接字符串每次都生成新的对象,而且是在堆内存上进行,而堆内存速度比较慢(相对而言),那么再大量连接字符串时直接+是不可取的,当然需要一种效率高的方法。Java提供的StringBuffer和StringBuilder就是解决这个问题的。区别是前者是线程安全的而后者是非线程安全的。所以促使我写这篇博客的问题的原因就找到了。此外,值得注意的一点是,驻留池是不会被GC回收的,它会在程序运行期间一直保留。

  最后我还想再说点题外话,请看下面这段程序:

public class test {
  public static void main(String[] args) {
      String a = "a";
      String b = "b";
      String c = "ab";
      String d = "a" + "b";
      String e = a + "b";
      String f = a + b;
      System.out.println(c == d);  // true
      System.out.println(c == e);  // false
      System.out.println(c == f);  // false
      System.out.println(d == e);  // false
      System.out.println(d == f);  // false
      System.out.println(e == f);  // false
  }
}

  c == d输出true,因为c和d都是字符串常量,他们的值在编译时就确定了。而所有涉及到引用的地方都是在运行时才确定值的,所有下面会全部输出为false。

Java中的字符串驻留的更多相关文章

  1. Java中的字符串驻留(String Interning)

    1. 首先String不属于8种基本数据类型,String是一个对象. 因为对象的默认值是null,所以String的默认值也是null:但它又是一种特殊的对象,有其它对象没有的一些特性. 2. ne ...

  2. [原创]Java中的字符串比较,按照使用习惯进行比较

    java中的字符串比较一般可以采用compareTo函数,如果a.compareTo(b)返回的是小于0的数,那么说明a的unicode编码值小于b的unicode编码值. 但是很多情况下,我们开发一 ...

  3. 理解Java中的字符串类型

    1.Java内置对字符串的支持: 所谓的内置支持,即不用像C语言通过char指针实现字符串类型,并且Java的字符串编码是符合Unicode编码标准,这也意味着不用像C++那样通过使用string和w ...

  4. Java中的字符串拼接

    Java中的字符串拼接 1.设计源码 /** * @Title:IndexOf.java * @Package:com.you.freemarker.model * @Description: * @ ...

  5. java中,字符串类型的时间数据怎样转换成date类型。

    将字符串类型的时间转换成date类型可以使用SimpleDateFormat来转换,具体方法如下:1.定义一个字符串类型的时间:2.创建一个SimpleDateFormat对象并设置格式:3.最后使用 ...

  6. Android学习笔记----Java中的字符串比较

    用习惯了C#.C++,在做字符串比较时想当然地使用如下语句: string str1 = "abcd", str2 = "abcd"; if(str1==str ...

  7. 转载:Java中的字符串常量池详细介绍

    引用自:http://blog.csdn.net/langhong8/article/details/50938041 这篇文章主要介绍了Java中的字符串常量池详细介绍,JVM为了减少字符串对象的重 ...

  8. 《Java入门第三季》第二章 认识 Java 中的字符串

    什么是 Java 中的字符串.1.在Java的世界里,字符串被作为String类型的对象处理. 2.通用的初始化的方式:String s = new String("i love you & ...

  9. 为什么Java中的字符串是不可变的?

    原文链接:https://www.programcreek.com/2013/04/why-string-is-immutable-in-java/ java字符串是不可变的.不可变类只是一个不能修改 ...

随机推荐

  1. C# GDI+发生一般性错误(A generic error occurred in GDI+))

    解决思路: 1. 因为 .net GDI+ 是对底层 的封装. 所以可以尝试用 Marshal.GetLastWin32Error();函数获得底层错误代码. try{ image.Save(file ...

  2. LeetCode OJ-- Single Number II **@

    有一列数,其中有1个数出现了1次,其它数都出现了3次,求这个数. class Solution { public: int singleNumber(int A[], int n) { ) ; ; ; ...

  3. 怎样成为全栈工程师(Full Stack Developer)?

    "Facebook 工程师说 Facebook 只招 full stack engineer,那么 Facebook engineer 都是怎样的人啦."? 具体经验不重要,重要的 ...

  4. c++ primer复习(四)

    1 标准库容器 顺序容器:vector.list.deque 容器适配器:stack.queue.priority_queue 2 容器元素类型约束: 容器元素类型必须支持复制和赋值,因为容器存放的都 ...

  5. Spring 创建 IOC 容器 ClassPathXmlApplicationContext 类

    一.ClassPathXmlApplicationContext 类的作用 在 Java 环境中使用 Spring 时,需要用 new ClassPathXmlApplicationContext(c ...

  6. JS input file 转base64 JS图片预览

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  7. Spark RPC框架源码分析(二)RPC运行时序

    前情提要: Spark RPC框架源码分析(一)简述 一. Spark RPC概述 上一篇我们已经说明了Spark RPC框架的一个简单例子,Spark RPC相关的两个编程模型,Actor模型和Re ...

  8. 痞子衡嵌入式:飞思卡尔i.MX RT系列MCU启动那些事(3)- Serial Downloader模式(sdphost/MfgTool)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是飞思卡尔i.MX RT系列MCU的Serial Downloader模式. 在上一篇文章 Boot配置(BOOT Pin, eFUSE) ...

  9. supervisord支持扩展(xml RPC API &amp; Third Party Applications and Libraries)

    XML-RPC API Documentation http://www.supervisord.org/api.html Third Party Applications and Libraries ...

  10. 8、Dockerfile详解

    除了init之外,每一个进程都应该是其他进程的子进程(init是内核启动的),当手动启动nginx时,那么这个nginx就以shell子进程存在.当打开一个命令行提示符时,这个就相当于在运行一个she ...