Java系列3 常量池和字符串

参考:https://www.jianshu.com/p/c7f47de2ee80

一.JVM相关概念

1.class文件中的常量池
  • 存放 字面量(Literal) + 符号引用量(Symbolic References)
  • 字面量 = Java语言层面常量(如文本字符串,声明为final的常量值)
  • 符号引用量 = 类和接口的全限定名 + 字段名称和描述符 + 方法名称和描述符
2.方法区中的运行时常量池
  • class文件中常量池的内容将在类加载后进入方法区的运行常量池
  • 运行常量池具备动态性,即运行期间也可将新常量放入常量池中
3.常量池的好处
  • 节省内存空间:实现对象的共享
  • 节省运行时间:避免频繁创建和销毁对象

二.8种基本类型的包装类和常量池

  • 包装类Byte,Short,Integer,Long,Character,Boolean实现了常量池技术
    这5种包装类在[-128,127]范围内在常量池存储值,超出此范围则仍然创建新对象
  Integer i1 = 40;
  Integer i2 = 40;
  System.out.println(i1==i2);  //输出TRUE

  Integer i1 = 400;
  Integer i2 = 400;
  System.out.println(i1==i2);//输出false
  • 浮点数包装类Float,Double没有实现常量池技术
  • 常量池应用场景
    1)Integer i1=40;
    Java在编译的时候会直接将代码封装成Integer i1=Integer.valueOf(40);从而使用常量池中的对象
    2)Integer i1 = new Integer(40);
    这种情况下会创建新的对象
  Integer i1 = 40;
  Integer i2 = new Integer(40);
  System.out.println(i1==i2);    //输出false
  • 示例
    判断以下语句的输出结果
Integer i1 = 40;
Integer i2 = 40;
Integer i3 = 0;
Integer i4 = new Integer(40);
Integer i5 = new Integer(40);
Integer i6 = new Integer(0);

System.out.println("i1 = i2    " + (i1 == i2));
System.out.println("i1 = i2 + i3  " + (i1 == i2 + i3 ));
System.out.println("i1 = i4  " + ( i1 == i4 ));  
System.out.println("i4 = i5  " + ( i4 == i5 ));
System.out.println("i4 = i5 + i6  " + ( i4 == i5 + i6 ));
System.out.println("40 = i5 + i6  " + ( 40 == i5 + i6 ));

//输出结果为

i1 = i2    true
i1 = i2 + i3    true
i1 = i4    false
i4 = i5    false
i4 = i5 + i6    true
40 = i5 + i6    true

注意:
语句 i4 = i5 + i6
+ 操作符不适用Integer对象,故首先i5和i6自动拆箱完成数值相加,表达式变为 i4 == 40
Integer对象无法与数值进行直接比较,所以i4自动拆箱转为int类型,表达式变为 40 == 40,返回真

三.String和常量池

1.String对象的两种创建方式
  • 不使用new,编译时创建,存于字符串常量池中:
  • 使用new运行时创建,存于
      String str1 = "abcd";          //在栈中创建引用str,然后搜索字符串常量池
                                                //如果常量池有“abc",则令str指向常量池的"abc";否则则将"abc"放入常量池
      String str2 = new String("abcd"); 
      System.out.println(str1==str2);//false
  • 使用new时创建的是不同对象,不使用new则是多个引用指向常量池里的同一个值:
String str1="abc";
String str2="abc";
System.out.println(str1==str2);  //true

String str3=new String("abc");
String str4=new String("abc"):
System.out.println(Str3==str4);  //false
2.字符串连接 “+” 和字符串不可变性
  • 只有使用引号包含文本的方式创建的String对象间使用“+”连接产生的新对象才被加入字符串常量池
  String str1 = "str";
  String str2 = "ing";
  
  String str3 = "str" + "ing";
  String str4 = str1 + str2;
  System.out.println(str3 == str4);//false  str4不是 引号包含文本 的方式创建的String对象
  
  String str5 = "string";
  System.out.println(str3 == str5);//true

Java字符串拼接参考

  • String实例一旦生成将不会改变,直接使用String拼接将产生大量临时变量
String str = "ki" + "ll" + "until" + "win";

上述语句将生成3个临时变量
1)临时变量kill
2)临时变量killuntil
3)临时变量killuntilwin

  • StringBuilder和StringBuffer
    1)StringBuilder线程不安全,StringBuffer线程安全
    2)性能上:StringBuilder > StringBuffer > String
  • 定义为final的字符串特例
public static final String A = "ab"; // 常量A
public static final String B = "cd"; // 常量B
public static void main(String[] args) {
     String s = A + B;  // 将两个常量用+连接对s进行初始化 
     String t = "abcd";   
     System.out.println(s == t);  //true
}

A和B都是常量,值是固定的,因此s的值也是固定的,它在类被编译时就已经确定了

public static final String A; // 常量A
public static final String B;    // 常量B
static {   
     A = "ab";   
     B = "cd";   
 }   
 public static void main(String[] args) {   
     String s = A + B;       // 将两个常量用+连接对s进行初始化   
     String t = "abcd";   
     System.out.println(s == t);   //false
 } 

A和B虽然被定义为常量,但是它们都没有马上被赋值。在运算出s的值之前,他们何时被赋值,以及被赋予什么样的值,都是个变数。因此A和B在被赋值之前,性质类似于一个变量。那么s就不能在编译期被确定,而只能在运行时被创建了。

3. String s1 = new String("xyz");创建了几个对象?
  • 2个
  • "xyz"在类加载时就已经创建并放在全局共享的字符串常量池中
  • 代码运行时会将常量池中的这个对象复制到heap,并把heap中的这个对象的引用交给s1
4.java.lang.String.intern()体现运行时常量池的动态性
  • 运行时常量池相对于Class文件常量池的一个重要特征是具备动态性
  • 即Java语言并不要求常量一定只有编译期才能产生,运行期间也可能将新的常量放入池中
  • String的intern()方法会查找在常量池中是否存在一份equal相等的字符串,如果有则返回该字符串的引用,如果没有则添加自己的字符串进入常量池
public static void main(String[] args) {    
      String s1 = new String("计算机");
      String s2 = s1.intern();
      String s3 = "计算机";
      System.out.println("s1 == s2? " + (s1 == s2));
      System.out.println("s3 == s2? " + (s3 == s2));
  }

s1 == s2? false
s3 == s2? true
5.String常量池的跨包适用
public class Test {
 public static void main(String[] args) {   
      String hello = "Hello", lo = "lo";
      System.out.println((hello == "Hello") + " ");
      System.out.println((Other.hello == hello) + " ");
      System.out.println((other.Other.hello == hello) + " ");
      System.out.println((hello == ("Hel"+"lo")) + " ");
      System.out.println((hello == ("Hel"+lo)) + " ");
      System.out.println(hello == ("Hel"+lo).intern());
 }   
}

class Other {
 static String hello = "Hello"; 
}

package other;
public class Other {
 public static String hello = "Hello"; 
}

结果为:

true true true true false true```
在同包同类下,引用自同一String对象.
在同包不同类下,引用自同一String对象.
在不同包不同类下,依然引用自同一String对象.
在编译成.class时能够识别为同一字符串的,自动优化成常量,引用自同一String对象.
在运行时创建的字符串具有独立的内存地址,所以不引用自同一String对象.

无情最是台城柳,依旧烟笼十里堤

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容

  • 相关概念 常量池的定义常量池(constant pool):指的是在编译期被确定,并被保存在已编译的.class文...
    snoweek阅读 795评论 0 4
  • 一.相关概念 什么是常量用final修饰的成员变量表示常量,值一旦给定就无法改变!final修饰的变量有三种:静态...
    梦工厂阅读 58,062评论 38 277
  • Java是一种可以撰写跨平台应用软件的面向对象的程序设计语言。Java 技术具有卓越的通用性、高效性、平台移植性和...
    Java小辰阅读 390评论 1 4
  • 结果: 结果分析: 1.i和i0均是普通类型(int)的变量,所以数据直接存储在栈中,而栈有一个很重要的特性:栈中...
    撑起头顶的天阅读 358评论 0 0
  • 学习和使用java已经有4年多了,反省一下,太多关注了实际的应用层面,对某些基础的东西没有特别理解透彻。好记性不如...
    锦书诗词阅读 1,668评论 4 26