String之常量池小结

1、String 常量池

String使用private final char value[ ]实现字符串的存储,也就是说String创建对象之后不能够再次修改此对象中存储的字符串内容,因而String类型是不可变的(immutable),因而String类是线程安全的。

其中字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价。JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化。为了减少在JVM中创建的字符串的数量,字符串类维护了一个字符串池,每当代码创建字符串常量时,JVM会首先检查字符串常量池。如果字符串已经存在池中,就返回池中的实例引用。如果字符串不在池中,就会实例化一个字符串并放到池中。Java能够进行这样的优化是因为字符串是不可变的,可以不用担心数据冲突进行共享


Note:常量池在java用于保存在编译期已确定的,已编译的class文件中的一份数据。它包括了关于类,方法,接口等中的常量,也包括字符串常量,如String s = "java"这种申明方式。常量池是在堆内存中的一块。常量池中除了包含代码中所定义的各种基本类型(如int、long等等)和对象型(如String及数组)的常量值外,还包含一些以文本形式出现的符号引用,比如:类和接口的全限定名、字段的名称和描述符、方法和名称和描述符。另外Byte,Short,Integer,Long,Character这5种整型的包装类也只是在对应值小于等于127时才可使用对象池(-128——127)具体可以参阅http://book.51cto.com/art/201202/317488.htm这里就不展开赘述了。


2、True OR False

首先得看下 几个对象???

相信很多 玩java的都做做类似 String s = new String("abc")这个语句创建了几个对象的题目。 这种题目主要就是为了考察对字符串对象的常量池掌握与否。其实上述的语句中是创建了2个对象:

第一个对象是"abc"字符串存储在常量池中

第二个对象在JAVA Heap中的 String 对象。这里不要混淆了s是放在栈里面的指向了Heap堆中的String对象

下面再看:
String str1 = new String("abc");
String str2 = "abc";
这里是两种的形式来创建,第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象。运行时期创建
而第二种是先在栈中创建一个对String类的对象引用变量str2,然后通过符号引用去字符串常量池里找有没有"abc",如果没有,则将"abc"存放进字符串常量池,并令str2指向”abc”,如果已经有”abc” 则直接令str2指向“abc”。编译期间完成

还有几个经常考的面试题: 
  String s1 = new String("s1") ;
  String s2 = new String("s1") ;
  上面创建了几个String对象?
  答案:3个 ,编译期Constant Pool中创建1个,运行期heap中创建2个.(用new创建的每new一次就在堆上创建一个对象,用引号创建的如果在常量池中已有就直接指向,不用创建)


比较类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==,下面用例子说明上面的理论。
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
可以看出str1和str2是指向同一个对象的。

String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false
用new的方式是生成不同的对象。每一次生成一个。

下面就是用几个例子说明来说明一下

        例1
  String s1 = "sss111";
  String s2 = "sss111";
  System.out.println(s1 == s2); //结果为true
  例2
  String s1 = new String("sss111");
  String s2 = "sss111";
  System.out.println(s1 == s2); //结果为false 
  例3
  String s1 = new String("111");
  String s2 = "sss111";
  String s3 = "sss" + "111";
  String s4 = "sss" + s1;
  System.out.println(s2 == s3); //true
  System.out.println(s2 == s4); //false
  System.out.println(s2 == s4.intern()); //true
  

  结果上面分析,总结如下:

   1.单独使用""引号创建的字符串都是常量,编译期就已经确定存储到String Pool中;

  2,使用new String("")创建的对象会存储到heap中,是运行期新创建的;

  3,使用只包含常量的字符串连接符如"aa" + "aa"创建的也是常量,编译期就能确定,已经确定存储到String Pool中;

  4,使用包含变量的字符串连接符如"aa" + s1创建的对象是运行期才创建的,存储在heap中;


3、 令人产生迷惑的intern

上面例子比较简单再看看这个呢?

【例1】

  String s1 = new String("sss111");
  s1=s1.intern();
  String s2 = "sss111";
  System.out.println(s1 == s2);  //true


(若s1=s1.intern();改为s1.intern则为false,由于仅仅做了查询和存放。)

搞清楚这个问题首先要知道intern是做什么的,当我们声明String常量时池往往有以下俩种方式:


    * 直接使用双引号声明出来的String对象会直接存储在常量池中。
    * 如果不是用双引号声明的String对象,可以使用String提供的intern方法。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中

可以在源码中看到(String.intern方法中看到),这个方法是一个 native 的方法,但注释写的非常明了。“如果常量池中存在当前字符串, 就会直接返回当前字符串. 如果常量池中没有此字符串, 会将此字符串放入常量池中后, 再返回”。

【例2】

来看一段代码:
public static void main(String[] args) {
    String s = new String("1");
    s.intern();
    String s2 = "1";
    System.out.println(s == s2);

    String s3 = new String("1") + new String("1");
    s3.intern();
    String s4 = "11";
    System.out.println(s3 == s4);
}
打印结果是

    * jdk6 下false false
    * jdk7 下false true

    *在第一段代码中,先看 s3和s4字符串。String s3 = new String("1") + new String("1");,这句代码中现在生成了2最终个对象,是字符串常量池中的“1” 和 JAVA Heap 中的 s3引用指向的对象。中间还有2个匿名的new String("1")我们不去讨论它们。此时s3引用对象内容是"11",但此时常量池中是没有 “11”对象的。
     接下来s3.intern();这一句代码,是将 s3中的“11”字符串放入 String 常量池中,因为此时常量池中不存在“11”字符串,因此常规做法是在 jdk6 在常量池中生成一个 "11" 的对象从而s指向的是堆中1,s2指向常量池中1,因而为false。关键点是 jdk7 中常量池,可以直接存储堆中的引用。这份引用指向 s3 引用的对象。 也就是说引用地址是相同的因而为true。









  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值