学习笔记
- 认识String类。
- 熟悉Java中String类的常用方法。
- 认识 StringBuffer 和 StringBuilder
- StringAPI来辅助学习熟练操作。
- C语言字符串与Java字符串的区别.
认识String类
C语言中的字符串用字符指针,字符数组和特殊结束字符’\0’表示。
Java没有内置一个字符串类型,而是封装成了一个类。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc
String是公共类.
它不能派生子类(被final修饰)
默认继承Object类.
它还实现了我们比较熟悉的Comparable接口。
String内部封装了一个字符数组,char[] value。但其被final修饰,因此String类是不可变的。
String类很重要,面试要考!
字符串使用操作
字符串构造
- 和C语言一样,用双引号引起的字符序列就是一个字符串,如"hello"。
String s="hello";
右边的"hello"是常量字符串。用String类的引用变量接收。 - String类,类实例化对象用new关键字和构造器。
String s = new String("hello")
。
String类构造器有多种重载形式,比如String s = new String()
,默认构造一个空字符串。 - 通过字符数组构造
String类有一种构造器,通过字符串数组作为构造器参数来实例化对象。
public static void main(String[] args) {
char[] arr={'h','e','l','l','o'};
String s=new String(arr);
System.out.println(s);
}
String是一个类,引用类型变量存储的是地址,是对象在堆上内存的地址。堆上放的是对象具体的内容。
"hello"同样看作String类的对象。它可以调用String类的方法。System.out.println("hello".length());
调用length方法计算字符串长度。
int length():返回字符串长度。
String类的比较
讨论==,equals方法,compareTo方法的区别。
"=="比较
String s1 = new String("hello");
String s2 = new String("hello");
String s3 = new String("world");
String s4 = s1;
System.out.println(s1 == s2); // false
System.out.println(s2 == s3); // false
System.out.println(s1 == s4); // true
Java在等号比较上更像C
如果你熟悉C语言,可以明白上面的s1与s2,s3,s4比较。它们实际上比较引用对象所在地址。
实际上Java仍然脱离这一套,Java中的引用变量是地址,s1,s4引用同一个对象,s1和s4的地址相同,因此s1==s4结果为true.s2实际上new一个对象,只不过内容与s1一样,它们在JVM上的内存地址不同,所以不相等。s3更不言说了。
- 总结:==只能判断两者是不是虚拟内存的同一位置。
equals方法
String类没显式继承任何一个类,则其默认继承Object类,equals方法是其父类Object类中的。如果你熟悉父类的equals方法,它与"=="功能相同,我们通常认为里面的字段相同就认为它们是同一事物,所以String类重写了equals方法。
public boolean equals(Object anObject) {
// 先检测this 和 anObject 是否为同一个对象比较,如果是返回true
if (this == anObject) {
return true;
}
//判断传入参数是不是String类,再进行内容比较。
return (anObject instanceof String aString)
&& (!COMPACT_STRINGS || this.coder == aString.coder)
&& StringLatin1.equals(value, aString.value);
}
扩展了equals方法,现在字符串内容相同也视为相等。
public static void main(String[] args) {
String s1 = new String("hello");
String s2 = new String("hello");
String s3 = new String("world");
// 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
补充equalsIgnoreCase方法,功能与equal方法一致,但是忽略大小写!!!
compareTo方法
String类实现了comparable泛型接口,该接口有且只有一种抽象方法compareTo方法。
String类不是抽象类,它必然实现了这个方法。public int compareTo(String anotherString)
- 它的比较逻辑和C中的strcmp函数一样,它是字符串比较大小函数。
它会将两个字符串按顺序逐字符比较大小,知道决出胜负。
当且仅当两个字符串长度相等且每个对应位置字符相同时,返回0. - 与equals(String类中重写的)方法区别:equals方法判断字符串是不是相等,返回值是布尔类型(true或者false),compareTo方法比较两个字符串的“大小”返回类型是int。
- 与String类的equals方法一样,针对compareTo方法也提供了忽略大小写的方法。
int compareToIgnoreCase(String str) 方法:与compareTo方式相同,但是忽略大小写比较
字符串查找
建议去隔壁力扣
坐牢刷题,不用死记。
- 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
总而言之,三个函数charAt函数,indexof函数,lastIndexOf函数。
根据下标找字符,根据字符找下标。
字符串转化
非字符串转字符串
- 记住一个方法valueOf()。
- 整型,浮点数,布尔类型转字符串。
String s1 = String.valueOf(1234);
String s2 = String.valueOf(12.34);
String s3 = String.valueOf(true);
valueOf()
方法重载,可以接受各种基本数据类型,以字符串形式返回。
为什么可以这样,你需要了解包装类的知识,请自行搜索学习。
同样字符串转基本数据类型也涉及包装类的知识。(相关方法)
- 对象转字符串
如果valueOf方法接收的是对象,那么会调用对象的toString方法。
String s4 = String.valueOf(new Student("Hanmeimei", 18));
//Student类
public class Student implements Comparable<Student>{
private String name = null;
private int score = 0;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
@Override
public String toString() {
return "[" + name + ":" + score + "]";
}
public static void main(String[] args) {
Student[] students = new Student[]{
new Student("张三", 95),
new Student("李四", 96),
new Student("王五", 97),
new Student("赵六", 92),
};
Arrays.sort(students);
System.out.println(Arrays.toString(students));
}
@Override
public int compareTo(Student o) {
//这里根据分数比较。
return this.score-o.score;
}
}
这里来解释这个valueOf干了什么?
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
valueOf重载,支持接受一个对象为参数。接受参数若空引用则把字符串初始化"null",否则返回执行toString方法的结果。
这里由于Student类重写了toString方法 ,返回结果如下:
@Override
public String toString() {
return "[" + name + ":" + score + "]";
}
字符串大小写转换
小写转大写 toUpperCase();
大写转小写 toLowerCase();
public static void main(String[] args) {
String s1 = "hello";
String s2 = "HELLO";
// 小写转大写
System.out.println(s1.toUpperCase());
// 大写转小写
System.out.println(s2.toLowerCase());
}
字符串转数组
前面在字符串构造时说明了字符数组转字符串的情况。
构造器传入字符数组名,就转化成字符串。
字符串转字符数组
char[] to
字符串拼接
Java语言中允许+来连接两个字符串。
字符串优先级高,当字符串与非字符串相加时,非字符串会转换成字符串。
+为什么可以实现字符串拼接?实际上它实现了和C++相同的一种运算符重载。Java不允许我们程序员重载运算符,但是它提供了+运算符的重载。
//请自行举例
子串(字符串截取)
String类提供了一个substring方法,可以从大的字符串提取一个子串。
它有两种形式。
public String substring(int beginIndex)
只输入起始下标,开始从下标处截取直到原字符串末尾。public String substring(int beginIndex, int endIndex)
第一个是截取的起始位置,第二个参数是不想截取的第一个位置。从数学角度它是截取下标为[beginIndex,endIndex)的下标。
public static void main(String[] args) {
System.out.println("I love JavaSE".substring(2,6));
}
输出love。
字符串的不可变性
前面的例子似乎在告诉我们字符串可以改变呢,但这是错误的理解,对字符串的修改实际上是新建了字符串对象。
下面我们来说明两点:
- 为什么字符串不可变,从String类的哪里可以得出结论。
- 如何达到一个字符串被改变的效果。
为什么字符串不可变?
可能会被下面的final引起误解
public final class String implements java.io.Serializable, Comparable<String>, CharSequence,Constable, ConstantDesc
这里的final说明String不可被继承!@Stable private final byte[] value;
这里的final意味着引用的数组不可被修改;本质上对里面的整型还是可以修改。(字符串内容可变)
真正原因是private修饰,而String类又不提供任何修改值的set方法。
获取字符
不能通过下标获取字符,因为String类对象不可变。
char ch = str.charAt(0); // 'H'
字符串修改
对于改变原对象的内容,要用到反射相关的知识,但我们可以通过新建对象,截取子串,字符串拼接来实现“修改”的目的。
既然String类对象不可变,那我们修改String的引用变量,让它引向一个新的对象。
public static void main(String[] args) {
String s="hello";
System.out.println(s);
s=s.substring(0,3)+"p";
System.out.println(s);//输出help
}
Java中一切String类方法对字符串的“修改”都是返回了一个新的对象
StringBuilder和StringBuffer介绍
由于String不可修改的特性,Java提供了StringBuilder类和StringBuffer类。它们可以直接修改对象的内容,而不会返回新的对象,提升了效率。
StringBuilder在线文档
C系字符串与Java字符串
以下摘自chatgpt
- 字符串的定义与存储:
Java:在Java中,字符串是作为String类的对象存在的。String是一个标准的类,提供了丰富的字符串操作方法。字符串在Java中是不可变的,一旦创建,其值就不能再改变。
C语言:C语言中没有专门的字符串类型,通常使用字符数组(char类型数组)来表示字符串。字符串以null字符(\0)结尾,用于标识字符串的结束。 - 字符集支持:
Java:Java基于Unicode字符集,支持所有主要语言和书写系统中的字符,包括非拉丁语系的字符,如汉字、希腊字母等。Unicode字符集使用16位编码。
C语言:C语言传统上使用ASCII字符集,它仅支持基本的拉丁字符集,每个字符使用8位表示。虽然C语言也可以通过其他方式支持Unicode,但这需要额外的编程工作。 - 字符串操作:
Java:String类提供了大量的方法来操作字符串,如连接(concat)、获取长度(length)、提取子字符串(substring)等。这些操作都是基于新的String对象的创建,因为Java中的字符串是不可变的。
C语言:在C语言中,对字符串的操作通常依赖于标准库函数,如strlen用于计算字符串长度,strcpy和strcat用于字符串的复制和连接,以及strcmp用于比较字符串等。这些操作直接修改原始的字符数组。 - 字符串比较:
Java:在Java中,==操作符用于比较两个对象引用是否相同,而不是比较字符串的内容。要比较字符串的内容是否相等,应使用equals方法。
C语言:在C语言中,通常使用strcmp函数来比较两个字符串的内容是否相等。如果两个字符串相同,strcmp会返回0。 - 内存管理:
Java:Java有自动的垃圾回收机制来管理内存,因此开发者通常不需要手动管理字符串对象的内存。
C语言:在C语言中,开发者需要手动管理字符数组的内存分配和释放,这包括为字符数组分配足够的空间以及在使用完毕后释放这些空间,以防止内存泄漏。
另外一点,C语言字符串可以用字符指针加[]访问字符串内的字符,无论这个字符指针指向是在栈上,还是堆上,或者常量区。
Java不允许通过[]访问来操作字符串,这正也与我们前面所说的字符串不可变性不谋而合,charAt()方法可以通过下标返回字符.