文章目录
在C语言中已经涉及到字符串了,但是在C语言中要表示字符串只能使用字符数组或者字符指针,可以使用标准库提供的字符串系列函数完成大部分操作,但是这种将数据和操作数据方法分离开的方式不符合面向对象的思想,而字符串应用又非常广泛,因此Java语言专门提供了String类。
📕1. 常用方法
✏️1.1 字符串构造
String类提供的构造方式非常多,常用的就以下三种:
public static void main(String[] args) {
// 使用常量串构造
String s1 = "hello bit";
System.out.println(s1);
// 直接newString对象
String s2 = new String("hello bit");
System.out.println(s1);
// 使用字符数组进行构造
char[] array = {'h','e','l','l','o','b','i','t'};
String s3 = new String(array);
System.out.println(s1);
}
💡【注意】
- String是引用类型,内部并不存储字符串本身
在String类的实现源码中,String类实例变量如下:
public static void main(String[] args) {
// s1和s2引用的是不同对象 s1和s3引用的是同一对象
String s1 = new String("hello");
String s2 = new String("world");
String s3 = s1;
}
2. 在Java中“”引起来的也是String类型对象。
//打印hello字符串(String对象)的长度
System.out.println("hello".length());
✏️1.2 String对象的比较
- == 比较是否引用同一个对象
💡注意:对于内置类型 ,==比较的是变量中的值;对于引用类型 ==比较的是引用中的地址。
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
- boolean equals(Object anObject) 方法:按照字符大小的顺序进行比较
String类重写了父类Object中equals方法,Object中equals默认按照==比较,String重写equals方法后,按照
如下规则进行比较,比如: s1.equals(s2)
public static void main(String[] args) {
String s1 = new String("hello");
String s2 = new String("hello");
String s3 = new String("Hello");
// 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
}
- int compareTo(String s) 方法: 按照字符大小的顺序进行比较
与equals不同的是,equals返回的是boolean类型,而compareTo返回的是int类型。具体比较方式:
1): 先按照字典次序大小比较,如果出现不等的字符,直接返回这两个字符的大小差值
2):如果前k个字符相等(k为两个字符长度最小值),返回值两个字符串长度差值
public class Test {
public static void main(String[] args) {
String s4 = "abcd";
String s5 = "abcz";
String s6 = "abc";
String s7 = "accd";
String s8 = "abcd"
System.out.println(s4.compareTo(s5));// d和z不等,大小差值为-22
System.out.println(s4.compareTo(s6));//前k个字符完全相同,输出字符串长度差值1
System.out.println(s4.compareTo(s7));//不同输出d和c的差值-1
System.out.println(s4.compareTo(s8));//相同输出 0
}
}
- . int compareToIgnoreCase(String str) 方法:与compareTo方式相同,但是忽略大小写比较
public static void main(String[] args) {
String s1 = new String("abc");
String s2 = new String("ac");
String s3 = new String("ABc");
String s4 = new String("abcdef");
System.out.println(s1.compareToIgnoreCase(s2)); // 不同输出字符差值-1
System.out.println(s1.compareToIgnoreCase(s3)); // 相同输出 0
System.out.println(s1.compareToIgnoreCase(s4)); // 前k个字符完全相同,输出长度差值 -3
}
✏️1.3 字符串查找
字符串查找也是字符串中非常常见的操作,String类提供的常用查找的方法:
public static void main(String[] args) {
String s = "aaabbbcccaaabbbccc";
System.out.println(s.charAt(3)); // 'b'
System.out.println(s.indexOf('c')); // 6
System.out.println(s.indexOf('c', 10)); // 15
System.out.println(s.indexOf("bbb")); // 3
System.out.println(s.indexOf("bbb", 10)); // 12
System.out.println(s.lastIndexOf('c')); // 17
System.out.println(s.lastIndexOf('c', 10)); // 8
System.out.println(s.lastIndexOf("bbb")); // 12
System.out.println(s.lastIndexOf("bbb", 10)); // 3
}
💡注意:上述办法都是实例办法
✏️1.4 转化
- 数值和字符串转化
public class Test {
public static void main(String[] args) {
// 数字转字符串
int n = 12;
String s = String.valueOf(12);
System.out.println(s);//12,此时的12是字符串。
// 字符串转数字
// 注意:Integer、Double等是Java中的包装类型,
int data1 = Integer.parseInt("1234");
double data2 = Double.parseDouble("12.34");
System.out.println(data1);
System.out.println(data2);
}
}
💡注意:这里数字转化成字符串,是指类型转换,而不是将ASCLL码中12对应的字符赋值给s。
- 大小写转换
public class Test {
public static void main(String[] args) {
String s1 = "hello";
String s2 = "HELLO";
// 小写转大写
System.out.println(s1.toUpperCase());
// 大写转小写
System.out.println(s2.toLowerCase());
}
}
- 字符串转数组
public class Test {
public static void main(String[] args) {
String s = "hello";
// 字符串转数组
char[] ch = s.toCharArray();
for (int i = 0; i < ch.length; i++) {
System.out.print(ch[i]);
}
System.out.println();//生成换行符
// 数组转字符串
String s2 = new String(ch);
System.out.println(s2);
}
}
- 格式化
public class Test {
public static void main(String[] args) {
String s = String.format("%d-%d-%d", 2019, 9,14);
System.out.println(s);
}
}
✏️1.5 字符串替换
使用一个指定的新的字符串替换掉已有的字符串数据,可用的方法如下:
public class Test {
public static void main(String[] args) {
String str = "helloworld" ;
System.out.println(str.replaceAll("l", "_"));//he__owor_d
System.out.println(str.replaceFirst("l", "_"));//he_loworld
}
}
💡注意事项: 由于字符串是不可变对象, 替换不修改当前字符串, 而是产生一个新的字符串.
✏️1.6 字符串拆分
可以将一个完整的字符串按照指定的分隔符划分为若干个子字符串。
public class Test {
public static void main(String[] args) {
String str = "hello world hello xiaozhu" ;
String[] result = str.split(" ",2) ;//按照空格拆分成两组
for(String s: result) {
System.out.println(s);
}
}
}
拆分是特别常用的操作. 一定要重点掌握. 另外有些特殊字符作为分割符可能无法正确切分, 需要加上转义
代码示例: 拆分IP地址
public class Test {
public static void main(String[] args) {
String str = "192.168.1.1" ;
String[] result = str.split("\\.") ;
for(String s: result) {
System.out.println(s);
}
}
}
✏️1.7 字符串截取
从一个完整的字符串之中截取出部分内容。可用方法如下:
public class Test {
public static void main(String[] args) {
String str = "helloworld" ;
System.out.println(str.substring(5));//world
System.out.println(str.substring(0, 5));//hello
}
}
💡注意事项:
- 索引从0开始
- 注意前闭后开区间的写法, substring(0, 5) 表示包含 0 号下标的字符, 不包含 5 号下标
✏️1.8 intern方法
String.intern() 方法是 Java 中的一个方法,用于将字符串常量池中不存在的字符串加入到字符串常量池中。
String.intern() 主要用于优化字符串的内存使用,特别是在处理大量字符串时。通常,在使用大量字符串的情况下,使用intern()方法可以减少内存消耗,因为它可以确保相同内容的字符串只存储一次。
String s1 = new String("abc"); // 创建一个新的字符串对象
String s2 = "abc"; // 创建一个在常量池中的字符串对象
// 使用intern()方法将s1字符串对象的引用添加到常量池中
String s3 = s1.intern();
// 检查引用是否相等
System.out.println(s2 == s1); // false,不同的引用
System.out.println(s2 == s3); // true,引用相同
✏️1.9 其他操作方法
public class Test {
public static void main(String[] args) {
String str = " hello world " ;
System.out.println("["+str+"]");//[ hello world ]
System.out.println("["+str.trim()+"]");//[hello world]
}
}
trim 会去掉字符串开头和结尾的空白字符(空格, 换行, 制表符等).
📕2. 字符串的不可变性
- String类在设计时就是不可改变的,String类实现描述中已经说明了
String类中的字符实际保存在内部维护的value字符数组中,该图还可以看出:
- String类被final修饰,表明该类不能被继承
- . value被修饰被final修饰,表明value自身的值不能改变,即不能引用其它字符数组,但是其引用空间中的内容可以修改。
- 所有涉及到可能修改字符串内容的操作都是创建一个新对象,改变的是新对象
💡注意:网上有些人说:字符串不可变是因为其内部保存字符的数组被final修饰了,因此不能改变。
这种说法是错误的,不是因为String类自身,或者其内部value被final修饰而不能被修改。
final修饰类表明该类不想被继承,final修饰引用类型表明该引用变量不能引用其他对象,但是其引用对象中的内容是可以修改的。
public static void main(String[] args) {
final int array[] = {1,2,3,4,5};
array[0] = 100;
System.out.println(Arrays.toString(array));
// array = new int[]{4,5,6}; // 编译报错:Error:(19, 9) java: 无法为最终变量array分配值
}
综上所述:为什么String是不可变的?
答:String内容的核心,是由一个char类型数组来保存的,而且
1):数字被private修饰,外界不能直接访问到数组
2):针对这个数组,String没有提供任何可以修改的办法
❓提问:为什么 String 要设计成不可变的?(不可变对象的好处是什么?)
- 方便实现字符串对象池. 如果 String 可变, 那么对象池就需要考虑写时拷贝的问题了.
- 不可变对象是线程安全的.
- 不可变对象更方便缓存 hash code, 作为 key 时可以更高效的保存到 HashMap 中.
❓提问:那如果想要修改字符串中内容,该如何操作呢?
📕3. StringBuilder和StringBuffer
✏️3.1 StringBuilder的介绍
由于String的不可更改特性,为了方便字符串的修改,Java中又提供StringBuilder和StringBuffer类。这两个类大部分功能是相同的,这里介绍 StringBuilder常用的一些方法
String和StringBuilder最大的区别在于String的内容无法修改,而StringBuilder的内容可以修改。频繁修改字符串的情况考虑使用StringBuilder。
💡注意:String和StringBuilder类不能直接转换。
如果要想互相转换,可以采用如下原则:
- String变为StringBuilder: 利用StringBuilder的构造方法或append()方法
- StringBuilder变为String: 调用toString()方法。
✏️3.2 面试题
.String、StringBuffer、StringBuilder的区别:
- String的内容不可修改,StringBuffer与StringBuilder的内容可以修改.
- StringBuffer与StringBuilder大部分功能是相似的
- StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作