因为字符串应用的广泛,所以Java中提供了String类来对字符串进行操作
我们在刷题的时候,大多数题目都是关于字符串的,所以如果更进一步的了解String类,对于我们日常刷题也是达到了事半功倍的作用,除了面试笔试题的涉及,String类在面试中也是常客
所以大家跟着我一起来学习String类吧!
String类常用的构造方法有这三种(想查看更多的构造方法可以在官方文档中查):
String s = "hello"; //直接使用字符串常量
String s = new String("hello"); //通过new String传递一个字符串常量
char[] chars = {'h','e','l','l','o'};
String s = new String(chars); //通过new String传递一个字符数组
注意:String类是引用类型,内部并不存储字符串本身,而是存的是对象的地址
看一下String类的源码:发现字符串实际保存在成员变量字符数组value
中
==
比较,对于基本类型来说比较的是值是否相等,对于引用类型来说比较的是地址是否相同 int a = 10;
int b = 10;
int c = 20;
System.out.println(a==b); //true 值相等
System.out.println(a==c); //false 值不相等
String s1 = new String("a");
String s2 = new String("a");
String s3 = s2;
System.out.println(s1==s2); //false 地址不同为false
System.out.println(s2==s3); //true 地址相同为true
equals
方法比较equals方法是Object类中的方法,默认是按照==也就是地址比较,但是String类重写了equals方法,使得可以比较字符串的内容是否相等
String str1 = new String("hello");
String str2 = new String("hello");
String str3 = new String("world");
System.out.println(str1.equals(str2)); //true
System.out.println(str2.equals(str3)); //false
注意: a.equals(b),此时a不能为null,否则会发生空指针异常
String m = null;
String n = "a";
System.out.println(m.equals(n));
compareTo
方法比较前面两种比较都是比较是否相同,而compareTo可以比较大小,它的返回值类型为int,它比较大小的方式是按照字典次序比较,返回比较字符的次序差值(如果前面字符相同,返回长度差值)
String s1 = "abc";
String s2 = "abcd";
int ret = s1.compareTo(s2); //-1,因为前面字符相同,差值为长度差
System.out.println(ret);
String s3 = "a";
String s4 = "d";
System.out.println(s4.compareTo(s3)); //3 d比a多了3
注意: a.compareTo(b),a和b都必须不能为null,否则会发生空指针异常
compareToIgnoreCase
方法比较该方法与上述compareTo方法比较方式相同,只是忽略了大小写进行比较
字符串查找是字符串使用最频繁的操作的之一,经常刷题的小伙伴可以感受的到哦
方法 | 功能 |
---|---|
char charAt(int index) | 获取index位置上的字符 |
int indexOf(int c) | 返回c第一次出现的位置,没有返回-1 |
int indexOf(int c,int fromIndex) | 从fromIndex位置开始找,返回c第一次出现的位置,没有返回-1 |
int indexOf(String str) | 返回字符串str第一次出现的位置,没有返回-1 |
int indexOf(String str,int fromIndex) | 从fromIndex位置开始找返回字符串str第一次出现的位置,没有返回-1 |
int lastindexOf(int c) | 从后往前找,返回c第一次出现的位置,没有返回-1 |
int lastindexOf(int c,int fromIndex) | 从fromIndex位置开始从后往前找,返回c第一次出现的位置,没有返回-1 |
int lastindexOf(String str) | 从后往前找,返回字符串str第一次出现的位置,没有返回-1 |
int lastindexOf(String str,int fromIndex) | 从fromIndex位置开始从后往前找,返回字符串str第一次出现的位置,没有返回-1 |
String s = "abcdef";
System.out.println(s.charAt(2)); //c
System.out.println(s.indexOf('b')); //1
System.out.println(s.indexOf('d',1)); //3
System.out.println(s.indexOf("cde")); //2
System.out.println(s.lastIndexOf("bcd")); //1
//数值转化为字符串
int a = 10;
double b = 20;
String sa = String.valueOf(a); //10
String sb = String.valueOf(b); //20.0
//字符串转为数值
String s = "10";
int is = Integer.valueOf(s); //字符串转为Integer,Integer->int为自动拆箱,所以可以用int直接接收
int is1 = Integer.parseInt(s); //将字符串解析为整形,返回值为int
double ds = Double.valueOf(s);
double ds1 = Double.parseDouble(s);
注意: 字符串转数值类型的时候,字符串必须是可转的,否则会报类型转化异常
String ss = "12b";
int ssi = Integer.valueOf(ss);
toUpperCase()
:将字符串所有的字符转换为大写
toLowerCase()
:将字符串所有的字符转换为小写
String str = "abcdEF";
System.out.println(str.toLowerCase()); //abcdef
System.out.println(str.toUpperCase()); //ABCDEF
String s = "hello";
char[] c = s.toCharArray(); //字符串转换为字符数组
String ss = new String(c); //字符数组转换为字符串
int[] a = {1,2,3};
String sa = Arrays.toString(a); //数组转为字符串,带中括号 [1,2,3]
System.out.println(sa);
String str = String.format("%d-%d-%d",2023,2,25);
System.out.println(str); //2023-2-25
替换可以替换所有指定的内容,也可以替换第一次出现的内容
String s = "hello";
String ret = s.replaceAll("l","a"); //替换所有,将l替换为a
System.out.println(ret); //heaao
String ret1 = s.replaceFirst("l","a"); //替换第一次出现的l为a
System.out.println(ret1); //healo
提示:字符串是不可变的,所以每次对字符串进行修改后,会
重新生成一个String对象
比如有这样的一个字符串:“张三,李四,王五”,我们需要将该字符按逗号进行分割
String str = "张三,李四,王五";
String[] array = str.split(",");
for(String i : array){
System.out.println(i);
}
注意: 有些特殊的字符作为分割符号需要加上转义符号
String str1 = "a*b*c*d";
String[] arr = str1.split("\\*"); //*得使用转义符
String str2 = "a-b-c+d-e-f+g-h-i";
String[] arr1 = str2.split("\\+|-"); //按照+和-分割
方法 | 功能 |
---|---|
String substring(int index) | 从index位置开始一直截取到末尾 |
String substring(int start,int end) | 从start位置开始截取到end位置,不包括end位置的字符 |
String s = "abcdef";
String ret1 = s.substring(2); //cdef 从位置2截取至末尾
String ret2 = s.substring(2,4); //cd 截取区间为[2,4)
System.out.println(ret1); //cdef
System.out.println(ret2); //cd
使用trim()
方法可以去除字符串两边的空格
注意: 不能去掉字符串中间的空格
String ss = " abc def ";
String ret3 = ss.trim();
System.out.println(ret3); //abc def
观察下面一段代码,思考:为什么s1和s2引用的是同一个对象,而s3和s4不是呢?
String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");
String s4 = new String("hello");
System.out.println(s1==s2); //true
System.out.println(s1==s3); //false
System.out.println(s3==s4); //false
}
在Java中,为了使程序执行的更快并节省空间,Java为String类提供了字符串常量池
字符串常量池在JVM中是一个StringTable,实际是一个固定大小的HashTable,在Java8中字符串常量池的位置在堆
中
String s1 = "hello";
String s2 = "hello";
String s1 = "hello";
String s2 = "hello";
String s3 = new String("world");
String s4 = new String("world");
说明:只要是new出来的对象,都是唯一的,通过上面的例子,可以发现使用常量池的对象创建String字符串的方法效率更高,而且更节省空间
intern方法是一个native
方法(底层使用C++实现),作用是手动的将创建的String对象添加到字符串常量池中
看下面一段代码:
char[] c = {'h','e','l','l','o'};
String s1 = new String(c);
//s1.intern();
String s2 = "hello";
System.out.println(s1==s2); //false
发现结果为false,因为通过字符数组创建String对象底层是将char数组进行拷贝,不会加入到常量池中,观察此时的内存分布:
将intren方法打开,观察结果:
char[] c = {'h','e','l','l','o'};
String s1 = new String(c);
s1.intern();
String s2 = "hello";
System.out.println(s1==s2); //true
String是一个不可变的对象,也就是内容一旦定义就不可以改变,那为啥是不可变的呢?
看一下String底层的源码:
发现字符数组使用
private final
修饰的,使用final修饰说明指向不能改变,使用private修饰说明对外不提供访问接口,别的类中不能直接访问到字符数组,所以是不可变的
那字符串是如何进行修改的呢?
String s = "hello ";
s += "world";
System.out.println(s);
说明:底层会创建一个StringBuilder对象,调用append对象进行字符串拼接,然后通过StringBuilder对象的toString方法构造一个新的String对象,将新构造的String对象的引用赋值给s
因为String对象的不可变性,为了方便字符串的修改,Java中提供了StringBuilder和StringBuffer类
StringBuilder的构造方法:
方法 | 功能 |
---|---|
StringBuilder() | 构造一个空的StringBuilder对象 |
StringBuilder(String s) | 使用字符串s构造StringBuilder对象 |
StringBuilder的常用方法:
方法 | 功能 |
---|---|
StringBuilder() | 构造一个空的字符串构建器 |
int length() | 返回构建器中代码单元的数量 |
StringBuilder append(String str) | 追加一个字符串 |
StringBuilder append(char c) | 追加一个代码单元 |
void setCharAt(int i,char c) | 将第i个代码单元设置为c |
StringBuilder insert(int offset,String str) | 在offset位置插入一个字符串 |
StringBuilder insert(int offset,char c) | 在offset位置插入一个代码单元 |
StringBuilder delete(int starIndex,int endIndex) | 删除偏移量从starIndex到endIndex-1的代码单元 |
StringBuilder reverse() | 反转字符串 |
String toString() | 将所有字符按照String方式返回 |
String substring(int start) | 从start开始一直到末尾的字符以String的方式返回 |
StringBuilder s = new StringBuilder("hello");
s.append(' ');
s.append("world");
System.out.println(s); //hello world
System.out.println(s.length()); //11
s.insert(6,"and");
System.out.println(s); //hello andworld
s.reverse();
System.out.println(s); // dlrowdna olleh
System.out.println(s.substring(5)); //dna olleh
System.out.println(s.toString()); //dlrowdna olleh
String类与StringBuilder类的转换:
String s = "hello";
StringBuilder sb = new StringBuilder(s); //String -> StringBuilder
String str = sb.toString(); //StringBuilder -> String
StringBuffer和StringBuilder的功能差不多,只不过是StringBuffer在多线程环境下是线程安全的,而StringBuilder是不安全的
StringBuffer的关键方法都加了synchronized关键字保证线程安全:
String是不可变的,其内容不可修改,StringBuilder和StringBuffer是可变的,内容可以修改
StringBuffer是线程安全的,其关键方法使用synchronized关键字修饰,StringBuilder是线程不安全的
String str1 = new String("abc");
String str2 = new String("hello") + new String("world");
- str1:一个new出来的String对象,一个常量池中的对象,一共是
2
个- str2:两个new出来的String对象,两个常量池中的对象,拼接会创一个StringBuilder对象,StringBuilder对象转换为String时又会创建一个String对象,将String对象的引用赋值给str2,所以一共是
6
个