目录
3.与String类的转换(以StringBuilder为例)
4.String,StringBuilder,StringBuffer的区别
一.认识String类
- String是Java中的字符串,字符串是一个特殊的对象,属于引用类型。
在Java中,String类对象创建后,字符串一旦初始化就不能更改,因为string类中所有字符串都是常量,数据是无法更改,由于string对象的不可变,所以可以共享。对String类的任何改变,都是返回一个新的String类对象。
二.常用方法
1.字符串构造
关于字符串的对象创建方法,常用的有四种方式:
a.使用字符串的常量直接赋值
//使用常量串构造
String s1="hhh";
System.out.println(s1);
b.通过字符串常量的构造方法
//直接newString对象
String s2=new String("hhh");
System.out.println(s2);
c.通过字符串数组进行构造
//使用字符数组进行构造
char[] array={'h','h','h'};
String s3=new String(array);
System.out.println(s3);
d.调用字符串的valueOf方法
//调用字符串的valueOf方法
String s4=String.valueOf("hhh");
System.out.println(s4);
注意
- Java中String是引用数据类型,保存的只是地址。
- 在Java中""括起来的都是字符串常量,也是String类型对象。
2.两个字符串对象的比较
(1).通过==比较。(比较的是两个字符串的地址,不看内容)
(2).通过equals方法来比较两个字符串对象的内容是否相等(大小敏感)。(返回boolean)
(3).通过equalsIgnoreCase比较两个字符串对象的内容是否相等(大小写不敏感)。(返回boolean)
(4).通过compareTo方法来比较两个字符串对象的大小关系。(返回int)
compareTo方法的比较规则:
- 首先按照两个字符串的字典序大小比较,若出现不相等的字符,直接返回两个字符的大小差值(字符编码差值)
- 若前k个字符相等(k是两个字符长度的最小值),返回长度差。
三.字符串常用的操作
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 |
(1).在字符串中查找指定的字符
char charAt(int index);
返回字符串中指定索引index位置的字符。
//字符串查找
String s1="hello";
//注意越界问题
char c=s1.charAt(2);
System.out.println(c);
注意:下标不要越界。
(2).contains方法
boolean contains(String s);
判断字符串中是否有指定的字串,若有返回true,否则返回false。
2.字符串转化
(1).valueOf方法
将其他类型转化为字符串对象,可以接收所有参数,当传入的是第三方对象时,默认调入toString方法。
String s1=String.valueOf(123);
String s2=String.valueOf(12.34);
String s3=String.valueOf(false);
//当传入的是第三方对象时,默认调入toString方法
String s4=String.valueOf(new Student("生菜虾",13));
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
System.out.println(s4);
(2).大小写转化
方法 | 功能 |
String toUpperCase() | 字符串转大写 |
String toLowerCase() | 字符串转小写 |
//大小写转换
String s1="aBcdE";
String s2="FghIj";
System.out.println(s1.toUpperCase());
System.out.println(s2.toLowerCase());
(3).字符串转为字符数组
a.转为字符数组
toCharArray();
//转为字符数组
String s1="hello";
char[] c=s1.toCharArray();
System.out.println(Arrays.toString(c));
b.转为字节数组getBytes(可以传入指定的字符编码)
getBytes(/*可以传入指定的字节编码*/);
(4).格式化字符串
//类似于C的printf,但是支持正则表达式
String.format();
//格式化输出
String str=String.format("%d-%d-%d",2022,10,2);
System.out.println(str);
3.字符串的替换
方法 | 功能 |
String replaceAll(String regex, String replacement) | 替换所有的指定内容 |
String replaceFirst(String regex, String replacement) | 替换收个内容 |
regex为替换前的内容,replacement为替换后的内容。
//字符串替换
String str="hello world";
System.out.println(str.replaceAll("l","_"));
System.out.println(str.replaceFirst("l","_"));
4.字符串拆分
字符串的拆分,将一个字符串按照指定的格式拆分成数组。
注意:当碰到某些特殊字符无法正确拆分时,需要使用转义字符 \ 。
方法 | 功能 |
String[] split(String regex) | 将字符串全部拆分 |
String[] split(String regex, int limit) | 将字符串以指定的格式,拆分为limit组 |
//字符串拆分
String str="hello world hello Java";
String[] data1=str.split(" ");
String[] data2=str.split(" ",2);
System.out.println(Arrays.toString(data1));
System.out.println(Arrays.toString(data2));
5.字符串截取
方法 | 功能 |
String substring(int beginIndex) | 从指定索引截取到结尾 |
String substring(int beginIndex, int endIndex) | 截取部分内容 |
注意:
- 索引从0开始。
- 注意前闭后开区间的写法, substring(0, 5) 表示包含 0 号下标的字符, 不包含 5 号下标。
//字符串截取
String str="helloworld";
System.out.println(str.substring(5));
System.out.println(str.substring(5,9));
6.其他方法
方法 | 功能 |
String trim() | 去掉字符串中的左右空格,保留中间空格 |
String toUpperCase() | 字符串转大写 |
String toLowerCase() | 字符串转小写 |
7.举个例子
判断用户输入的字符是否为纯数字字符,是则返回true,不是则返回false。
判断字符是否是数字字符
Character.isDigit(c);
char的包装类Character中,有一个静态方法,判断用户输入的字符串是否由纯数字组成,是则返回true,不是则返回false。
public static boolean isDigit(String str){
//边界条件,字符串为空null,或者内容为空
if(str==null||str.length()==0){
return false;
}
//将字符串拆分成一个一个字符来判断
for(int i=0;i<str.length();i++){
char c=str.charAt(i);
//出现不是数字的字符,直接退出
// 第一种方式
if(!(Character.isDigit(c))) {
return false;
}
// 第二种方式
// if(c<'0'||c>'9'){
// return false;
// }
}
//循环完成,没有退出,是纯数字字符
return true;
}
四.关于字符串常量的理解
采用直接赋值类似"String str=字符串字面量"的方式创建字符串对象,会使用字符串的常量池。
- 若该对象是第一次使用,就在堆中开辟新空间,产生新对象,产生之后将该对象置入字符串的常量池。
- 当下次再次使用该对象时(还是采用直接赋值的方式),若常量池中已有该内容的字符串,直接引用常量池中的对象,不再创建字符串。
只有直接赋值的方式会用到字符串的常量池。
- 池化思路:是程序设计中的常用思路,有字符串常量池,数据库常量池,线程池等等。
- 看到池:节省内存,提高效率。字符串字面量经常会被重复利用,因此放入常量池,当再次使用该内容时,省去了创建对象,开辟内存的时间,直接复用已有对象。
eg:
//常量池
String s="hello";
String s1="hello";
String s2=new String("hello");
String s3=new String("hello");
System.out.println(s==s1);//true
System.out.println(s==s2);//false
System.out.println(s==s3);//false
字符串中的intern方法
调用这个方法,若产生的字符串常量池中没有,则会将产生的字符串对象置入常量池,若常量池中已存在该对象,则返回常量池中的字符串对象引用。
注意:为了避免歧义,最好接收一下intern方法的返回值。
//intern方法
String str="hello";
String str1="hello";
String str2=new String("hello");
String str3=str2.intern();
System.out.println(str==str1);//true
System.out.println(str==str2);//false
System.out.println(str==str3);//true
五.字符串的不可变性
1.定义
当一个字符串的对象产生后,他的内容就无法修改,这称之为字符串常对象的不可变性。
2.示例
我们来看如下程序的运行:
//字符串对象的不可变性
String str="hello";
str+="world";
str+="!!!";
//这里其实是字符串的引用在改变,实际字符串的对象并没有改变
System.out.println(str);
可以发现,这里的字符串对象好像 " 变了 " ,但是实际上,我们都知道字符串在Java中是引用数据类型,所以这里并不是字符串对象改变了,而是字符串的引用改变了!
画图理解:
3.字符串对象的内容无法修改
问:如何理解字符串对象不可变,是如何做到内容无法修改的?
来看一下String的源码:
value字符数组就是用来保存字符串的内容的。
可以看到,字符数组被private和final关键字所修饰,这时很多人都会认为,是final关键字导致了字符数组不能再被修改,实则不然,final关键字的作用是为了保证字符串对象的线程安全问题,final修饰引用数据类型的变量,不能修改这个引用的指向,但是内容还是可变的!
final关键字的介绍及用法可查阅:final关键字的介绍及用法
示例:
final char[] c={'a','b','c'};
//内容可以改变
c[0]='h';
System.out.println(Arrays.toString(c));
//引用无法改变
//char[] c1={'e','f','g'};
//c=c1;
那么是什么导致字符串对象的内容不可变呢?
根本就在于private关键字的修饰,当一个类中的某个被private修饰的属性或方法我们想要进行调用时,就需要调用该类提供的能够获取该属性的方法(getter和setter方法),否则外部无法对其进行操作。
在String类中,把value数据完全封装,对外没有提供任何获取或修改其内容的方法,因此字符串对象一旦产生,内容就无法修改!
这样做的目的是什么呢?
- 方便实现字符串对象池. 如果 String 可变, 那么对象池就需要考虑写时拷贝的问题了。
- 不可变对象是线程安全的。
- 不可变对象更方便缓存 hash code, 作为 key 时可以更高效的保存到 HashMap 中。
String类上一把刀,他的对象不可变
我们现在知道,String类的对象是无法修改的,那么当我们想要修改字符串对象时,在String类中,所有的修改都会创建新对象,效率非常低下。所以,要尽量避免直接对String类型对象进行修改。
那如果某个场景需要反复修改字符串内容,该如何操作呢?
推荐使用Java提供的StringBuffer或者StringBuilder。
六.StringBuffer和StringBuilder
1.介绍
- StringBuffer:线程安全,效率低,适用于10%需要保证线程安全的场景。
- StringBuilder:线程不安全,效率高,适用于90%的场景。
2.常用方法(以StringBuilder为例)
方法 | 说明 |
StringBuff 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最后一次出现的位置 |
StringBuff 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)位置的字符替换为str |
String substring(int start) | 从start开始一直到末尾的字符以String的方式返回 |
String substring(int start,int end) | 将[start, end)范围内的字符以String的方式返回 |
StringBuffer reverse() | 反转字符串 |
String toString() | 将所有字符按照String的方式返回 |
3.与String类的转换(以StringBuilder为例)
(1).String->StringBuilder
a.通过StringBuilder构造方法
b.通过StringBuilder的append方法
//String->StringBuilder
//a.通过StringBuilder构造方法
String str="hello";
StringBuilder s=new StringBuilder(str);
System.out.println(s);
//b.通过StringBuilder的append方法
StringBuilder s1=new StringBuilder();
s1.append(str);
System.out.println(s1);
(2).StringBuilder->String
调用toString方法
//StringBuilder->String
//调用toString方法
StringBuilder s2= new StringBuilder("abc");
String ret=s2.toString();
System.out.println(ret);
4.String,StringBuilder,StringBuffer的区别
- String的内容不可修改,StringBuffer与StringBuilder的内容可以修改。
- StringBuffer与StringBuilder大部分功能是相似的。
- StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作。
如有建议或想法,欢迎一起讨论交流~