1、String的创建
1.1、普通创建方式和new关键字创建的区别
String最常用的创建方式就是:
String a = "abcd";
使用这种方式创建字符串时,jvm首先会在字符串常量池中检查是存在该字符串,如果有则将该对象的地址赋给它的引用a;若不存在则jvm会在常量池中创建这个字符串对象并将地址赋给a。还有一点需要明确:字符串一经过创建就不会改变。在这个基础上加入:
a = "xxx";
虽然a被指向了xxx,但是原来的abcd却没有变,只是jvm又在常量池中创建了新的xxx,然后让a指向xxx的地址,其示意图如下:
如果使用new关键字创建字符串,jvm首先检查字符串常量池,如果该字符串已经存在常量池中,那么不再在字符串常量池创建该字符串对象,而直接堆中复制该对象的副本,然后将堆中对象的地址赋值给引用s,如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中,然后在堆中复制该对象的副本,然后将堆中对象的地址赋值给引用s。
- String s1= “xxx”;
- String s2 = new String(“xxx”);
这两种创建方式的示意图如下:
有了以上的概念,我们来看看以下几个输出结果:
String s1 = "aaa";
String s2 = "aaa";
System.out.println(s1 == s2); //输出结果为true
String s3 = new String("aaa");
System.out.println(s3 == s1); //输出结果为false
String s4 = new String("aaa");
System.out.println(s3 == s4); //输出结果为false
上述结果是很容易理解的,因为字符串常量池中已经有了aaa就不会再次创建(这说明了常量池中不会有两个相同的字符串),所以s2这个引用指向的就是之前创建的aaa,因而s1和s2指向的地址是相同的故输出结果为真;而s3指向的是堆内存中的字符串aaa(虽然也是从常量池中复制过来的,但是地址却变了)所以输出false;使用new关键字一定会在堆内存中创建对象,所以虽然s3和s4内容是一样的,但是由于它们都是new出来的所以是两个对象故地址不同,因而输出的是false。其示意图如下:
此外,再提一个插曲:
String s1 = "aaa";
String s2 = new String("aaa");
System.out.println(s1.equals(s2)); //输出结果为true
虽然s1和s2的地址不相同,但是String类重写了Object的equals方法,它里面比较的是字符串的内容是否相同,所以输出结果为true。
1.2、String的其他常用构造方法
(1)String():创建一个空的字符串序列,值得注意的是,String s = new String()并不等同于String s = null
(2)String(byte[] bytes):将指定的bytes数组解码成一个字符串
(3)String(char[] value):由给定的char数组创建一个字符串
(4)String(char[] value, int offset, int count):截取给定的char数组的一部分(从offset角标开始,长度为count)生成字符串
2、String的其他常用功能
2.1、获取
(1)获取字符串中字符的个数(长度)
int length()
(2)根据指定位置获取字符
char charAt(int index)
(3)获取字符或字符串在调用者字符串中出现的位置(若没搜到返回-1,可根据这一点判断字符串中有无指定字符或字串)
正向搜索
int indexOf(int ch) //返回指定字符在此字符串中第一次出现处的索引
int indexOf(int ch, int fromIndex) //返回在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索
int indexOf(String str) //返回指定子字符串在此字符串中第一次出现处的索引
int indexOf(String str, int fromIndex) //返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始
反向搜索
int lastIndexOf(int ch) //返回指定字符在此字符串中最后一次出现处的索引
int lastIndexOf(int ch, int fromIndex) //返回指定字符在此字符串中最后一次出现处的索引,从指定的索引处开始进行反向搜索
int lastIndexOf(String str) //返回指定子字符串在此字符串中最右边出现处的索引
int lastIndexOf(String str, int fromIndex) //返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索
(4)获取字符串中的一部分字符串(子串)
String substring(int beginIndex)
String substring(int beginIndex, int endIndex) //包含开始,不包含结尾
2.2、转换
String[] split(String regex) //根据给定正则表达式的匹配拆分此字符串(切割)。
char[] toCharArray() //将此字符串转换为一个新的字符数组。
byte[] getBytes(Charset charset) //字符串转换成字节数组
String toUpperCase() //将字符串字母大写。
String toLowerCase(Locale locale) //字符串字母小写
String replace(char oldChar, char newChar) //替换字符
String trim() //去掉字符串两端的空格
static String valueOf(char/boolean/char[]/double/float/long/int/Object ) //将参数转化成字符串
2.3、判断
boolean equals(Object anObject) //比较内容是否相等
boolean equalsIgnoreCase(String anotherString) //比较内容是否相等,不考虑大小写
boolean startsWith(String prefix) //判断此字符串是否以指定的前缀开始
boolean endsWith(String suffix) //判断此字符串是否以指定的后缀结束
2.3、比较
int compareTo(String anotherString) //按字典顺序比较两个字符串 ,如果按字典顺序此 String 对象位于参数字符串之前,则比较结果为一个负整数。
//如果按字典顺序此 String 对象位于参数字符串之后,则比较结果为一个正整数。如果这两个字符串相等,则结果为 0
int compareToIgnoreCase(String str) //按字典顺序比较两个字符串,不考虑大小写。
3、可变字符串
由于String创建的字符串是不可变的,如果改变字符串的话,jvm会在内存中创建一个新的字符串,如果经常进行修改就会浪费大量的资源。Java提供了可变字符串StringBuilder和StringBuffer来解决这个问题,它是一个类似于String的字符串缓冲区,其实体容量会随着存放字符串的增加而自动增大。
它的3种常用构造方法如下:
StringBuffer() //构造一个其中不带字符的字符串缓冲区,初始容量为 16 个字符
StringBuffer(int capacity) //构造一个不带字符,但具有指定初始容量的字符串缓冲区
StringBuffer(String str) //构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容
常用方法:
StringBuffer append(boolean/char/char[]/double/float/int/long/Object/String ……) //追加字符串序列
void setCharAt(int index, char ch) //修改指定索引处的字符
StringBuffer reverse() //字符串反序
StringBuffer delete(int start, int end) //删除字串,包括开始不包括结尾
StringBuffer replace(int start, int end, String str) //替换字串,包括开始不包括结尾
StringBuilder和StringBuffer有相同的API,即用法和StringBuffer相同。
String、StringBuilder和StringBuffer使用场景如下: