前情提要:
在此前我们谈到数据类型和变量的时候了解到了String类,但是没有并没有详细地讲到String类的应用以及jvm提供许多相关API的应用,这篇博客就来了解一下String类以及相关API的应用。
String类的重要性:
在学习C语言的时候就已经对字符串有所涉猎,但是C语言中要表达字符串只能使用字符数组或者字符指针,再调用标准库提供的已经完成的相关函数来完成编译需求。因为讲数据和操作数据的方法分离不符合面向对象编程的理念,Java中为了满足string类广泛的应用要求,所以提供了String类。
常用方法:
字符构造:
String类提供的构造方式非常多,常用的就以下三种:
public static void main(String[] args) {
//第一种:直接new一个String类对象
String s1 = new String("abcd");
//第二种:直接创建一个常量字符串
String s2 = "abcde";
//第三种:使用字符数组构建
char[] ch = {'a','b','c','d','e'};
String s3 = new String(ch);
}
注意:String本身是引用类型,内部并不储存字符串本身
String类对象进行比较:
1、==用于比较是否引用同一个对象
String属于引用类型,引用变量与基本类型变量的比较方式有所不同。
public static void main(String[] args) {
int a = 10;
int b = 10;
System.out.println(a==b); //true
//引用类型进行比较,==则是比较引用对象是否为同一个
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1==s2); //false
}
==在比较引用类型变量的时候比较的是地址(如果地址相同说明是同一个对象,返回值会是true),比较基本类型变量的时候比较的是内置储存的值是否一样。
2、boolean equals(Object anObject)方法:按照字典序比较
我们用ctrl+鼠标左键点击equals()可以直接看对应API中的方法是怎么进行重写的
字典序比较:按照字符大小写进行比较
public boolean equals(Object anObject) {
// 1. 先检测this 和 anObject 是否为同一个对象比较,如果是返回true
if (this == anObject) {
return true;
}
// 2. 检测anObject是否为String类型的对象,如果是继续比较,否则返回false
if (anObject instanceof String) {
// 将anObject向下转型为String类型对象
String anotherString = (String)anObject;
int n = value.length;
// 3. this和anObject两个字符串的长度是否相同,是继续比较,否则返回false
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
// 4. 按照字典序,从前往后逐个字符进行比较
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
例:
三个实例化对象s1,s2,s3,我们可以知道不同于==比较,equals不仅比较对象,还看引用对象的内容:
不同引用对象但存放内容相同——返回true
不同引用对象存放内容不同——返回false
public static void main(String[] args) {
String s1 = new String("abc");
String s2 = new String("abc");
String s3 = new String("abd");
System.out.println(s1.equals(s2)); //true 虽然经过比较发现不是同一个引用对象,但是引用对象存放的内容相同所以返回值是true
System.out.println(s1.equals(s3)); //false 因为不是同一个引用对象内容也不同,所以返回值是false
}
3、int compareTo(String s)方法比较:按照字典序比较
与equals()不同的是compareTo()的返回值是int类型,具体比较方式:
1、先按字符序比较,当比较到不同的字符时返回两个字符串的长度差
2、前n个字符相同,则返回两个字符串的长度差
public static void main(String[] args) {
String s1 = new String("abc");
String s2 = new String("ad");
String s3 = new String("abcd");
String s4 = new String("abc");
System.out.println(s1.compareTo(s2)); //-1 s1和s2进行比较,b≠c且c>b,所以返回值是ascii值 s1-s2 = -1
System.out.println(s1.compareTo(s3)); //-1 s1和s3比较,前面相同但是s3长度比s1大1,返回值是s1.length-s3.length
System.out.println(s1.compareTo(s4)); //0 s1和s4比较,内容一样所以返回0
}
字符串查找
我们可以给字符串查找分类:直接查找、顺序查找、逆序查找
直接查找:
直接查找用到的实例方法是char charAt(int ch),直接访问字符串下标为ch的字符
例:
public static void main(String[] args) {
String s1 = new String("abcd");
System.out.println(s1.charAt(3)); //char charAt(int index) 返回index下标所在的符号
}
还有顺序直接查找int index(int ch,int fromIndex),逆序直接查找int lastIndex(int ch,int fromIndex)
public static void main(String[] args) {
String string = new String("Harry Porter");
System.out.println(string.indexOf('a',0));
System.out.println(string.lastIndexOf('a',5));
}
顺序查找:
也就是从前往后查找字符,要用到的方法是int index()
public static void main(String[] args) {
String s2 = new String("hello");
System.out.println(s2.indexOf('e')); //int indexof(int ch) 返回字符ch 所在的下标index,没有的话返回-1
System.out.println(s2.indexOf("ell")); //int indexof(String ch) 返回字符ch第一次出现的下标index,没有的话返回-1
System.out.println(s2.indexOf("el",3)); //int indexof(String ch,int fromindex) 从fromindex开始找字符ch,返回字符ch第一次出现的下标index,没有的话返回-1
}
运行结果是:
逆序查找:
倒序查找,从字符串的尾端开始查找,要用到的方法是int lastIndexOf()
public static void main(String[] args) {
String s1 = new String("abcd");
System.out.println(s1.lastIndexOf('a')); //int lastIndexOf(int ch) 返回字符ch 所在的下标index,没有的话返回-1
System.out.println(s1.lastIndexOf("abc")); //int lastIndexOf(String str) 从后往前找,返回字符str第一次出现的下标index,没有的话返回-1
System.out.println(s1.lastIndexOf('b',3)); //int lastIndexOf(String str,int fromindex) 从fromindex开始,从后往前面找字符ch,返回字符str第一次出现的下标index,没有的话返回-1
}
运行结果:
特别注意:在调用index()和fromIndexOf()方法的时候,查找字符要用' '引用,查找字符串要用" "引用,不然容易搞出乌龙
字符串拆分
字符拆分用到的方法String split(String regex)和String split(String regex,int limit)
前者是整个字符串都按照字符regex部分进行整体拆分,后者是按照字符regex进行拆分的同时还将字符串拆分成limit份(也就是部分拆分)
我们用代码实例来体会一下二者的区别:
调用前者:
public static void main(String[] args) {
String string = new String("Harry Porter great magic");
String[] sp1 = string.split(" "); //split(String regex) ,该例子中按照" "空格切割
for(String st:sp1)
{
System.out.println(st);
}
}
按照空格切割整个字符串:
调用后者:
public static void main(String[] args) {
String string = new String("Harry Porter great magic");
String[] sp2 = string.split(" ",2);
for(String st:sp2)
{
System.out.println(st);
}
}
按照空格切割,limit=2,所以将字符串拆分成两部分:
字符串替换:
字符串替换用到的方法是String replaceAll(String regex, String replacement) 和String replaceFirst(String regex, String replacement)
方法中第一个参数是原字符中要被替换的字符串,第二个参数是替换内容,上述两个方法前者是将原字符串中符合参数的字符串全部替换,后者是替换第一个符合参数字符串的字符串
public static void main(String[] args) {
String string = new String("Iron man and Bat man");
System.out.println(string.replaceAll("man","woman"));
System.out.println(string.replaceFirst("man", "woman"));
}
运行结果:
字符串截取:
字符串截取用到的方法是String substring(int beginIndex)和String substring(int beginIndex, int endIndex)
在字符串中从区间(begin,end)截取字符
public static void main(String[] args) {
String str = "helloworld" ;
System.out.println(str.substring(5));
System.out.println(str.substring(0, 5));
}
运行结果:
String的引用雷点:
在日常开发时有时会涉及到修改字符串的操作,但是有一个特点非常重要!!
那就是——String引用变量的内容不能直接进行修改!!
此前,对于String类引用变量不可修改有一种说法——之所以不可修改是因为String类内部方法被final关键字修饰,所以不能被修改。但是这种说法是错的,final修饰的类表明该类不想被继承,final修饰的方法和成员变量表明不想被其他对象引用,但是引用对象中的内容是可以被修改的
String类的各种修改操作都是通过调用新的对象来进行修改(new一个新的对象接受修改对象的属性,在新的对象上对这些属性进行修改,最后将新的对象作为返回类型替换旧的对象)
为什么String类引用不支持直接修改?
答:因为如果String类引用如果课修改的话,那么对象池就得考虑临时拷贝的问题,而且不可变对象的线程是安全的。
StringBuffer和StringBuilder
因为在日常开发难免会有修改需求,所以大佬们设计了StringBuffer和StringBuilder这两个引用类型并封装在String的API中。
二者可调用的引用方法基本上一致
public static void main(String[] args) {
StringBuffer s1 = new StringBuffer("abc");
System.out.println(s1.append("abcd")); // append(str) 在字符串末尾追加str
System.out.println(s1.delete(0, 4)); // delete(起始,截止] 左边为开区间,右边是闭区间 删除区间内的字符
System.out.println(s1.deleteCharAt(2)); // delete (index:下标) 删除下标所在的字符
System.out.println(s1.indexOf("abcd")); // indexof(String str)返回str 第一次出现的位置
System.out.println(s1.capacity()); // capacity() 获得底层保存字符串的空间大小
System.out.println(s1.substring(2)); // substring(int start) 将start下标开始的字符以string的方式返回
System.out.println(s1.substring(0,3)); // substring(int start,int end) 截取start下标开始到end下标的字符以string的方式返回
System.out.println(s1.replace(1,4,"abc")); // replace(start,end,str) 从起始下标到截止下标的字符用str替代
System.out.println(s1.reverse()); // reverse() 将字符串旋转abc——>cba然后以String的方式返回
System.out.println(s1.lastIndexOf("abcd")); // lastIndexOf(str:) 找到最后一次出现str的位置并且返回
System.out.println(s1.lastIndexOf("abcd",0)); // lastIndexOf(str,fromindex) 从fromIndex 位置开始寻找最后一次str出现的位置*/
s1.ensureCapacity(20); //扩容
s1.toString(); //将整个字符串以String方式返回
}
Tips:
StringBuffer和String不能直接进行转换,要想相互转换就得遵守一下规则:
StringBuffer——>String:调用toString()方法
String——>StringBuffer:利用StringBuffer的构造方法或者调用append()方法
经典面试题:
String、 StringBuffer和StringBuilder三者有何区别?
答:1、String的内容不可修改,而 StringBuffer和StringBuilder的内容可以修改
2、StringBuffer与StringBuilder大部分功能是相似的
3、StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作
本次博客内容分享到此啦!!请您看完后不要吝啬您的三连!!