String初探

来源:互联网 发布:java微信支付查询订单 编辑:程序博客网 时间:2024/05/21 17:45

private final char value[];

value只是stack上的一个引用,数组的本体结构在heap堆,final修饰了value,只是value的引用地址不能变,而堆里array本身数据是可以变的。(如value[1]是可变的)

final int value[]={1,2,3};

value[1]=100;  //不会出错  value[]={1,100,3};

String是不变的关键,是在后面所有String的方法里很小心的没有去动Array里的元素,没有暴露内部成员字段。private final char value[] 这里,private的私有访问权限的作用比final大。而且设计师还很小心的把整个String设成final禁止继承,皮面被其他人继承后破坏。所以String是不可变的关键在底层的实现,而不是一个final。
String str="hello";
str+="world";
系统其实创建了一个新的对象,把str的指向改了,指向新的对象,"hello"就变成了垃圾。


String str=new String("abc");

创建了1个对象(1.new   "abc"在字符串池中直接将它的引用赋给str 通过String的equals方法来判断是否存在,若不存在,先创建字符常量再将它加入到字符串池中)或2个对象(1.new  2."abc"字符常量 )

String str="abc";

可能没创建对象也有可能创建了1个对象("abc"字符常量


在字符串池没字符的情况下,创建String对象的个数

String str="abc";1

String a="abc"; String b="abc";1

String a="ab"+"c";3  

String str=new String("abc");2


String s1="abc";    在常量池中

String s2="ab"+"c";

String s3=new String("abc");

s1==s2true在常量池中

s1==s3false  堆中

只有使用引号包含文本的方式创建的String对象之间使用“+”连接产生的新对象才会被加入字符串池中。对于所有包含new方式新建对象(包括null)的“+”连接表达式,它所产生的新对象都不会被加入字符串池中,使用引号包含文本的方式创建String能提高效率。


String str1="abc";

String str2=new String("abc");

String str3=new String("abc").intern();

str1==str2  false

str1==str3true 

当调用intern方法时,如果池中已经包含一个等于此String对象的字符串(equals()确定),则返回池中的字符串,否则,将此String对象添加到常量池中,并返回此String对象的引用。对于两任意的字符串s和t,当且仅当s.equals(t)为true时,s.intern()==t.intern()为true.

存在于.class文件中的常量池,在运行期间被jvm装载,并且可以扩充。String的intern()方法就是扩充常量池的一个方法


String str1="abc";

String str2="ab";

String str3="c";

String str4="ab"+"c";

String str5=str2+"c";

str1==str2+str3false

str1==str4true

str1==str5false

str2+str3和str2+"c"都涉及到变量(不是常量)的相加,所以会生成新的对象。String内部拼接是通过StringBuilder来实现的,先new一个StringBuilder,再append(str2)、append("c"),然后通过toString()来返回。

在使用"+"对字符串连接时,JVM会尽可能多的直接把(左边开始)字符串常量连接起来(中间的不自动拼接)

String s="aa"+"bb"+a1+"cc"+"dd"+a2;  

实质过程:String s=new StringBuilder("aabb").append(a1).append("cc").append("dd").append("a2").toString();


String s1 = "a1"; 
String s2 = "a" + 1;
s1 == s2;    true    1是常量,经编译器优化,在编译期间字符串常量的值就确定下来


String str1="abc";   

String str2="def";   

String str3=str1+str2;

str3=="abcdef";  false

JVM对String str="abc"对象放在常量池中是在编译时做的,字符串的字面拼接("ab"+"cd")也是在编译期完成的(操作得到"abcd"常量直接放入字符串池),而String str3=str1+str2是在运行时刻才能知道的。new对象也是在运行时才做的。总结来说就是:字面量"+"拼接是在编译期间进行的,拼接后的字符串存放在字符串池中;而字符串引用的"+"拼接运算实在运行时进行的,新创建的字符串存放在堆中。而这段代码总共创建了5个对象,字符串池中两个、堆中三个。+运算符会在堆中建立来两个String对象,这两个对象的值分别是"abc"和"def",也就是说从字符串池中复制这两个值,然后在堆中创建两个对象,然后再建立对象str3,然后将"abcdef"的堆地址赋给str3。

new创建字符串时首先查看池中是否有相同值的字符串,如果有,则拷贝一份到堆中,然后返回堆中的地址;如果池中没有,则在堆中创建一份,然后返回堆中的地址。对于最后拼接生成的对象是在堆中的,JVM不会将它拷贝到池中,这样会使堆中的对象都是池的子集,浪费内存。

步骤:1)栈中开辟一块中间存放引用str1,str1指向池中String常量"abc"。2)栈中开辟一块中间存放引用str2,str2指向池中String常量"def"。3)栈中开辟一块中间存放引用str3。4)str1 + str2通过StringBuilder的最后一步toString()方法还原一个新的String对象"abcdef",因此堆中开辟一块空间存放此对象。5)引用str3指向堆中(str1 + str2)所还原的新String对象。 6)str3指向的对象在堆中,而常量"abcdef"在池中,输出为false。

String str1="abc";   

final String str2="ab";   

String str3=str2+"c";

str1==str3;  true

对于final修饰的变量,在编译时作为一个常量解析


private static String getS1() {  
    return "b";   
}

String s1 = "ab"; 

final String s2 = getS1(); 

String s3 = "a" + s1;

s1 == s3;false 

这里面虽然将s2用final修饰了,但是由于其赋值是通过方法调用返回的,那么它的值只能在运行期间确定。
总结:对于字符串的拼接,若在编译期间就能确定的,JVM就会直接将其拼接并且放入常量池中;在编译期间不能确定的(在运行期间确定),新创建字符串在堆中


创建对象的两种方式:  每个对象都是某个类(class)的一个实例(instance)

1.通过new创建String存储到heap中,运行期创建

2.利用反射机制创建对象

 使用Class.forName来动态加载类,加载完毕后在调用Class类的newInstance静态方法来实例化对象

3.String的引号内包含文本 String str="abc";存储到String Pool中,编译期存储



字符串常量池的优缺点

每当我们创建字符串常量时,JVM会先检查字符串常量池,如果该字符已存在常量池中,那么就直接返回常量池中的实例引用。

Java中的常量池分为静态常量池和运行时常量池

静态常量池,即*.class文件中的常量池,class文件中的常量池不仅仅包含字符串(数字)字面量,还包含类、方法的信息,占用class文件绝大部分空间。 运行时常量池,则是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池。
优点:避免了相同内容字符串的创建,节省了内存,省去了创建相同字符串的时间,提升了性能缺点:牺牲了JVM在常量池中遍历对象所需要的时间。

String,StringBuilder,StringBuffer

都是final类,都不允许被继承

String长度不可变,StringBuilder,StringBuffer长度可变

String中的对象是不可变的,可理解为常量,线程安全;StringBuffer中的方法大都采用synchronized关键字修饰,线程安全;StringBuilder线程不安全

性能 StringBuilder>StringBuffer>String

如果String类的字符串在编译时就可以确定是一个字符串常量,则编译完成后,字符串会自动拼接成一个常量,此时的速度比StringBuilder,StringBuffer都要快。String s="a"+"b";>StringBuilder sb=new StringBuilder("a").append("b"); 






原创粉丝点击