JavaSE - 字符串

JavaSE - 字符串

本节学习目标:

  • 了解并掌握字符串对象的声明与初始化;
  • 了解并掌握字符串使用运算符操作;
  • 了解并掌握如何获取字符串信息;
  • 了解并掌握字符串操作的有关方法;
  • 了解并掌握字符串的格式化方法;
  • 了解并掌握两种字符串生成器。

1. String 类

字符串(String)是Java语言中的一个引用数据类型String类位于java.lang包下。在Java中所有字符串字面量,如"ABCDEFG""1234567"等,
用两个双引号包括起来的一串字符,都是实现了String类的一个实例,所以在Java语言中,字符串被看做是一个String类的对象

1.1 字符串的声明

字符串变量的声明方式:

String 标识符;

字符串字面量常量)的声明方式:使用两个双引号包括:

"Hello World!""Java Programming Language"

1.2 字符串变量的初始化

String类提供了很多构造方法,常用的构造方法如下:

  • String(String original):引用字符串字面量初始化一个字符串变量:
String str = new String("Hello");

// 上面的等价于
String str = "Hello";
  • String(char[] value):使用char类型的数组初始化一个字符串变量:
char[] charArr = new char[] {'H', 'e', 'l', 'l', 'o'};
String str = new String(charArr);

// 上面的等价于
String str = "Hello";
  • String(char[] value, int offset, int count):使用char类型的数组中,将从索引为offset开始的count个元素拼接作为字符串:
char[] charArr = new char[] {'H', 'e', 'l', 'l', 'o'};
String str = new String(charArr, 1, 4);

// 上面的等价于
String str = "ello";

1.3 String 类的细节

  • String类本身被final关键字修饰,也就是说String类本身不可被继承
  • 查看源码我们会发现,字符串底层是用的字符型数组char[])实现的,这个字符型数组成员变量也被final关键字修饰,所以说字符串一旦确定就不能更改
  • 直接使用相同的字符串字面量初始化的两个字符串引用,它们引用的是同一个字符串对象。使用new关键字通过构造方法的字符串引用,即使内容相同,引用的是不同的对象。
String str1 = "HelloWorld";
String str2 = "HelloWorld";
String str3 = new String("HelloWorld");
System.out.println(str1 == str2);
System.out.println(str1 == str3);

// 运行结果
true
false

2. 连接字符串

字符串与字符串之间可以使用+运算符进行拼接操作,将两个字符串首尾相连拼接在一起成为一个新的字符串:

String str1 = "Hello";
String str2 = "World!";
String str3 = str1 + " " + str2;

// 上面的等价于
String str3 = "Hello World!";

如果字符串字面量太长需要换行,在Java语言中不能直接换行,需要使用+运算符进行拼接:

// 错误的换行
String str = "Hello
         World!";

// 正确的换行
String str = "Hello" +
        " World!";

字符串也可以和其他数据类型之间使用+运算符进行运算,运算结果为字符串类型,实质是将其他数据类型转换字符串类型进行拼接:

int i = 59;
String str = "Hello" + i;

// 上面的等价于
String str = "Hello59";

只要+运算符两侧有一个为字符串类型,则运算结果就为字符串类型,所以需要多个操作数进行运算时要注意先后顺序,以免出现错误的结果。

3. 获取字符串信息

String类提供了很多方法来获取字符串的信息

3.1 获取字符串长度

int length()方法可以获取字符串对象的长度。

String str = "Hello World!";
System.out.println(str.length());

// 运行结果
12

3.2 查找子字符串

  • int indexOf(String str)方法与int indexOf(String str, int fromIndex)方法:

第一个方法用于返回字符串str在指定字符串中首次出现的索引位置,会从指定字符串的开始位置开始从前往后查找。如果没有查找到,返回-1
第二个方法可以指定查找的开始位置fromIndex

String str = "AAA123AAA123AAA";
System.out.println(str.indexOf("123"));
System.out.println(str.indexOf("123", 4));

// 运行结果
3
9
  • int lastIndexOf(String str)方法与int lastIndexOf(String str, int fromIndex)方法:

第一个方法用于返回字符串str在指定字符串中最后一次出现的索引位置,会从指定字符串的末尾位置开始从后往前查找,如果没有查找到,返回-1
第二个方法可以指定查找的开始位置fromIndex

String str = "AAA123AAA123AAA";
System.out.println(str.lastIndexOf("123"));
System.out.println(str.lastIndexOf("123", 8));

// 运行结果
9
3

3.3 返回指定索引处的字符

char charAt(int index)方法返回指定索引处的字符。

String str = "ABCDEFG";
System.out.println(str.charAt(4));

// 运行结果
E

4. 字符串的操作方法

String类提供了很多操作字符串的方法。

4.1 获取子字符串

String substring(int beginIndex)方法与String substring(int beginIndex, int endIndex)方法:

第一个方法返回从指定的索引位置beginIndex开始截取直到该字符串结尾子字符串
第二个方法返回从指定的索引位置beginIndex开始截取直到索引位置endIndex结尾子字符串

String str = "HelloWorld!";
System.out.println(str.substring(3));
System.out.println(str.substring(4, 8));

// 运行结果
loWorld!
oWor

4.2 去除空格

String trim()方法返回字符串的副本,忽略字符串开头与结尾的空格字符与字符中间的空格不会被忽略。

String str = "   Hello World!   ";
System.out.println("原来的字符串:" + str + ",它的长度" + str.length())
System.out.println("trim后字符串:" + str.trim() + ",它的长度" + str.trim().length());

// 运行结果
原来的字符串:   Hello World!   ,它的长度18
trim后字符串:Hello World!,它的长度12

4.3 字符串替换

String replace(char oldChar, char newChar)方法与String replace(CharSequence target, CharSequence replacement)方法。

第一个方法返回将原字符串内的指定字符oldChar替换为新字符newChar之后的字符串;
第二个方法返回将原字符串内的指定字符序列target替换为新字符序列replacement之后的字符串(字符序列可以看做字符串)。
如果一个字符串中出现多次oldChartarget,则两者会全部进行替换。

String str = "Hello World!";
System.out.println(str.replace('l', 'A'));
System.out.println(str.replace("o", "OOO"));

// 运行结果
HeAAo WorAd!
HellOOO WOOOrld!

4.4 判断字符串的开头与结尾

boolean startsWith(String prefix)方法与boolean endsWith(String suffix)方法。

startsWith()方法判断字符串是否以指定的内容开始endsWith()方法判断字符串是否以指定的内容结束

String str = "Hello World";
System.out.println("字符串str是否以“He”开头?" + str.startsWith("He"));
System.out.println("字符串str是否以“d!”结尾?" + str.endsWith("d!"));

// 运行结果
字符串str是否以“He”开头?true
字符串str是否以“d!”结尾?false

4.5 判断字符串是否相等

由于字符串为引用数据类型,所以不能直接使用==运算符判断。

String类提供了两种方法比较字符串是否相等

  • 重写Object类提供的boolean equals(Object obj)方法:

String类的equals()方法判断步骤:

①直接使用==运算符判断对象obj与本字符串对象的地址是否相同,如果则返回true

②使用instanceof关键字判断对象obj是否是String类的一个实例,如果不是则返回false

③将对象obj转换为字符串,比较obj的字符串形式的长度与本字符串长度是否相同,如果不相同则返回false

④将对象obj的字符串形式的每个字符和本字符串的每个字符一一比对,如果全都相同则返回true,否则返回false。

  • equalsIgnoreCase(String anotherString)方法:与equals()方法判断步骤一样,但是忽略了字母的大小写
String str1 = "HelloWorld";
Object str2 = new String("helloworld");
System.out.println("equals()方法判断:" + str1.equals(str2));
System.out.println("equalsIgnoreCase()方法判断:" + str1.equalsIgnoreCase(str2.toString()));

// 运行结果
equals()方法判断:false
equalsIgnoreCase()方法判断:true

4.6 按字典顺序比较两个字符串

int compareTo(String anotherString)方法按字典顺序比较两个字符串,该比较基于字符串中每个字符的Unicode值,
按字典顺序将本字符串与字符串anotherString进行比较,如果按字典顺序本字符串在anotherString之前,则返回一个负整数
之后则返回一个正整数,如果本字符串使用equals()方法与anotherString字符串比较为true时,compareTo()方法返回0

String str1 = "a";
String str2 = "b";
String str3 = "b";
String str4 = "c";
System.out.println("b与a比较" + str2.compareTo(str1));
System.out.println("b与b比较" + str2.compareTo(str3));
System.out.println("b与c比较" + str2.compareTo(str4));

// 运行结果
b与a比较1
b与b比较0
b与c比较-1

4.7 字母大小写转换

String toLowerCase()方法将本字符串中的大写字母转换为小写字母后返回该字符串;
String toUpperCase()方法将本字符串中的小写字母转换为大写字母后返回该字符串。

String str = "Hello World!";
System.out.println(str.toLowerCase());
System.out.println(str.toUpperCase());

// 运行结果
hello world!
HELLO WORLD!

4.8 字符串分割

String[] split(String regex)方法和String[] split(String regex, int limit)方法。

第一个方法可以使字符串按指定的字符字符串正则表达式分割成子字符串数组。第二个方法可以指定分割后的子字符串数组元素个数

String str = "HelloWorld!";
System.out.println(Arrays.toString(str.split("o")));
System.out.println(Arrays.toString(str.split("o", 2)));

// 运行结果
[Hell, W, rld!]
[Hell, World!]

5. 字符串的格式化

String类提供了静态方法format()用于格式化字符串,format()方法有两种重载形式:

  • String format(String format, Object... args)

    • format:格式化的字符串,使用格式化转换符
    • args:格式化转换符引用的参数,参数的个数是可变的,如果参数个数多于格式化转换符个数,则剩下的参数个数将会被忽略参数个数可以为0
  • String format(Locale l, String format, Object... args)

    • l:格式化时使用的语言环境,指定为null时不进行本地化;
    • format:格式化的字符串,使用格式化转换符
    • args:格式化转换符引用的参数,参数的个数是可变的,如果参数个数多于格式化转换符个数,则剩下的参数个数将会被忽略参数个数可以为0

5.1 日期字符串格式化

常用的日期格式化转换符:

日期格式化转换符说明示例
%te一个月中的某一天2
%tb指定语言环境的月份简称Feb、二月
%tB指定语言环境的月份全称February、二月
%tA指定语言环境的星期全称Monday、星期一
%ta指定语言环境的星期简称Mon、星期一
%tc全部日期和时间信息星期二 三月 25 13:37:22 CST 2017
%tY4位年份2017
%tj一年中的第几天076
%tm一年中的第几月07
%td一个月中的第一天15
%ty2位年份17

编写代码进行测试:

import java.util.Date;
import java.util.Locale;
public class TestDateFormat {
    public static void main(String[] args) {
        Locale.setDefault(Locale.ENGLISH); // 设置当前语言环境为英文环境
        Date date = new Date();            // 获取当前日期的对象
        System.out.println(String.format("%te", date));
        System.out.println(String.format("%tb", date));
        System.out.println(String.format("%tB", date));
        System.out.println(String.format("%tA", date));
        System.out.println(String.format("%ta", date));
        System.out.println(String.format("%tc", date));
        System.out.println(String.format("%tY", date));
        System.out.println(String.format("%tj", date));
        System.out.println(String.format("%tm", date));
        System.out.println(String.format("%td", date));
        System.out.println(String.format("%ty", date));
    }
}

运行结果:

2
Nov
November
Tuesday
Tue
Tue Nov 02 19:36:57 CST 2021
2021
306
11
02
21

5.2 时间字符串格式化

常用的时间格式化转换符:

时间格式化转换符说明示例
%tH24小时制的2位小时数(有0占位,00~23)14
%tI12小时制的2位小时数(有0占位,01~12)06
%tk24小时制的2位小时数(无0占位,0~23)9
%tl12小时制的2位小时数(无0占位,1~12)7
%tM2位分钟数(00~59)10
%tS2位秒钟数(00~60)09
%tL3位毫秒数(000~999)457
%tN9位微秒数(000000000~999999999)062000000
%tp指定语言环境的上下午标记pm、下午
%tzGMT时区偏移量+0900
%tZ时区缩写形式的字符串CST
%tsUNIX时间戳秒钟数(从1970-01-01 00:00:00至今经过的秒钟数)1635853859
%tQUNIX时间戳毫秒数(从1970-01-01 00:00:00至今经过的毫秒数)1635853859077

编写代码进行测试:

import java.util.Date;
import java.util.Locale;
public class TestTimeFormat {
    public static void main(String[] args) {
        Locale.setDefault(Locale.SIMPLIFIED_CHINESE); // 指定当前语言环境为简体中文环境
        Date date = new Date();                       // 获取当前日期的对象
        System.out.println(String.format("%tH", date));
        System.out.println(String.format("%tI", date));
        System.out.println(String.format("%tk", date));
        System.out.println(String.format("%tl", date));
        System.out.println(String.format("%tM", date));
        System.out.println(String.format("%tS", date));
        System.out.println(String.format("%tL", date));
        System.out.println(String.format("%tN", date));
        System.out.println(String.format("%tp", date));
        System.out.println(String.format("%tz", date));
        System.out.println(String.format("%tZ", date));
        System.out.println(String.format("%ts", date));
        System.out.println(String.format("%tQ", date));
    }
}

运行结果:

19
07
19
7
50
59
077
077000000
下午
+0800
CST
1635853859
1635853859077

5.3 日期时间组合的格式化

常用的日期和时间组合的格式化转换符:

日期时间组合格式化转换符说明示例
%tF“年-月-日”格式(4位年份)2021-11-02
%tD“月/日/年”格式(2位年份)11/02/21
%tc全部日期和时间信息星期二 十一月 02 20:02:20 CST 2021
%tr“时:分:秒 上下午”格式(12小时制)08:02:20 下午
%tT“时:分:秒”格式(24小时制)20:02:20
%tR“时:分”(24小时制)20:02

编写代码进行测试:

import java.util.Date;
import java.util.Locale;
public class TestDateTimeFormat {
    public static void main(String[] args) {
        Locale.setDefault(Locale.ENGLISH); // 设置当前语言环境为英文环境
        Date date = new Date();            // 获取当前日期的对象
        System.out.println(String.format("%tF", date));
        System.out.println(String.format("%tD", date));
        System.out.println(String.format("%tc", date));
        System.out.println(String.format("%tr", date));
        System.out.println(String.format("%tT", date));
        System.out.println(String.format("%tR", date));
    }
}

运行结果:

2021-11-02
11/02/21
Tue Nov 02 20:02:20 CST 2021
08:02:20 PM
20:02:20
20:02

5.4 基本类型字符串格式化

基本类型的格式化可以应用于任何参数类型,格式化转换符一览:

格式化转换符说明示例
%b%B结果被格式化为布尔类型false
%h%H结果被格式化为散列码(Hash值)eada0001
%s%S结果被格式化为字符串类型266.6666666666667
%c%C结果被格式化为字符类型‘N’
%d结果被格式化为十进制整数266
%o结果被格式化为八进制整数412
%x%X结果被格式化为十六进制整数10a
%e结果被格式化为科学计数法十进制数2.666667e+02
%a结果被格式化为带有有效位数和指数的十六进制浮点数266.666667
%n结果为换行符
%<n>.f结果被格式化为有n位小数的浮点数266.667
%%结果为字面值%%

编写代码进行测试:

public class TestBaseFormat {
    public static void main(String[] args) {
        System.out.println("3>5 格式化 %b 为 " + String.format("%b", 3>5));
        System.out.println("1 格式化 %b 为 " + String.format("%b", 1));
        System.out.println("800/3.0 格式化 %h 为 " + String.format("%h", 800/3.0));
        System.out.println("800/3.0 格式化 %s 为 " + String.format("%s", 800/3.0));
        System.out.println("78 格式化 %c 为 " + String.format("%c", 78));
        System.out.println("800/3 格式化 %d 为 " + String.format("%d", 800/3));
        System.out.println("800/3 格式化 %o 为 " + String.format("%o", 800/3));
        System.out.println("800/3 格式化 %b 为 " + String.format("%x", 800/3));
        System.out.println("800/3.0 格式化 %b 为 " + String.format("%e", 800/3.0));
        System.out.println("800/3.0 格式化 %.3f 为 " + String.format("%.3f", 800/3.0));
        System.out.println("800/3.0 格式化 %.6f 为 " + String.format("%.6f", 800/3.0));
        System.out.println("%n 为 " + String.format("%n"));
        System.out.println("%% 为 " + String.format("%%"));
    }
}

运行结果:

3>5 格式化 %b 为 false
1 格式化 %b 为 true
800/3.0 格式化 %h 为 eada0001
800/3.0 格式化 %s 为 266.6666666666667
78 格式化 %c 为 N
800/3 格式化 %d 为 266
800/3 格式化 %o 为 412
800/3 格式化 %b 为 10a
800/3.0 格式化 %b 为 2.666667e+02
800/3.0 格式化 %.3f 为 266.667
800/3.0 格式化 %.6f 为 266.666667
%n 为 

%% 为 %

6. 字符串生成器

查看源码我们了解到String类底层使用了字符型数组来存储字符串,但是用来存储字符串的字符型数组成员变量被final关键字修饰,
也就意味着字符串对象一旦完成初始化,它的长度就被固定了,并且内容不能被改变
在Java中虽然可以使用+运算符进行字符串的拼接,但是这种方法会产生一个新的String实例,会在内存中创建新的String对象,
如果重复的使用+运算符对字符串进行修改,将会极大地增加系统资源开销。

为了解决这个问题,Java提供了两种可变的字符串序列类StringBufferStringBuilder,它们也被称为字符串生成器,可以对字符串进行多次修改不会增加新的对象
大大提高了频繁修改字符串的效率。

6.1 StringBuffer 类与 StringBuilder 类的区别

AbstractStringBuilder抽象类位于java.lang包下,它是StringBuffer类和StringBuilder类的父类。

String类,StringBuffer类与StringBuilder类的继承树:

String类,StringBuffer类与StringBuilder类的继承树

AbstractStringBuilder抽象类已经定义好了绝大多数字符串生成器使用的方法,唯一的抽象方法为toString()

  • StringBuffer的方法都被synchronized关键字修饰,因此StringBuffer线程安全的,但执行效率要比StringBuilder
  • StringBuilder的方法没有被synchronized关键字修饰,因此StringBuffer非线程安全的,所以执行效率要比StringBuffer
  • StringBuilderStringBuffer都继承于AbstractStringBuilder抽象类,所以绝大多数方法的使用方式相同;
  • 在大多数情况下建议使用StringBuilder,因为它执行效率。但是在多线程环境下必须使用StringBuffer,因为它是线程安全的。

6.2 字符串生成器变量的初始化

StringBuilder为例,常用的构造方法如下:

  • StringBuilder():初始化一个空内容的生成器,默认设置生成器的容量为16
  • StringBuilder(int capacity):初始化一个空内容的生成器,可以指定生成器的容量
  • StringBuilder(String str):使用字符串str初始化一个生成器,此生成器的容量为str的长度+16
  • StringBuilder(CharSequence seq):使用字符序列seq初始化一个生成器,此生成器的容量为seq的长度+16

6.3 字符串生成器的容量

字符串生成器是可变长度的,所以它们的容量(Capacity)可以改变。

使用无参构造方法初始化的生成器的默认容量为16个字符:

StringBuilder sb = new StringBuilder();
System.out.println(sb.capacity());

// 运行结果
16

当生成器的容量不足时,会自动扩容当前容量,扩容规则:

  • 每次新增插入新字符序列时会对当前容量进行判断;
  • 确定需要的最小容量已经存储的字符串的长度加上准备存储的字符串的长度)是否大于当前容量,如果大于就扩容;
  • 容量扩充至当前容量的两倍+2再次判断需要的最小容量是否大于当前容量,如果小于扩容完成,如果大于扩容至需要的最小容量
  • 检测扩容后的容量(即char数组的长度)是否超过允许的最大值(Integer.MAX_VALUE - 8),如果超过则抛出异常(OutOfMemoryError)。
StringBuilder sb = new StringBuilder();
System.out.println(sb.capacity());
sb.append("ABCDEFGHIJKLMNOPQ");
// 添加17个字符,按照扩容规则16*2+2=34 > 17,所以扩容至当前容量的两倍
System.out.println(sb.capacity());
sb.append("ABCDEFGHIJKLMNOPQABCDEFGHIJKLMNOPQABCDEFGHIJKLMNOPQABC");
// 再添加54个字符,按照扩容规则34*2+2=70 < 17+54=71,所以扩容至最小容量

// 运行结果
16
34
71

6.4 字符串生成器的常用方法

StringBuilder为例,常用的方法如下:

  • length():获取生成器中当前字符串长度

  • capacity():获取生成器的当前容量

  • trimToSize():将生成器的当前容量缩小至当前字符串的长度。

StringBuilder sb = new StringBuilder();
sb.append("HelloWorld!");
System.out.println(sb.capacity());
sb.trimToSize();
System.out.println(sb.capacity());

// 运行结果
16
11
  • append(Object obj)新增方法有很多重载形式,几乎支持所有数据类型的新增操作,此方法直接把任何对象的字符串形式增加到当前字符串尾部
StringBuilder sb = new StringBuilder("Hello");
sb.append(1);
sb.append(false);
sb.append("World");
System.out.println(sb.toString());

// 运行结果
Hello1falseWorld
  • insert(int offset, Object obj)插入方法也有很多重载形式,此方法将对象的字符串形式插入至索引offset位置。
StringBuilder sb = new StringBuilder("Hello");
sb.insert(3, "123");
System.out.println(sb.toString());

// 运行结果
Hel123lo
  • delete(int start, int end)删除方法,删除索引start到索引end之间的字符串;
  • deleteChatAt(int index)删除指定位置方法:删除索引index位置的字符。
StringBuilder sb = new StringBuilder("HelloWorld!");
sb.delete(1, 5);
System.out.println(sb.toString());
sb.deleteCharAt(5);
System.out.println(sb.toString());

// 运行结果
HWorld!
HWorl!
  • replace(int start, int end, String str)替换方法:将索引start到索引end之间的字符串替换为指定字符串。
StringBuilder sb = new StringBuilder("HelloWorld!");
sb.replace(1, 5, "0");
System.out.println(sb.toString());

// 运行结果
H0World!
  • reverse()反转方法:将当前字符串反转。
StringBuilder sb = new StringBuilder("HelloWorld!");
sb.reverse();
System.out.println(sb.toString());

// 运行结果
!dlroWolleH
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值