String类 1.String类声明为final,不可被继承。 2.String实现了Serializable接口,表示字符串支持序列化。 3.String实现了Comparable接口,表示可以比较大小。 4.String内部定义了final char[] value用于存储字符串数据。 5.String代表不可变 的字符序列,简称不可变性。
String的不可变性 先看一个例子:
1 2 3 4 5 6 7 8 9 package demo03;public class StringTest { public static void main (String[] args) { String s1 = "abc" ; String s2 = "abc" ; System.out.println(s1 == s2); } }
输出结果:
true
原因:字面量字符串储存在字符串常量值,一样的字符串只储存一个。则通过字面量赋值的s1和s2指向同一个字符串,即地址值一样。 所以有以下结论:通过字面量方式(区别于new)给一个字符串赋值,此时的字符串值生命在字符串常量池中,字符串常量池中是不会存储相同内容的字符串的。 再添点例子,充分体现String的不可变性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package demo03;public class StringTest { public static void main (String[] args) { String s1 = "abc" ; String s2 = "abc" ; System.out.println(s1 == s2); System.out.println("==========================" ); String s3 = "abc" ; System.out.println(s2 == s3); s3 +="def" ; System.out.println(s3); System.out.println(s2); System.out.println("==========================" ); String s4 = s2.replace('a' ,'m' ); System.out.println(s2); System.out.println(s4); } }
true true abcdef abc ========================== abc mbc
当不管是调用String方法,比如replace,来修改值,还是拼接还是更改引用,只要和原来值不一样,就是在字符串常量池新造一个字符串,来更改引用,原value没有变。String代表不可变的字符序列。
String类的赋值 有四种主要方式,除此之外还有比较多的API,比如用StringBuffer和StringBuilder赋值等。new和字符串字面量赋值的区别?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package demo03;public class StringTest { public static void main (String[] args) { String s1 = "abc" ; String s2 = new String("abc" ); String s3 = new String("abc" ); System.out.println(s1.equals(s2)); System.out.println(s1 == s2); System.out.println(s2 == s3); System.out.println(s2.equals(s3)); } }
输出结果:
true false false true
就算是用构造器也是一样的,下面来一个Person类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package demo03;public class Person { int age; String name; public Person () { } public Person (String name, int age) { this .age = age; this .name = name; } }
再来测试一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package demo03;public class StringTest { public static void main (String[] args) { Person p1 = new Person("tom" ,12 ); Person p2 = new Person("tom" ,12 ); System.out.println(p1.name == p2.name); System.out.println(p1.name.equals(p2.name)); Person p3 = new Person(new String("tom" ),12 ); Person p4 = new Person(new String("tom" ),12 ); System.out.println(p3.name == p4.name); System.out.println(p3.name.equals(p4.name)); } }
结果:
true true false true
内存结构如下:
进一步验证了String类的不变性。
String类不同拼接操作的对比 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package demo03;public class StringTest { public static void main (String[] args) { String s1 = "hello" ; String s2 = "world" ; String s3 = "helloworld" ; String s4 = "hello" + "world" ; String s5 = s1 + "world" ; String s6 = "hello" +s2; System.out.println(s3 == s4); System.out.println(s3 == s5); System.out.println(s3 == s6); System.out.println(s5 == s6); } }
结果:
true false false false
两个字面量字符串通过+连接,相当于一个字面量 ,也就是s3和s4指向同一个字符串常量池中的字面量字符串。而赋值过程有变量名(注意并非是final修饰的变量,final修饰后为常量,储存在常量池)参与而不是字面量直接参与,则此时不在常量池了 ,需要在堆空间中开辟,相当于new。 有一个方法是**intern()**,返回字符串常量池中的字符串。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package demo03;public class StringTest { public static void main (String[] args) { String s1 = "hello" ; String s2 = "world" ; String s3 = "helloworld" ; String s4 = "hello" + "world" ; String s5 = s1 + "world" ; String s6 = "hello" +s2; System.out.println(s3 == s4); System.out.println(s3 == s5); System.out.println(s3 == s6); System.out.println(s5 == s6); String s7 = s5.intern(); System.out.println(s3 == s7); } }
true false false false true
String与byte数组之间的转换 String –> byte[]:调用String的getBytes() 1 2 3 4 5 6 7 8 9 10 11 12 package demo03;import java.util.Arrays;public class StringTest { public static void main (String[] args) { String s = "abc123" ; byte [] bytes = s.getBytes(); System.out.println(Arrays.toString(bytes)); } }
[97, 98, 99, 49, 50, 51]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package demo03;import java.util.Arrays;public class StringTest { public static void main (String[] args) { String s2 = "中文" ; byte [] bytes2 = s2.getBytes(); System.out.println(Arrays.toString(bytes2)); } }
[-28, -72, -83, -26, -106, -121]
默认UTF-8编码 一个中文对三个字节。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package demo03;import java.io.UnsupportedEncodingException;import java.util.Arrays;public class StringTest { public static void main (String[] args) throws UnsupportedEncodingException { String s2 = "中文" ; byte [] bytes2 = s2.getBytes("gbk" ); System.out.println(Arrays.toString(bytes2)); } }
[-42, -48, -50, -60]
gbk编码下一个中文对两个字节
解码通过String的构造器实现 1 2 3 4 5 6 7 8 9 10 11 12 package demo03;public class StringTest { public static void main (String[] args) { byte [] b = new byte []{ 97 , 98 , 99 , 49 , 50 , 51 }; String s = new String(b); System.out.println(s); } }
abc123
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package demo03;import java.io.UnsupportedEncodingException;public class StringTest { public static void main (String[] args) throws UnsupportedEncodingException { byte [] b = new byte []{-42 , -48 , -50 , -60 }; String s = new String(b,"gbk" ); System.out.println(s); } }
中文
要注意编码集和解码集一致,否则会出现乱码。
String、StringBuffer和StringBuilder的异同 String:不可变的字符序列,底层使用char[]存储。 StringBuffer:since JDK1.0,可变的字符序列,效率低,线程安全,底层使用char[]存储。 StringBuilder:since JDK1.5,可变的字符序列,效率高,线程不安全,底层使用char[]存储。
可变性 1 2 3 4 5 6 7 8 9 10 11 12 package demo03;public class SBTest { public static void main (String[] args) { StringBuffer sb1 = new StringBuffer("abc" ); sb1.setCharAt(0 ,'m' ); System.out.println(sb1); sb1.append('d' ); System.out.println(sb1); } }
mbc mbcd
底层实现 源码空参构造器: 有参构造器(JDK8):
1 2 3 4 5 6 7 8 9 package demo03;public class SBTest { public static void main (String[] args) { StringBuffer sb1 = new StringBuffer("abc" ); System.out.println(sb1.length()); } }
3
length源码:
扩容问题:如果要添加的数据底层数组装不下了,如何扩容? 默认情况下,扩容为原来的容量的二倍再+2,同时将原有的数组中的元素复制到新数组中。 开发中可以用下面的构造器,指定底层char数组容量:添加null字符串
1 2 3 4 5 6 7 8 9 10 11 12 package demo03;public class SBTest { public static void main (String[] args) { String str = null ; StringBuffer sb = new StringBuffer(); sb.append(str); System.out.println(sb); System.out.println(sb.length()); } }
null
4
构造器添加null字符串
1 2 3 4 5 6 7 8 9 10 11 package demo03;public class SBTest { public static void main (String[] args) { String str = null ; StringBuffer sb = new StringBuffer(str); System.out.println(sb); } }
产生NullPointerException
常用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package demo03;public class SBTest { public static void main (String[] args) { StringBuffer sb1 = new StringBuffer("abc" ); sb1.append(1 ); sb1.append('1' ); System.out.println(sb1); sb1.delete(2 ,4 ); System.out.println(sb1); StringBuffer sb2 = new StringBuffer("abc11" ); sb2.replace(2 ,4 ,"hello" ); System.out.println(sb2); StringBuffer sb3 = new StringBuffer("abc" ); sb3.insert(1 ,false ); System.out.println(sb3); sb3.reverse(); System.out.println(sb3); } }
abc11 ab1 abhello1 afalsebc cbeslafa
效率对比 StringBuilder>StringBuffer>>String