1 String的基本特性
1)字符串,使用一对”“引起来表示。
2)String 声明为final,不可被继承
3)String实现了Serializable接口:表示字符串是支持序列化的。实现了comparable接口,表示String是可以比较大小的。
4)String在JDK8及其以前使用Final char[] value用于存储字符串数据。JDK9改为byte[],因为大部分字符串是拉丁符,一个字节就能存下,这些字符一个byte就能存储。但是中文需要两个byte,因此还增加了字符编码集的标识。
5)String:代表不可变的字符序列
6)通过字面量赋值和new赋值的区别。
例如String str1="ABC"; 和String str2 = new
String("ABC"); 前者可能创建一个对象也可能不创建对象,后者创建两个或者一个。前者如果”ABC"在常量池中则不创建,后者在堆区创建一个对象,如果常量池中不存在则再创建一个。
7)字符串常量池中不会存储相同内容的字符串。
String的String pool是一个固定大小的Hashtable如果放进String pool的string非常多,就会造成hash冲突严重,从而导致链表长度很长,大幅度降低查询效率。
jdk6的默认值大小长度为1009,jdk7默认大小为60013。可以使用-XX:StringTableSize来设置其长度,jdk8中对设置长度有最低要求,为1009。
2 String内存分配
java中有八种基本数据类型和一种比较特殊的类型String。这些类型为了使它们在运行过程中更快、更节省内存,都提供了一种常量池的概念。
java常量池就类似一个java系统级别提供的缓存。8种数据类型的常量池都是系统协调的,S特ing类型比较特殊,主要两种:1.直接双引号声明出来的String 对象对直接存储在常量池中。2.如果不是双引号声明的String对象,可以使用intern()方法。
3 字符串的拼接操作
1)常量与常量的拼接结果在常量池,原理是编译器优化。
2)常量池中不会存在相同的常量。
3)只要其中有一个变量(final修饰的虽然是引用,不算变量),结果就在堆中,变量的拼接原理StringBuilder。
1.new一个StringBuilder
2.append("a")
3.append("b")
4s.tostring(),约等于new String("ab")
补充:jdk5.0之前使用stringbuffer,之后使用stirngbuilder。
4)如果拼接的结果调用intern()方法,则主动将常量池中还没有的字符串对象放入池中,并返回此对象地址。
拼接操作和append的效率对比:
拼接操作的效率远小于append,因为每一次拼接操作都会创建一个strngbuilder和一个string对象。
4 intern()的使用
native修饰的本地方法
intern方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中。在任意字符串上调用String.intern()方法,其返回的结果所指向的那个类实例,必须和直接以常量形式出现的字符串实例完全相同。
如何确保变量s指向的是字符串常量池中的数据?
方式一:String s ="shkstart";//字面量赋值
方式二:调用intern方法
String s= new String("shkstart").intern();
String s = new StringBuilder("shkstart").toString().intern();
5 new String("ab")会创建几个对象?new String("a")+new String("b")呢?
两个,看字节码就知道是两个。一个是new关键字在堆空间创建,一个是在常量池,字节码中的ldc指令。
六个:
1.StringBuilder.
2.String("a")
3.常量池“a”
4.String("b")
5.常量池”b“
6.String("ab"):tostirng的调用,在常量池中没有生成“ab"
6 intern()的使用jdk6 vs jdk7/jdk8
jdk1.6中,将这个字符串对象尝试放入串池。如果池中有,并不会放入。返回已有串池的对象的地址。如果没有,会把对象复制一份放入串池中,并返回串池中的对象地址。
jdk7起,如果串池有,不会放入,返回串池中对象地址,没有则会把对象的引用地址复制一份,放入串池,并返回串池中的引用地址。
小结:jdk6的时候字符串常量池在永久代,1.7以后均位于堆区,而对象创建于堆区,所有本着节约空间的想法,常量池中直接引用对象地址。
String s = new String("1");
s.intern();//调用此方法时,常量池中已经存在了”1“
String s2 = "1";
System.out.println(s1==s2);//jdk6:false jdk7/jdk8:false
String s3 = new String("1")+new String("1");//常量池中没有”11“
s3.intern();//jdk7没有创建新的对象,直接使用堆中对象的地址。
String s4 = "11";
System.out.println(s3==s4);//jdk6:false jdk7/jdk8:true
如果s3.intern("11")和 String s4 = "11";结果为false,因为前者已经直接在常量池中创建了一个对象。
intern()测试执行效率:(空间使用上)
在拥有大量重复对象的时候执行效率提升很大
虽然依旧会创建对象,但是对象实例指向的是常量池中引用,没有指向的对象会被销毁。