“Java不会字符串,开发就得凉一半”
本文将对 String 进行稍微深入全面一点的学习,大佬请回避 ~
总所周知
String 是Java定义好的一个类。定义在java.lang 包中,而 lang 包是java 的核心包,因此使用的时候不需要导包,而且,是个类,证明String就不属于基本类型,Java的基本类型就那 8 个,String是引用类型
创建String对象的方法
方法名 | 说明 |
public String() | 创建一个空白字符串对象,不含有任何内容 |
public String(String original) | 根据传入的字符串,创建字符串对象 |
public String(char[] chs) | 根据字符数组的内容,来创建字符串对象 |
public String(byte[] bys) | 根据字节数组的内容,来创建字符串对象 |
String s = “abc”; | 直接赋值的方式创建字符串对象,内容就是abc |
注意,使用 public String(byte[] bys) 的时候,不是简单的拼接,需要进行ACSII转换,如下
byte[] bys = {97, 98, 99}; String s3 = new String(bys); System.out.println("s3:" + s3);
输出结果是 abc
String 内存分析
我们看到String类的内部最终是由 char 数组来存储字符串的
现在我们看
String s1 = “abc”;
String s2 = new String("abc")
在内存中的区别
为了节省空间,JVM会在堆区中(JDK 7之前是在方法区中)开辟一个字符串常量池StringTable,用来存放字符串常量,而且是“单例”的,如下
String s3="abc"
String s4="abc"
内存情况如下
所以,我们可以理解为 s1 记录的是 字符串常量 在 字符串常量池中的地址,而 s2 记录的是new String("abc")这个String实例对象在堆中的地址,所以当判断 s1 == s2 时,结果会返回false
因此,如果想比较两个字符串的内容,我们最好使用 equals 方法
StringBuilder & StringBuffer
总所周知,String是不可变的,String不可变不是指
String s="aaa" 后,不能再 s="bbb"
而是说执行上面的代码,不是直接将字符串常量池中的 "aaa" ,改为 "bbb" ,而是会在字符串常量池中新建一个"bbb"常量,然后让引用s指向"bbb"
因为字符串常量的创建会消耗性能,所以如果要对一个String进行频繁的修改,会浪费内存和影响程序效率
在这种场景下,我们可以使用 StringBuilder ,StringBuilder 可以看成是一个容器,创建之后里面的内容是可变的,能极大提高字符串的操作效率
StringBuilder 的构造方法
方法名 | 说明 |
public StringBuilder() | 创建一个空白可变字符串对象,不含有任何内容 |
public StringBuilder(String str) | 根据字符串str的内容来创建可变字符串对象 |
StringBuilder 的常用方法
方法名 | 说明 |
public StringBuilder append(任意类型) | 添加数据并返回对象本身 |
public StringBuilder reverse() | 反转容器中的内容 |
public int length() | 返回长度(字符出现的个数) |
public String toString() |
实现将 StringBuilder 转换为 String |
那么StringBuffer是什么呢?
StringBuffer 和 StringBuilder 几乎相同,只不过 StringBuffer 的方法上都加了同步锁(如下图),因此StringBuffer是线程安全的 ,但是性能较差
字符串拼接的底层原理
String s1="a"; String s2=s1+"b"; String s3="ab"; System.out.println(s2==s3);
输出 false
String s1="a"; String s2="a"+"b"; String s3="ab"; System.out.println(s2==s3);
输出 true
为什么呢?
这两个的区别就是在拼接到时候是 纯字符("a"+"b"),还是 存在引用(s1+"b"),纯字符会被优化
本例中创建 String s2="a"+"b" ,在编译的时候,Java会自动优化为 String s2= "ab"
而在JDK8中(JDK7有所不同,但结果一样,不再讲解),创建 String s2=s1+"b" 的过程中,Java会预估一个合适长度(本例为 2)的字符数组,然后分别将 "a" (来自s1) 和 "b" 存入该数组,然后再根据该数组生成 String 对象,相当于是new String,所以此时的s2是指向堆区的,而s3是指向StringTable 的,因此输出 s2==s3 就是fasle
String常用API
最后再总结下String常用API
- length():返回字符串的长度。
- charAt(int index):返回指定索引处的字符。
- substring(int beginIndex, int endIndex):返回一个新字符串,它是此字符串的一个子字符串。
- equals(Object anObject):比较此字符串与指定对象是否相等。
- equalsIgnoreCase(String anotherString):比较此字符串与指定字符串是否相等,忽略大小写。
- indexOf(int ch):返回指定字符在此字符串中第一次出现处的索引。
- lastIndexOf(int ch):返回指定字符在此字符串中最后一次出现处的索引。
- startsWith(String prefix):测试此字符串是否以指定的前缀开始。
- endsWith(String suffix):测试此字符串是否以指定的后缀结束。
- replaceAll(String regex, String replacement):使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
- split(String regex):根据正则表达式将此字符串拆分为子字符串,并返回这些子字符串的数组。
- toCharArray():将此字符串转换为字符数组。
- toLowerCase():将此字符串转换为小写形式。
- toUpperCase():将此字符串转换为大写形式。
- hashCode():计算此字符串的哈希码。
- constructor():创建一个新的字符串对象,其内容是该参数的字符序列。