目录
连接符"+"
1.关于String的immutable性质。
另外补充一点intern()
包装类的常量池技术
连接符"+"
String a="a";String b="b";
String s="a"+"b"; sysytem.out.println(s=="ab")
//返回true。
//分析:JVM对于字符串常量”+“号连接,在程序编译器,JVM就将字符串常量的”+“连接优化为连接后的值,即如上s经编译器优化
//后在class中已经是"ab",即可在编译后字符串s的值已经确定下来,所以返回true。
String s=a+"b"; system.out.println(s=="ab");//返回false
/* JVM对于字符串引用进行“+”连接时是无法被编译优化成字符串常量在赋给s的,只有在程序运行期进行动态分配并将连接后的新地址赋给s。 */
将 String a 改为 final String a="a"后,system.out.println(s="ab");//返回true
//分析:对于final修饰的变量,它在编译时被解析为常量值的一个本地拷贝存储到自己的常量 池中或嵌入到它的字节码流中。所以 //此时的a + ”b“和"a" + "b"效果是一样的。故上面程序的结果为true。
关于”+“连接符效率低下的原因:
首先先了解有字符串引用时连接的内部原理。
String a="a";
String b="b";
String s=a+"b";
会被编译为:
s=new StringBuffer().append(a).append("b").toString();
在应用到循环中时
for(int i=1;i<100;i++) s+="a";
每做一次循环就会进行一次StringBuffer的创建与销毁。
解决方案:在循环外创建一个StringBuffer进行循环后在复制给s。可以省去n-1次创建和销毁对象的时间
速度差异:
1.关于String的immutable性质。
1.关于String的immutable性质。
由于String的实例一旦生成就不会在改变,所以会导致存在多个临时变量在常量池中 如s=”a"+"b" 则 a,b,ab都在常量池中,而StringBuffer时可变的,所以建议使用String buffer。
2.String中的final的用法与理解
final StringBuffer a=new StringBuffer("a");
final StringBuffer b = new StringBuffer("b");
a=b;//无法通过编译
a.append(b);//编译通过
由此可见final针对的是引用的值即内存地址,他迫使引用指向初始的对象,改变它的指向会报错,而它指向的对象发生变化,final是不负责的。
另外补充一点intern()
// String str2="李鑫"; //只要这一句放到intern前就返回false
String str1 = new String("李") + new String("鑫");
//System.out.println(str1.intern() == str1); 先进行intern则全返回true
System.out.println(str1 == "李鑫"); //判断时str1与指向李鑫的引用比较;如果"李鑫"="李鑫" 则不会在常量池中生成相应字符串
System.out.println(str1.intern() == str1);
System.out.println(str1.intern() == "李鑫");
//false ,false,true
//else
public static void main(String[] args) throws Exception {
String s1= new String("li")+new String("xin");
String s2 ="lixin";
s1.intern();
String s3 ="lixin";
String s4 ="lixin";
System.out.println(s1==s2);
System.out.println(s1==s3);
System.out.println(s1==s4);
//all false
}
大致理解:在1.6(不包含1.6)以后,若使用String.intern()时,该String在常量池不存在,则将这个String对象添加到常量池并返回指向这个String的引用str1。然后在创建一个字符串为该常量值时,会直接指向该引用。所以会相等。
相关源码:意思是常量池存在则直接返回string,不存在则在常量池中引用该String,为什么可以做到呢,因为常量池在jdk1.7以后就搬到了堆中,可以直接引用,而不是生成。
When the intern method is invoked, if the pool already contains a
string equal to this {@code String} object as determined by
the {@link #equals(Object)} method, then the string from the pool is
returned. Otherwise, this {@code String} object is added to the
pool and a reference to this {@code String} object is returned.
具体原因参考博客: (Intern)
包装类的常量池技术
建议参考:
总结:
- 除了两个包装类Long和Double 没有实现这个缓存技术,其它的包装类均实现了它
- Integer实现范围只有-128~127
- == 这个运算在不出现算数运算符(+,-...)的情况下 不会自动拆箱,所以i1 和 i 2它们不是数值进行的比较,仍然是比较地址是否指向同一块内存
第一点示例:
Integer i1 = 40;
Integer i2 = 40;
Double i3 = 40.0;
Double i4 = 40.0;
System.out.println("i1=i2 " + (i1 == i2));//true
System.out.println("i3=i4 " + (i3 == i4));//false
第2,3点示例
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));
----结果----
(1)i1=i2 true
(2)i1=i2+i3 true
(3)i1=i4 false
(4)i4=i5 false
(5)i4=i5+i6 true
(6)40=i5+i6 true
Integer i1 = 400;
Integer i2 = 400;
Integer i3 = 0;
Integer i4 = new Integer(400);
Integer i5 = new Integer(400);
Integer i6 = new Integer(0);
Integer i7 = 1;
Integer i8 = 2;
Integer i9 = 3;
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("400=i5+i6 " + (400 == i5 + i6));
----结果----
i1=i2 false
i1=i2+i3 true
i1=i4 false
i4=i5 false
i4=i5+i6 true
400=i5+i6 true