文章目录
- 写在前面
- 1 String类的常用方法
- 1.1 字符串的构造
- 1.2 String对象的比较
- 1. 利用 == 比较是否引用同一对象
- 2. 利用equals() 方法比较
- 3. 利用compareTo 方法比较两个字符串的
- 4.利用compareToIgnoreCase方法比较(忽略大小写)
- 1.3字符串查找
- 1.4转化
- 1. 数值和字符串的转化
- 2. 大小写转化
- 3. 字符串转数组
- 4. 格式化
- 1.5 字符串的替换
- 1.6 字符串拆分
- 1.7 字符串截取
- 1.8 其他操作方法
- 1.9 字符串常量池
- 1.9.1 创建对象的思考
- 2. 字符串修改
- 2.1 StringBuilder和StringBuffer
- 2.2 面试题
写在前面
字符串广泛应用 在 Java 编程中,在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。在开发和校招中, 字符串也是常客, 所以这一章节也非常重要, 在本章节中, 我们需要掌握的是:
- 认识String类
- 了解String类的基本用法
- 熟练掌握String的常见操作
- 认识字符串常量池
- 认识 StringBuffer 和 StringBuilder
1 String类的常用方法
1.1 字符串的构造
String 类提供的构造方法有很多, 我们首先掌握以下常见的三种:
public static void main(String[] args) {
// 使用常量串构造, 直接使用双引号引起来就行
String s1 = "hello would";
// 直接new一个String对象
String s2 = new String("hello would");
//使用字符数组进行构造
char[] array = {'h','e','l','l','o',' ','w','o','u','l','d'};
String s3 = new String(array);
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
}
这里我们虽然打印出的效果是一样的, 可是还是有一定区别,
注意
1.String 是引用类型, 内部并不储存字符串本身, 在String类的实现源码中, String类实例变量如下:
public static void main(String[] args) {
// s1和s2引用的是不同的对象, s1和s3引用的同一对象
String s1 = new String("hello");
String s2 = new String("would");
String s3 = s1;
System.out.println(s1.length()); //获取字符串的长度
System.out.println(s1.isEmpty()); //如果字符串的长度为0, 返回true, 否则返回false
}
2.在Java中, 用双引号引起来的也是String类
比如: “hello”
System.out.println("hello".lenth());
1.2 String对象的比较
字符串的比较是比较常见的操作之一, 比如: 字符串比较,
Java提供4中比较方法:
1. 利用 == 比较是否引用同一对象
注意: 对于内置对象, ==比较的是内在的值, 而引用类型, == 比较的是引用中的地址
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = 10;
System.out.println(a==b); // false
System.out.println(a==c); // true
String s1 = new String("hello");
String s2 = new String("would");
String s3 = new String("hello");
String s4 = s1;
System.out.println(s1==s2); //false
System.out.println(s1==s3); //false
System.out.println(s1==s4); //true
}
2. 利用equals() 方法比较
首先我们看一下这个方法的源码
String类重写的父类Object的equals方法, Object中equals方法默认按照==比较, String重写了equals方法后, 就是按照上述源码中的方法比较, 下面我们具体来看:
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两个字符串的长度是否相同, 是则继续比较, 否则返回true
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;
}
下面我们看一个实例:
public static void main(String[] args) {
String s1 = new String("hello");
String s2 = new String("hello");
String s3 = new String("would");
// s1 s2 s3 分别引用的是不同的对象, 所以==比较结果全是false
System.out.println(s1==s2); //false
System.out.println(s1==s3); //false
//equals 比较, String对象中的各个字符
//虽然s1和s2引用的不是一个对象, 但是它们两个对象中存放的内容是相同的, 所以输出true
//s1和s3 不是一个对象, 而且两个对象存放的内容也不同, 所以输出false
System.out.println(s1.equals(s2)); //true
System.out.println(s1.equals(s3)); //false
}
3. 利用compareTo 方法比较两个字符串的
与equals方法不同的是, equals返回的是boolean类型, compareTo方法返回的是int类型.
具体比较方式:
1.先按照字典序大小比较, 出现不等的字符, 直接返回这两个字符的大小差值.
2.如果前k个字符相等(k为这两个字符串的长度最小值),返回这两个字符串的长度差
public static void main(String[] args) {
String s1 = new String("abc");
String s2 = new String("aBc");
String s3 = new String("aD");
String s4 = new String("abcdef");
String s5 = new String("ab");
System.out.println(s1.compareTo(s2)); //第二个字符不相等, 返回这两个字符的差值32
System.out.println(s1.compareTo(s3)); //第二个字符不相等, 返回这两个字符的差值30
System.out.println(s1.compareTo(s4)); //前k个字符都相等, 返回这两个字符串的长度差-3
System.out.println(s1.compareTo(s5)); //前k个字符都相等, 返回这两个字符串的长度差1
}
4.利用compareToIgnoreCase方法比较(忽略大小写)
public static void main(String[] args) {
String s1 = new String("abc");
String s2 = new String("aBc");
String s3 = new String("aD");
String s4 = new String("abcdef");
String s5 = new String("ab");
System.out.println(s1.compareToIgnoreCase(s2)); //0
System.out.println(s1.compareToIgnoreCase(s3)); //-2
System.out.println(s1.compareToIgnoreCase(s4)); //-3
System.out.println(s1.compareToIgnoreCase(s5)); //1
}
1.3字符串查找
字符串查找也是字符串中非常常见的操作, String类提供的常见的查找方法:
方法 | 功能 |
---|---|
char charAt(int index) | 返回index位置上字符,如果index为负数或者越界,抛出IndexOutOfBoundsException异常 |
int indexOf(int ch) | 返回ch第一次出现的位置,没有返回-1 |
int indexOf(int ch, intfromIndex) | 从fromIndex位置开始找ch第一次出现的位置,没有返回-1 |
int indexOf(String str) | 返回str第一次出现的位置,没有返回-1 |
int indexOf(String str, intfromIndex) | 从fromIndex位置开始找str第一次出现的位置,没有返回-1 |
int lastIndexOf(int ch) | 从后往前找,返回ch第一次出现的位置,没有返回-1 |
int lastIndexOf(int ch, intfromIndex) | 从fromIndex位置开始找,从后往前找ch第一次出现的位置,没有返回-1 |
int lastIndexOf(String str) | 从后往前找,返回str第一次出现的位置,没有返回-1 |
int lastIndexOf(String str, intfromIndex) | 从fromIndex位置开始找,从后往前找str第一次出现的位置,没有返回-1 |
public static void main(String[] args) {
String s = new String("aabbccdefgh");
System.out.println(s.charAt(4)); //打印你所查找位置的上的元素c
System.out.println(s.indexOf("b")); //打印你查找的元素第一次出现的位置2
System.out.println(s.indexOf("b", 3)); //从你所选位置开始查找你所需元素 3
System.out.println(s.indexOf("bcc"));//打印你查找的字符串第一次出现的位置3
System.out.println(s.lastIndexOf("c")); //从后往前找,第一次出现的位置5
System.out.println(s.lastIndexOf("b", 2));//从你所选位置从后往前找
}
**注意:**上述方法都是实例方法
1.4转化
1. 数值和字符串的转化
public static void main(String[] args) {
//数字转字符串
String s1 = String.valueOf(1234); //int
String s2 = String.valueOf(12.32); //double
String s3 = String.valueOf(true); //boolean
//字符串转数字
int data1 = Integer.parseInt("1234");
double data2 = Double.parseDouble("20.23");
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
System.out.println(data1);
System.out.println(data2);
}
2. 大小写转化
public static void main(String[] args) {
String s1 = new String("HELLO");
String s2 = new String("would");
System.out.println(s1.toLowerCase()); //hello
System.out.println(s2.toUpperCase()); //WOULD
}
3. 字符串转数组
public static void main(String[] args) {
String s1 = new String("HELLO WOULD");
//字符串转数组
char[] ch = s1.toCharArray();
for (int i = 0; i < ch.length; i++) {
System.out.println(ch[i]);
}
System.out.println("=============================");
// 字符串转数字
int data1 = Integer.parseInt("1234");
double data2 = Double.parseDouble("12.14");
System.out.println(data1+1); //1235
System.out.println(data2+0.1);//12.24
}
4. 格式化
public static void main(String[] args) {
String s = String.format("%d-%d-%d", 2023, 04,18);
System.out.println(s);
}
1.5 字符串的替换
使用一个指定的字符串替换掉已有的字符串数据,方法如下:
方法 | 功能 |
---|---|
String replaceAll(String regex, String replacement) | 替换所有的指定内容 |
String replaceFirst(String regex, String replacement) | 替换首个内容 |
代码示例:
public static void main(String[] args) {
//字符串的替换
String str = "hello";
System.out.println(str.replaceAll("l", "8"));
System.out.println(str.replaceFirst("l", "8"));
}
这里我们需要注意的是:字符串是一个不可变对象替换的时候不修改原来的字符串, 而是产生一个新的字符串
1.6 字符串拆分
就是将一个完整的字符串按照指定的分隔符划分为若干个子字符串.
方法 | 功能 |
---|---|
String[] split(String regex) | 将字符串全部拆分 |
String[] split(String regex, int limit) | 将字符串以指定的格式,拆分为limit组 |
实现字符串的拆分处理:
public static void main(String[] args) {
//字符串的拆分
String str = "hello would hello would";
String[] result = str.split(" "); //按照空格拆分
for (String s:result) {
System.out.println(s);
}
}
字符串的部分拆分
public static void main(String[] args) {
//字符串的部分拆分
String str = "hello would hello would";
String[] split = str.split(" ",3); //按空格分为三部分
for (String s:split) {
System.out.println(s);
}
拆分是特别常见的操作, 我们一定要重点掌握,
还有一些特殊字符作为分隔符可能不能做出正确的拆分, 需要加上转义
拆分ip地址
String str = "192.168.1.1" ;
String[] result = str.split("\\.") ; //这里我们就要用到转义字符才能正确分割
for(String s: result) {
System.out.println(s);
}
注意事项:
- 字符"|“, “.”, “*”, “+”; 前面加上”\"
- 二如果是"“, 那就得写成”\\"
- ** 如果字符串中有多个分隔符, 用"|"作为连字符**
多次拆分
public static void main(String[] args) {
//字符串的多次拆分
String str = "name=张三&age=18";
String[] result = str.split("&");
for (int i = 0; i < result.length; i++) {
String[] temp = result[i].split("=");
System.out.println(temp[0]+"="+temp[1]);
}
1.7 字符串截取
从一个完整字符串截取出部分内容
方法 | 功能 |
---|---|
String substring(int beginIndex) | 从指定索引截取到结尾 |
String substring(int beginIndex, int endIndex) | 截取部分内容 |
public static void main(String[] args) {
String str = "hellowould";
System.out.println(str.substring(5)); //would
System.out.println(str.substring(0, 5)); // hello
}
注意:
- 索引从0开始
- 这里的写法是**前闭后开**, substring(0,5),表示包含0下标的值, 不包括5下标的字符
1.8 其他操作方法
方法 | 功能 |
---|---|
String trim() | 去掉字符串中的左右空格,保留中间空格 |
String toUpperCase() | 字符串转大写 |
String toLowerCase() | 字符串转小写 |
trim方法实例:
public static void main(String[] args) {
String str = " hello would ";
System.out.println("["+str+"]"); //[ hello would ]
System.out.println("["+str.trim()+"]"); //[hello would]
}
字符转大小写实例
public static void main(String[] args) {
//字符串大小写转化
String str = "12abc&^%*34DEF";
System.out.println(str.toUpperCase()); //12ABC&^%*34DEF
System.out.println(str.toLowerCase()); //12abc&^%*34def
}
注意:
这两个函数只转换字母
1.9 字符串常量池
1.9.1 创建对象的思考
public static void main(String[] args) {
String str1 = "hello";
String str2 = "hello";
String str3 = new String("hello");
String str4 = new String("hello");
System.out.println(str1==str2); //true
System.out.println(str1==str3); //false
System.out.println(str3==str4); //false
}
上面我们创建方法类似, 为什么s1和s2 引用的是同一个对象, 而s3 和s4 不是呢?
在java程序中, 类似于:1, 2, 3, 4, 5, 6, 3.14 “hello” 等字面类型的常量会经常频繁的使用, 为了使程序运行的更快, 更节省内存,java为8种基本数据类型和String类都提提供了常量池.
为了节省存储空间和提高程序的运行效率, Java中引入了:
- **Class文件常量池:**每个Java源文件编译后生成 .class文件中会保存当前类中的字面常量和字符信息
- **运行时常量池:**在.class文件被加载时, .class文件中的常量池被加载到内存中被称为运行时常量池, 运行时常量池每个类都有一份.
- 字符串常量池
2. 字符串修改
在这里我们首先要知道:
- String类在设计时就是不可改变的,String类实现描述就已经说明了
- String类中的字符实际保存在内部维护的value字符数组中
- 所有涉及到可能修改字符串内容的操作都是创建一个新对象,改变的是新对象
- final修饰类表明该类不想被继承,final修饰引用类型表明该引用变量不能引用其他对象,但是其引用对象中的内
容是可以修改的(返回创建的新对象)。
那为什么我们还要谈字符串修改呢?
原因是:我们应该**尽量避免直接对String类型对象进行修改,因为String类是不能修改的,所有的修改都会创建新对象,效率
非常低下**。
public static void main(String[] args) {
String str = "hello";
str += "would";
System.out.println(str); // hellowould
}
这种方法不推荐使用, 其效率非常低下, 中间创建了许多临时对象
下面我们做一个对比
public static void main(String[] args) {
//传统修改字符串的方式
long start = System.currentTimeMillis(); //开始时间
String str = "0";
for (int i = 0; i < 100000; i++) {
str += i;
}
long end = System.currentTimeMillis(); //结束时间
System.out.println(end-start); //修改完用时:18619
//StringBuffer
start = System.currentTimeMillis();
StringBuffer str1 = new StringBuffer("0");
for (int i = 0; i < 100000; i++) {
str1.append(i);
}
end = System.currentTimeMillis();
System.out.println(end-start); //修改完用时:2
//StringBuilder
start = System.currentTimeMillis();
StringBuilder str2 = new StringBuilder("0");
for (int i = 0; i < 100000; i++) {
str2.append(i);
}
end = System.currentTimeMillis();
System.out.println(end-start); //修改完用时:1
}
在这里我们可以看出, 在对String类进行修改的时候, 效率是非常慢的, 因此我们要尽量对String的直接修改, 如果要修改, 建议使用StringButter和StringBuilder
2.1 StringBuilder和StringBuffer
由于String的不可更改特性,为了方便字符串的修改,Java中又提供StringBuilder和StringBuffer类。这两个类大
部分功能是相同的,下面介绍 StringBuilder常用的一些方法,其它需要用到了大家可参阅StringBuilder在线文档
方法 | 功能 |
---|---|
StringBuff append(Stringstr) | 在尾部追加,相当于String的+=,可以追加:boolean、char、char[]、 double、float、int、long、Object、String、StringBuff的变量 |
char charAt(int index) | 获取index位置的字符 |
int length() | 获取字符串的长度 |
int length() | 获取字符串的长度 |
int capacity() | 获取底层保存字符串空间总的大小 |
mininmumCapacity) | 扩容 |
void setCharAt(int index,char ch) | 将index位置的字符设置为ch |
int indexOf(String str) | 返回str第一次出现的位置 |
int indexOf(String str, intfromIndex) | 从fromIndex位置开始查找str第一次出现的位置 |
int lastIndexOf(String str) | 返回最后一次出现str的位置 |
int lastIndexOf(String str, | |
int fromIndex) | 从fromIndex位置开始找str最后一次出现的位置 |
StringBuff insert(intoffset, String str) | 在offset位置插入:八种基类类型 & String类型 & Object类型数据 |
StringBuffer | |
deleteCharAt(int index) | 删除index位置字符 |
StringBuffer delete(intstart, 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的方式返回 |
public static void main(String[] args) {
//字符串修改的应用
StringBuffer sb1 = new StringBuffer("hello");
StringBuffer sb2 = sb1;
sb1.append(' '); //尾插
sb1.append("would");
System.out.println(sb1); //hello would
sb1.append(1234);
System.out.println(sb1); //hello would1234
System.out.println(sb1==sb2); //true
System.out.println(sb2); //hello would1234
System.out.println(sb1.charAt(0)); //获取0位置上的字符 h
System.out.println(sb1.length()); //获取字符串的有效长度
System.out.println(sb1.capacity()); //获取底层数组的总大小
sb1.setCharAt(0,'H'); // 设置任意位置上的字符
sb1.insert(0,"hello would"); //设置任意位置的字符,可一改多
System.out.println(sb1);
sb1.append("hello would");
System.out.println(sb1.indexOf("hello")); //获取hello第一次出现的位置
System.out.println(sb1.lastIndexOf("hello"));// 获取hello最后出现的位置
System.out.println(sb1); //输出sb1 hello wouldHello would1234hello would
sb1.delete(0,5); //删除[0,5)的上的字符
String ret = sb1.substring(3,6); //截取[3.6)上的字符, 返回给ret
System.out.println(sb1); // wouldHello would1234hello would
sb1.reverse(); //字符串逆转
System.out.println(sb1); //dluow olleh4321dluow olleHdluow
sb1.toString(); //将StringBuffer以String的方式返回
System.out.println(sb1);
}
从上述例子可以看出:String和StringBuilder最大的区别在于String的内容无法修改,而StringBuilder的内容可
以修改。频繁修改字符串的情况考虑使用StringBuilder。
注意:String和StringBuilder类不能直接转换。如果要想互相转换,可以采用如下原则:
String变为StringBuilder: 利用StringBuilder的构造方法或append()方法
StringBuilder变为String: 调用toString()方法。
2.2 面试题
- String、StringBuffer、StringBuilder的区别
- String的内容不可修改,StringBuffer与StringBuilder的内容可以修改.
- StringBuffer与StringBuilder大部分功能是相似的
- StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操
作