目录
3.2StringBuilder类/StringBuffer类
3.2String、StringBuffer、StringBuilder的区别
1.String常用方法
1.1构造方法
String提供的构造方法很多,其中常用的有三种:
public static void main(String[] args) {
String str1 = "abc";//1.使用常量字符串进行构造
String str2 = new String("abc");//2.直接new String对象
char[] ch = {'a', 'b', 'c'};//3.使用字符数组进行构造
String str3 = new String(ch);
}
String是引用类型,内部并不存储字符串,而是一个字符数组。
这是源码:
在JAVA中用 双引号"" 引起来的也是String类型对象。
System.out.println("helloworld".length());//这个helloworld就是一个String对象,
//它能调用String的方法
//执行结果:10
1.2 String对象的比较
1.==
1.对于基本类型变量,==比较的是两个变量的值是否相等。
int a = 10;
int b = 10;
System.out.println(a == b);//执行结果:true
2.对于引用变量,==比较是否指向同一个对象。采用new关键字创建对象时,每次new出来的都是一个新的对象,也即是说引用str1和str2指向的是两个不同的对象。
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1 == str2);//执行结果:false
3.在我们的String中,存在字符串常量池。
JVM为了提升性能和减少内存开销,避免字符串的重复创建,其维护了一块特殊的内存空间,这一块空间就是字符串常量池。
我们采用常量字符串来构造一个String对象的时候,JVM会先在常量池中查找这个字符串是否存在,如果不存在则将这个字符串加入常量池中。如果存在,则不创建任何新的对象,直接将常量池中已有的对象的引用返回。
String str1 = "hello";
String str2 = "hello";
System.out.println(str1 == str2);//执行结果:true
值得一提的是,如果在使用new关键字创建String对象的时候,当常量池里不存在这个字符串,会现在常量池创建这个字符串,然后再在堆上创建一个新的对象,返回的是堆上的对象的引用。
如果常量池已经存在这个字符串,那么就不在常量池再创建一次了,会在堆上创建对象,返回的也是堆上对象的引用。
2.equals方法
equals方法用来比较两个String对象是否相等:
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1 .equals(str2));//执行结果:true
equals源码:
public boolean equals(Object anObject) {
if (this == anObject) {//如果是指向的是同一个对象
return true;//直接返回true
}
if (anObject instanceof String) {//如果都是String类则进行比较
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {//如果两个字符串长度相等
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
3.compareTo方法
compareTo方法用来比较两个字符的大小:
如果两个字符串相等,返回0。
//如果两个字符串相等,返回0。
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1 .compareTo(str2));//执行结果:0
先按照字典序比较,如果出现不同的字符则直接返回这两个字符的大小差值。
//先按照字典序比较,如果出现不同的字符则直接返回这两个字符的大小差值。
String str3 = new String("world");
String str4 = new String("hello");
System.out.println(str3 .compareTo(str4));//执行结果:15
如果前k个字符相等(k为两个字符长度最小值),就返回两个字符串长度差值。
//如果前k个字符相等(k为两个字符长度最小值),就返回两个字符串长度差值。
String str5 = new String("hello");
String str6 = new String("helloworld");
System.out.println(str5 .compareTo(str6));//执行结果:-5
compareTo源码:
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
1.3其他方法
1.字符串查找
方法 | 功能 |
char charAt(int index) | 返回index位置上字符,如果index为负数或者越界,抛出 IndexOutOfBoundsException异常 |
int indexOf(int ch) | 返回ch第一次出现的位置,没有返回-1 |
int indexOf(int ch, int fromIndex) | 从fromIndex位置开始找ch第一次出现的位置,没有返回-1 |
int indexOf(String str) | 返回str第一次出现的位置,没有返回-1 |
int indexOf(String str, int fromIndex) | 从fromIndex位置开始找str第一次出现的位置,没有返回-1 |
int lastIndexOf(int ch) | 从后往前找,返回ch第一次出现的位置,没有返回-1 |
int lastIndexOf(int ch, int fromIndex) | 从fromIndex位置开始找,从后往前找ch第一次出现的位置,没有返 回-1 |
int lastIndexOf(String str) | 从后往前找,返回str第一次出现的位置,没有返回-1 |
int lastIndexOf(String str, int fromIndex) | 从fromIndex位置开始找,从后往前找str第一次出现的位置,没有返 回-1 |
2.字符串替换
方法 | 功能 |
String replaceAll(String regex, String replacement) | 替换所有的指定内容 |
String replaceFirst(String regex, String replacement) | 替换首个内容 |
3.字符串拆分
方法 | 功能 |
String[] split(String regex) | 将字符串全部拆分 |
String[] split(String regex, int limit) | 将字符串以指定的格式,拆分为limit组 |
4.字符串截取
方法 | 功能 |
String substring(int beginIndex) | 从指定索引截取到结尾 |
String substring(int beginIndex, int endIndex) | 截取部分内容 |
5.其他操作方法
方法 | 功能 |
String trim() | 去掉字符串中的左右空格,保留中间空格 |
String toUpperCase() | 字符串转大写,只转换字母 |
String toLowerCase() | 字符串转小写,只转换字母 |
char[] toCharArray() | 将字符串转换为字符数组 |
2.字符串的不可变性
String类是一种不可变的对象,也就是说字符串不能被修改,原因如下:
1.这是源码中的一段话:字符串是常量;它们的值在创建后不能更改。
以及这个String类被final修饰,表示它不能被继承。它的value数组也被final修饰,表示value本身的值不能改变,它不能引用其他字符数组,但是它所引用的空间的内容是可以改变的。
2.所有涉及到更改字符串内容的操作,本质上都new了一个新对象:
以replace为例,这是replace源码:
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value; /* avoid getfield opcode */
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
if (i < len) {
char buf[] = new char[len];
for (int j = 0; j < i; j++) {
buf[j] = val[j];
}
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
return new String(buf, true);//返回了一个新的对象
}
}
return this;
}
为什么要将String设置为不可变呢?
1.方便实现字符串常量池,如果可变就要考虑常量池里的拷贝问题了。
2.不可变的是线程安全的(线程后面再讲)。
3.不可变对象可以更方便地缓存hashcode,作为key可以更高效地存入hashmap中。
3.字符串的修改
3.1直接修改String对象的弊端
在修改字符串的时候,我们应该避免直接使用String类进行修改,因为在修改String类的时候会创建一个新的对象效率十分低下 ->
String str = new String();
for (int i = 0; i < 100; i++) {
str += 'a';
}
(在不考虑常量池已有对象的情况下)循环体中的 str += 'a';语句可以写为 str = new String(str) + new String("a"),而执行这条语句就会创建一个新的StringBuilder类的对象,假设它叫tmp,是一个空字符串,它会追加一个new String(str)和一个new String("a"),然后将这个结果再toString(转换为字符串),所以一共创建了6个对象~~
对象1:new StringBuilder()
对象2:new String("a")
对象3:常量池中的"a"
对象4:new String("b")
对象5:常量池中的"b"
对象6:new String("ab")
以上代码由于直接作用于String类,所以会创建很多对象。
那我们在修改字符串的时候最好用StringBuilder或者StringBuffer类来进行修改。
3.2StringBuilder类/StringBuffer类
1.常用方法
这两个类的方法相差不大,这里以StringBuffer类的方法为例
方法 | 功能 |
StringBuffer append(String str) | 在尾部追加,相当于String的+=,可以追加:boolean、char、char[]、 double、float、int、long、Object、String、StringBuff的变量 |
char charAt(int index) | 获取index位置的字符 |
int length() | 获取字符串的长度 |
int capacity() | 获取底层保存字符串空间总的大小 |
void ensureCapacity(int mininmumCapacity) | 扩容 |
void setCharAt(int index, char ch) | 将index位置的字符设置为ch |
int indexOf(String str) | 返回str第一次出现的位置 |
int indexOf(String str, int fromIndex) | 从fromIndex位置开始查找str第一次出现的位置 |
int lastIndexOf(String str) | 返回最后一次出现str的位置 |
int lastIndexOf(String str, int fromIndex) | 从fromIndex位置开始找str最后一次出现的位置 |
StringBuffer insert(int offset, String str) | 在offset位置插入:八种基类类型 & String类型 & Object类型数据 |
StringBuffer deleteCharAt(int index) | 删除index位置字符 |
StringBuffer delete(int start, int end) | 删除[start, end)区间内的字符 |
StringBuffer replace(int start, int end, String str) | 删除[start, end)区间内的字符 |
String substring(int start) | 从start开始一直到末尾的字符以String的方式返回 |
String substring(int start,int end) | 将[start, end)范围内的字符以String的方式返回 |
StringBuffer reverse() | 反转字符串 |
String toString() | 将所有字符按照String的方式返回 |
3.2String、StringBuffer、StringBuilder的区别
1.String的内容不能修改,StringBuffer、StringBuilder的内容可以修改。
2.StringBuffer、StringBuilder的大部分功能是类似的。
3.StringBuffer采用同步处理,是线程安全的,StringBuilder未采用同步处理,是线程不安全的。
结语:最近越来越理解“书读百遍,其意自见”的含义了,不懂的代码就多敲,不会的知识点就多看~~