博主能力有限 若有错误欢迎指正
什么是常量
常量就是使用final修饰的变量 给值后无法修改
常量池
常量池主要用于存放两大类常量:字面量(Literal)和符号引用量(Symbolic References)。
字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值等,
符号引用则属于编译原理方面的概念,包括了如下三种类型的常量:
- 类和接口的全限定名
- 字段名称和描述符
- 方法名称和描述符。
(《深入理解Java虚拟机》)
常量池中字符串具有唯一性所以节省空间
常量池的内容在编译时期便已经确定,但是在运行时可以加入新的。例如String的intern方法
Java的基本类型包装类除了float以及double其余以及String都实现的常量池技术。
验证
String s = "sss";
String ss = "sss";
Integer i = 5;
Integer ii = 5;
Byte b = 1;
Byte bb = 1;
Boolean bo = false;
Boolean bbo = false;
Character c = 'a';
Character cc = 'a';
Long l = (long) 1.5;
Long ll = (long) 1.5;
Float f = (float) 1.5;
Float ff = (float) 1.5;
Double d = 1.5;
Double dd = 1.5;
//true
System.out.println(s == ss);
//true
System.out.println(i == ii);
//true
System.out.println(b == bb);
//true
System.out.println(bo == bbo);
//true
System.out.println(c == cc);
//true
System.out.println(l == ll);
//false
System.out.println(f == ff);
//false
System.out.println(d == dd);
聊到常量池就得说说 == 比较符
== 比较的是对象的引用(《Java编程思想》)
由常量池引起的现象
String s = "sss";
String ss = "sss";
String sss = new String("sss");
System.out.println(s == ss); //结果是true
System.out.println(s == sss);//结果是false
解析
1) s==ss 为true是因为s与ss都是对字面量的引用,而字面量的应用存放在常量池
字符串“sss”存储进入常量池时会先检查常量池中是否已经存在该字符串(使用的是String的equal的方法(Java6.0 API)),若存在则直接返回该字符串的引用。所以s与ss其实是同一个引用指向常量池
2)s==sss 为false的原因
首先来简述一下String对象产生过程
虚拟机接收到new关键字 首先去检查这个指令的参数是否能够在常量池中定位到一个类的符号引用,并检查该符号是否被加载,解析,和初始化过。(《深入理解Java虚拟机》)
new String(“sss”)时先会检查”sss”这个字符串在常量池中是否存在,若不存在就加进去,然后在堆中创建该对象的实体,并且返回其在堆中的引用,所以这个语句创建了两个对象
new String() 创建的字符串不是常量,不能在编译期就确定,所以new String() 创建的字符串不放入常量池中,它们有自己的地址空间。
特殊例子 字符串拼接
特例一
String s = "java";
String ss = "ja" + "va";
//true
System.out.println(s == ss);
当一个字符串由多个字符串常量连接而成时,它自己肯定也是字符串常量,所以ss也同样在编译期就被解析为一个字符串常量,所以ss也是常量池中”java”的一个引用。
特例二
String s = "java";
String ss = "ja" + new String("va");
// false
System.out.println(s == ss);
new String(“va”)的值在编译阶段无法确定所以ss的值也无法确定 因此 s与ss不是同一个对象的引用。
到此浅析常量池差不多结束了