java字符串池

一、String类的不变性
String类是不可变的,对String类的任何改变,都是返回一个新的String类对象。String对象是System.Char对象的有序集合,用于表示字符串。
String对象的值是该有序集合的内容,并且该值是不可变的。

特别注意:String类是不可变(final)的,有人对str = "a"+"b";语句提出疑问,怎么str的内容可以改变?其实仍然是因为不清楚:引用变量与对象的区别。str仅仅是引用变量,它的值即它持有的引用可以改变:你不停地创建新对象,我就不断地改变指向。

不变类的关键是:对于对象的所有操作都不可能改变原来的对象[只要需要,就返回一个改变了的新对象]。

这就保证了对象不可改变。为什么要将一个类设计成不变类?有一个OOD设计的原则:Law of Demeter。其广义解读是:使用不变类。只要有可能,类应当设计为不变类。

String类是不可变的,对String类的任何改变,都是返回一个新的String类对象.
这样的话把String类的引用传递给一个方法,该方法对String的任何改变,对原引用指向的对象没有任何影响,这一点和基本数据类型相似.

String池:String是不可改变的,为了提高效率Java引用了字符串池的概念,例如new String("abc");首先会在String池中创建一个对象“abc”因为有new的存在所以会在堆内存中分配地址空间复制字符串池的内容。当出现的String对象在字符串池中不存在时则在字符串池中创建该对象。

下面举个小例子分析一下:

 public class TestConstructString {    
        public static void main(String args[])    
        {    
            String s1 = "a";    
            String s2 = "b";    
            String s3 = "ab";    
                
            String s4 = "ab";    
            System.out.println("s3==s4的结果为:"+ (s3==s4));    
        
            String s5 = "a"+"b";    
            System.out.println("s3==s5的结果为:"+ (s3==s5));    
                
            String s6 = s1+s2;    
            System.out.println("s3==s6的结果为:"+ (s3==s6));    
                
            String s7 = new String("ab");    
            System.out.println("s3==s7的结果为:"+ (s3==s7));    
                
            final String s8 = "a" ;     
            final String s9 = "b" ;    
            String s10 = s8 + s9;    
            System.out.println("s3==s10的结果为:"+ (s3==s10));    
            }    
    }
输出结果为:
s3==s4的结果为:true
s3==s5的结果为:true
s3==s6的结果为:false
s3==s7的结果为:false
s3==s10的结果为:true

结果解析:

s3与s4根据String的概念他们都指向了同一个缓冲池内的地址,所以结果为true。
s3与s5因为相加的两个为常量所以编译器会把s5="a"+"b"优化为s5="ab",所以结果也为true。
s3与s6因为是两个变量的相加所以编译器无法优化,s1+s2即等同于(new StringBuilder(String.valueOf(s1))).append(s2).toString(); 在运行时,会有新的String地址空间的分配,而不是指向缓冲池中的“ab”,所以结果false。
s3与s7,根据缓冲池的定义在new的时候实际会新分配地址空间,s7指向的是新分配的堆地址空间所以与缓冲池地址不同,所以为false
s3与s10,类似于s3与s5,因为是final类型编译器进行了优化所以相同。

二、String对象的创建

由上面的的小例子我们发现,创建字符串的方式很多,归纳起来有三类:
其一,直接指定,比如String s1 = "abc";
其二,使用串联生成新的字符串,比如String s2 = "ab" + "c";
其三,使用new关键字创建字符串,比如String s3 = new String("abc");

String对象的创建也很讲究,关键是要明白其原理。
原理1:当使用任何方式来创建一个字符串对象s时,Java运行时(运行中JVM)会在String池中找是否存在内容相同的字符串对象,如果不存在,则在池中创建一个字符串s,否则,不在池中添加。

原理2:使用直接指定或者使用纯字符串串联来创建String对象,则仅仅会检查维护字符串池中的字符串,字符串池中没有就在字符串池中创建一个,有则罢了!但绝不会在堆栈区再去创建该String对象。 

原理3:Java中,只要使用new关键字来创建对象,则一定会(在堆区或栈区)创建一个新的对象

原理4:使用包含变量的表达式来创建String对象则不仅会检查维护字符串池,而且还会在堆栈区创建一个String对象


理解了上面创建字符串的原理,我们看一下下面三种情况各自创建了几个对象
//情况一  
String s1 = "s";
//情况二  
s1 = new String("s");
//情况三  
String s2 = new String("a" + s1); 

分析:
情况一:结合原理2,在字符串池中创建一个String对象"s",所以一共创建了一个String对象。
情况二:结合原理3,首先会在字符串池中创建一个对象“s”,而在运行时执行new String()时,将字符串池中的对象复制一份放到堆内存中,并且把堆内存中的这个对象的引用交给s1持有,所以一共创建了两个String对象。
情况三:结合原理4,首先在字符串池中创建一个String对象"a"和s1引用的String对象"s",将字符串池中的对象复制一份放到堆内存中,并且把堆内存中的这个对象的引用交给s2持有然后还会在堆内存创建一个新的String对象,所以一共创建了三个String对象。


三、intern()方法

定义为public native String intern();返回字符串对象的规范化表示形式。一个初始为空的字符串池,它由类 String 私有地维护。

当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(用 equals(Object) 方法确定),则返回字符串池中的字符串。否则,将此 String 对象添加到字符串池中,并返回此 String 对象的引用。intern()方法的价值在于让开发者能将注意力集中到String池上。

它遵循以下规则:对于任意两个字符串 s 和 t,当且仅当 s.equals(t) 为 true 时,s.intern() == t.intern() 才为 true。

举个例子看一下

public class TestIntern {
	public static void main(String[] args) {
		String s0="我是小李";
		String s1=new String("我是小李");
		String s2=new String("我是小李");
		System.out.println(s0==s1);
		s1.intern();
		s2=s2.intern();
		System.out.println(s0==s1);
		System.out.println(s0==s1.intern());
		System.out.println(s0==s2);
	}
}
结果为:
False
False //虽然执行了s1.intern(),但它的返回值并没有赋给s1。
True
True

java中字符串的知识先到这里吧,有新的东西在更新。

你可能感兴趣的:(java)