1. String类的介绍
String的应用场景非常多,也很实用,String给我们提供的功能也非常的丰富。那么String是什么呢?把一组字符(char)按照顺序串到一起就是字符串,而Java常常用""表示一个字符串,"hello","你好",Java的" "可以包含任意个字符。
相比之下,C中没有一个专门的“字符串”概念的,C的做法是使用char[]表示一个字符串。(最后一个字符,必须使用\0结尾)。
2. String的使用
2.1 构造String
构造一个String(创建一个String对象),有三种方法
public class Test1 {
public static void main(String[] args) {
//构造String对象
//1. 直接通过字面值常量来构造
String s1 = "hello";
//2. 通过new的方式来构造,String也是类
String s2 = new String("hello");
//3. 通过字符数组来构造String
char[] chars = {'h','e','l','l','o'};
String s3 = new String(chars);
}
}
Java中包名都是小写字母。字面值常量就是一见到字面就注定是常量的值,比如1,1.0,"hello"这种,String是标准库内置的类,使用这个类,不需要import,String类处于标准库中的java.lang这个包中。在Java中,更推荐使用第一种方法创建String,而不是new的方式。
第二种new String这样的做法,就需要从操作系统这里申请内存,并且创建字符串对象。而第一种方法,字符串常量池,直接String s1 = "hello",这样的时间开销会比上面的new的方法小很多。字符串常量池,是Java内置的机制,JVM运行时就会自动进行构造,不需要程序员来关注。常量池也不仅仅是只有"字符串常量”,还有一些其他的常量池。
2.2 String的内部实现
public class Test2 {
public static void main(String[] args) {
// String s1 = "abc";
// String s2 = "abc";
// // String 是引用类型, 使用 == 比较, 是判定两个引用保存的地址是否相同 (判定两个引用是否指向同一个对象)
// System.out.println(s1 == s2); // true
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1 == s2); // false
}
}
字符串常量值实际是StringTable类,实际是一个固定大小的HashTable
情况一:

"abc”是字面值常量就会被放到JVM的字符串常量池中

情况二:

把"abc"这个常量的每个字符,分别拷贝到两个String对象中。

3. String类的核心方法
这里介绍一些常用的,核心的方法,更多方法可以参考Java的官方文档(也可以直接看原码中的注释):Overview (Java SE 17 & JDK 17)
3.1 String对象的比较
①比较相等
1)两个String 引用,指向的对象是否是同一个==(学习语法印证下常量池的设定,实际开发中很少用)
2)两个String引用对应的对象,包含的字符内容是否是相同(equals),每个字符是否对应一致的
public class Test3 {
public static void main(String[] args) {
// 针对两个 String 引用来进行 equals
// String s1 = "hello";
// String s2 = new String("hello");
// System.out.println(s1.equals(s2)); //true
// // 也可以针对 String 字面值常量来 equals
// String s = null;
// // 这两个写法, 更推荐写成第二种写法;
// System.out.println(s.equals("hello"));
// System.out.println("hello".equals(s));
String s1 = "hello";
String s2 = "hello";
System.out.println(s1.equals(s2));
}
}
"hello"这个常量也是String类型,也可以使用访问String的各种方法
![]()
推荐使用上面的这种写法。类似C语言的一个设定,if(10 == n) 以防写成if(n=10)
②比较大小关系
数字比较大小,很明确的规则,而字符串比较大小,则使用字典序(按照字母顺序排序)。

public class Test4 {
public static void main(String[] args) {
String s1 = "AAA";
String s2 = "aaa";
System.out.println(s1.compareTo(s2)); //-32
System.out.println(s1.compareToIgnoreCase(s2)); //0
}
}
abcd这些字符都是有对应的 ascii码值的,此处看到的-32,就是前一个字符串的第一个字符,减去后一个字符串的第一个字符的ascii,compareToIgnoreCase方法是忽略了大小写。
3.2 字符串查找
①charAt给定下标,获取到对应的字符.
②indexOf查找该字符串包含的某个子字符串,从左往右找
③lastlndexOf,也是查找子字符串,是从右往左找
①charAt
public class Test5 {
public static void main(String[] args) {
String s1 = "hello";
System.out.println(s1.charAt(0));
System.out.println(s1.charAt(1));
System.out.println(s1.charAt(2));
System.out.println(s1.charAt(3));
System.out.println(s1.charAt(4));
// System.out.println(s1.charAt(5)); //报错
String s = "你好";
System.out.println(s.charAt(0));
System.out.println(s.charAt(1));
}
}

注意:比较两个带有汉字的字符串的大小关系,如果只是比较相等/不等,当然ok,规则也一致~~,如果是比较"大于小于”,使用compareTo比较结果是无意义的。
②indexOf
把一个字符串其中的一部分拿出来,就可以称为"子串"。indexOf 就是在查找字符串中包含的子串(连续),查找指定的参数是否是该字符串的子串,如果是,是处于哪个位置。

public class Test6 {
public static void main(String[] args) {
String s1 = "abcdef";
System.out.println(s1.indexOf("cdf")); // -1
String s = "abcabcabc";
System.out.println(s.indexOf("abc")); // 0
System.out.println(s.lastIndexOf("abc")); // 6
}
}
3.3 字符串的转换
①数字转换为字符串
![]()
Java中String的拼接,+可以把其他类型,自动转换为String类型
![]()
上面也是数字转换为字符串的方法
public class Test7 {
public static void main(String[] args) {
// int n = 10;
double n = 10.5;
// Boolean n = false;
String s1 = "" + n;
System.out.println(s1);
String s2 = String.valueOf(n); // 数字转为字符串
System.out.println(s2);
}
}
②字符串转换为数字
String s = "10";
int n = Integer.parseInt(s);
System.out.println(s + 10); //1010,而不是20
System.out.println(n + 10); // 20
3.4 字符串转大小写
toUpperCase()转大写
toLowerCase()转小写
String s = "Hello";
String s1 = s.toUpperCase();
System.out.println(s1);
String s2 = s.toLowerCase();
System.out.println(s2);
System.out.println(s);

【注意】此处的 toUpperCase 和toLowerCase都是只生成新的String,不修改本来的String。实际上,String 中的所有方法,都不会修改String本身,String这个对象,其实是一个“不可变对象",相当于"常量"。
3.5 字符串转数组
利用toCharArray()方法转为字符数组,或者利用getBytes()方法转为字节数组
String s = "你好";
//通过toCharArray转为字符数组
char[] chars = s.toCharArray();
for(char c : chars){
System.out.println(c);
}
byte[] bytes = s.getBytes();
for(byte b : bytes){
System.out.printf("%x\n", b);
}

String本身,就是按照byte[]的方式来存储的,字符,也是通过"字节"构成的。一个字节是8个二进制位(bit位),一个十六进制数字,正好就是4个bit位,一个字节,刚好是2个十六进制数字,如果按照二进制来打印,打出来的0101太多了,不方便看。按照十六进制打印,长度大幅度缩小,并且也很方便肉眼去换算到二进制。
汉字的编码方式有utf-8(一个汉字占3个字节)和utf-16(一个汉字占2个字节),而世界上最通用的编码方式是utf-8。
3.5 字符串格式化
int x = 10;
System.out.printf("x = %d\n", x);
String s = String.format("x = %d\n", x);
System.out.println(s);
构造格式化字符串,类似printf,format方法,就可以完成格式化创建字符串的操作。printf 只能打印,而String.format构造出来的字符串,不仅仅可以用来打印。
3.6 字符串替换
把字符串的一部分改成别的东西
String s = "大家今天过的很开心";
// replace 是替换所有的字符. 替换的结果是新的字符串.
String s2 = s.replace("大家","小红");
System.out.println(s);
System.out.println(s2);

3.7 字符串拆分
指定分隔符,把一个字符串,拆成多个部分,得到一个字符串数组~字符串拆分,是一个非常常见的场景,开发的时候,网络通信程序,网络上传输的都是"字符串"(二进制的串),但是通常发一次数据,就要携带多个信息。
String s = "C++,Java,Python,C#,Javascript";
String[] languages = s.split(",");
for( String language : languages){
System.out.println(language);
}
String s = "C++.Java.Python.C#.Javascript";
String[] languages = s.split("\\.");
for( String language : languages){
System.out.println(language);
}
上面的‘.’是个正则表达式,针对.进行转义,可以通过\.表达原始的.。由于在Java的字符串常量中,要求\还需要被转义一下,所以需要\\。

3.8 字符串截取
字符串取子串,substring

从指定的位置截取到末尾或者是截取指定区间:[beg,end),前闭后开区间
String s = "abcdefg";
System.out.println(s.substring(2)); //cdefg
System.out.println(s.substring(2, 4)); //cd
// System.out.println(s.substring(2,8)); //越界报错
// 虽然要求下标得是在合理范围, 但是 7 特殊. 取子串并不或访问 7 这个下标的元素.
System.out.println(s.substring(2,7));

3.9 去除两边的空格

String s = " \f Hello World! \n\t ";
String s2 = s.trim();
System.out.println("[" + s + "]");
System.out.println("[" + s2 + "]");

3.10 intern方法
intern()方法的作用就是手动把字符串添加到常量池中。而代码中写死的"hello"这种字符串常量,编译器编译期间就能都提出来,运行的时候直接把这些内容加到池子里,不需要程序员干预。

String类并没有提供任何一个public的,用来修改value 数组内容的方法。如果修改一个String,通常的做法就是创建一个新的String。String类不能被继承。
![]()
String s = new String("Hello");
// 此时 s2 的内容也是 "hello", 但是是指向常量池中的 "hello"
String s2 = s.intern();
System.out.println(s == "Hello"); //false
System.out.println(s2 == "Hello"); //true
4. StringBuilder和StringBuffer
String 不可变,StringBuilder 和StringBuffer可变。(优先使用StringBuilder),StringBuilder是新版本,StringBuffer是旧版本。
为啥要搞一个不可变的字符串,优先考虑使用不可变的字符串呢??计算机,更喜欢“不可变”的东西的,针对“不可变”可以做出一些优化手段,提高效率的。
String不可变优点
1.更方便的存储到"常量池"中了.
2.针对不可变的String,可以更好的计算 hash值(数据结构,哈希表的时候,详细展开)
3.天然线程安全(JavaEE初阶详细解释)
// Java 中提供的 "不可变的字符串"
long beg = System.currentTimeMillis();
String s = "hello";
for(int i = 0; i < 10_0000; i++){
s += i;
}
long end = System.currentTimeMillis();
System.out.println(end - beg); //2794
System.out.println(s);
// Java 还提供了可变的字符串 StringBuilder. 用法和 String 非常相似. 可以方便的和 String 相互转换.
// 但是这个东西可以修改.
// 记录程序开始的时间戳, 单位是毫秒.
StringBuilder stringBuilder = new StringBuilder("Hello");
long beg = System.currentTimeMillis();
for(int i = 0; i < 100_0000; i++){
// 类似于 String 的 += 这个过程不会创建新的 StringBuilder 对象, 都是针对同一个对象操作的.
stringBuilder.append(i);
}
long end = System.currentTimeMillis();
System.out.println(end - beg); //20
// System.out.println(stringBuilder);
Java SE中String类的介绍与使用
2240

被折叠的 条评论
为什么被折叠?



