Java 面向对象(八)

时间:2023-03-09 09:47:59
Java 面向对象(八)

常用类之String

String字符串类

什么是字符串?就是把多个字符,串连一起。

String字符串的本质

其实是一个char[]数组

/**该值用于字符存储。*/
private final char value [];

Java 面向对象(八)

String字符串分类

(1)不可变字符串:String :定义好之后,就不能再去改变了(内存地址不可变,一旦修改了,就会创建一个新的内存地址)

(2)可变字符串:StringBuilder/StringBuffer :定义好之后,还可以进行修改, 改变时,不会创建新的内存地址。

String, StringBuffer, StringBuilder 都实现了 CharSequence 接口。

String字符串创建

(1)直接赋值:String str = "myxq";

(2)通过构造器来创建:String str2 = new String("myxq");

两种创建的区别

方法区当中有一个常量池,存放常量。

(1)String str = "ABCD";

  • 这种创建字符串方式,要么创建一个对象,要么不创建。

  • 创建时会先到常量池当中看一下有没有存在该字符串常量。

  • 如果已经有了,就直接使用,不会创建新的地址。(不创建对象)

  • 如果常量池当中没有的话,就会在常量没当中创建一个对象。

(2)String str2 = new String("ABCD");

  • 这种创建字符串方式,至少得要创建一个对象(因为使用了new,至少要在堆当中创建一个对象)

  • 看一下,常量池当中,有没有传入的字符串常量。

  • 如果没有,会创建一个字符串常量,放到常量池当中。

Java 面向对象(八)

String字符串对象值为空

(1)表示引用为空:String str = null; 还没有初始化,没有分配内存空间。(地址为空)

(2)表示空字符串:String str = ""; 已经创建了对象,已经分配了内存。(内容为空)

String字符串判等(判断是否相等)

比较两个字符串相不相等。

(1) == :比较两个内存地址是否相等。

(2)使用equals :它是object的方法。在object中和 == 相同,都是比较两个内存地址是否相等。

public boolean equals(Object obj) {
return (this == obj);
}

建议子类,自己去覆盖此方法,自己在内部当中去根据自己的需求来去判断两个值是否相等。

String 已经覆盖了 equals:

public boolean equals(Object anObject) {

	// 1.判断传入的值是否与当前字符串地址相同,如果相同就直接返回true
if (this == anObject) {
return true;
} // 2.如果不相等,再去判断传入的字符串是否为String类型。如果不,直接返回false
if (anObject instanceof String) { // 是String,逐个判断每一个字符相不相等,如果全部相等返回true
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}

实例:

public static void main(String[] args) {
// 在内存当中不能再去修改了,修改,就会创建新的地址
String str = "ABC"; String str2 = new String("ABC"); // 比的是对象的地址
if (str == str2) {
System.out.println("地址相等");
} else {
System.out.println("地址不相等");
} // 因为String 覆盖了equals,比的是对象的内容
if (str.equals(str2)) {
System.out.println("内容相等");
} else {
System.out.println("内容不相等");
}
} /* 输出结果:
* 地址不相等
* 内容相等
*/

String字符串编译优化

public static void main(String[] args) {
String str1 = "ABCD";
String str2 = "A" + "B" + "C" + "D";
String str3 = "AB" + "CD";
String str4 = new String("ABCD");
String temp = "AB";
String str5 = temp + "CD";
System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // true
System.out.println(str1 == str4); // false
System.out.println(str1 == str5); // false
}

反编译后;

public static void main(String args[])
{
String str1 = "ABCD";
String str2 = "ABCD";
String str3 = "ABCD";
String str4 = new String("ABCD");
String temp = "AB";
String str5 = (new StringBuilder(String.valueOf(temp))).append("CD").toString();
System.out.println(str1 == str2);
System.out.println(str1 == str3);
System.out.println(str1 == str4);
System.out.println(str1 == str5);
}

凡是单独使用 "" 引用起来,都是直接量,在编译时期就已经确定好了。编译器会自动编译优化。

String与char[]之间的转换

public static void main(String[] args) {
// char[] -> String
char[] cs = new char[] { 'A', 'B', 'C' };
String str = new String(cs);
System.out.println(str); // 字符串 -> char[]
char[] cs2 = str.toCharArray();
for (char c : cs2) {
System.out.print(c + " ");
}
}

获取字符串当中的信息

(1)返回字符串长度

String str = "myxq";
int length = str.length();
System.out.println("字符串长度:" + length); /*
* 字符串长度:4
*/

(2)返回字符串当中的某一个(索引处的)字符

String str = "myxq";
char c = str.charAt(2);
System.out.println("字符串中的第三个字符:" + c); /*
* 字符串中的第三个字符:x
*/

(3)返回指定字符或子字符串在字符串当中第一次出现的位置(索引)

如果此字符串中没有这样的字符或子字符串,则返回 -1。

String str = "ABCDEFG ABCD ABCD";
char c = 'C';
String sub = "BCD";
int i1 = str.indexOf(c);
int i2 = str.indexOf(sub);
int i3 = str.indexOf(c, 7);
int i4 = str.indexOf(sub, 7);
System.out.println("字符 c 在字符串中从头开始第一次出现的索引:" + i1);
System.out.println("子字符串 sub 在字符串中从头开始第一次出现的索引:" + i2);
System.out.println("字符 c 在字符串中从索引7之后(包括7)第一次出现的索引:" + i3);
System.out.println("子字符串 sub 在字符串中从索引7之后(包括7)第一次出现的索引:" + i4); /*
* 字符 c 在字符串中从头开始第一次出现的索引:2
* 子字符串 sub 在字符串中从头开始第一次出现的索引:1
* 字符 c 在字符串中从索引7之后(包括7)第一次出现的索引:10
* 子字符串 sub 在字符串中从索引7之后(包括7)第一次出现的索引:9
*/

(4)返回指定字符或子字符串在字符串当中最后一次出现的位置(索引)

如果此字符串中没有这样的字符或子字符串,则返回 -1。

String str = "ABCDEFG ABCD ABCD";
char c = 'C';
String sub = "BCD";
int i1 = str.lastIndexOf(c);
int i2 = str.lastIndexOf(sub);
int i3 = str.lastIndexOf(c, 12);
int i4 = str.lastIndexOf(sub, 12);
System.out.println("字符 c 在字符串中从头开始最后一次出现的索引:" + i1);
System.out.println("子字符串 sub 在字符串中从头开始最后一次出现的索引:" + i2);
System.out.println("字符 c 在字符串中从索引12之前(包括12)最后一次出现的索引:" + i3);
System.out.println("子字符串 sub 在字符串中从索引12之前(包括12)最后一次出现的索引:" + i4); /*
* 字符 c 在字符串中从头开始最后一次出现的索引:15
* 子字符串 sub 在字符串中从头开始最后一次出现的索引:14
* 字符 c 在字符串中从索引12之前(包括12)最后一次出现的索引:10
* 子字符串 sub 在字符串中从索引12之前(包括12)最后一次出现的索引:9
*/

字符串字母大小写转换

String str = "The ABCD";

// 把大写字母转成小写
System.out.println(str.toLowerCase()); // 小写字母转成大写
System.out.println(str.toUpperCase()); /*
* the abcd
* THE ABCD
*/

字符串忽略大小写判等(判断是否相等)

String str = "The ABCD";

// 把大写字母转成小写
String lowerCase = str.toLowerCase(); // 小写字母转成大写
String upperCase = str.toUpperCase(); // 比较地址
System.out.println(lowerCase == upperCase); // false // 比较内容
System.out.println(lowerCase.equals(upperCase)); // false // 忽略大小写的比较内容
System.out.println(lowerCase.equalsIgnoreCase(upperCase)); // true

字符串的比较

// 按字典顺序比较两个字符串。
int compareTo(String anotherString) // 按字典顺序比较两个字符串,不考虑大小写。
int compareToIgnoreCase(String str)

比较规则:

如果第一个字符和参数的第一个字符不等,结束比较,返回他们之间的差值。

如果第一个字符和参数的第一个字符相等,则以第二个字符和参数的第二个字符做比较,以此类推。

直至比较的字符或被比较的字符有一方结束。若之前比较都相等,则返回两字符串的长度差值。

// 获取两字符串的长度
int len1 = str1.length();
int len2 = str2.length(); // 取两字符串的长度的最小值
int lim = Math.min(len1, len2); // 将两字符串转为字符数组
char[] v1 = str1.toCharArray();
char[] v2 = str2.toCharArray(); /*
* 比较规则:
* 如果第一个字符和参数的第一个字符不等,结束比较,返回他们之间的差值。
* 如果第一个字符和参数的第一个字符相等,则以第二个字符和参数的第二个字符做比较,以此类推。
* 直至比较的字符或被比较的字符有一方结束。若之前比较都相等,则返回两字符串的长度差值。
*/ int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;

例子:

String str = "ABCD";
String str2 = "AbCDEF";
String str3 = "ABCDEF"; int i = str.compareTo(str2);
System.out.println(i); // -32 int i2 = str.compareTo(str3);
System.out.println(i2); // -2 int i3 = str2.compareToIgnoreCase(str);
System.out.println(i3); // 2

字符串判断是否包含

// 当且仅当此字符串包含指定的 char 值序列时,返回 true。
boolean contains(CharSequence s)

底层实现:

public boolean contains(CharSequence s) {
return indexOf(s.toString()) > -1;
}

例子:

String str = "ABCD";
boolean contains = str.contains("BC");
System.out.println(contains); // true

字符串的截取

// 截取范围:[beginIndex, 最后]
String substring(int beginIndex) // 截取范围:[beginIndex, endIndex)
String substring(int beginIndex, int endIndex)

例子1:

String str = "ABCDEFG-HIJKLMN.OPQRST";
int beginIndex = str.indexOf('-');
int endIndex = str.lastIndexOf('.');
String str1 = str.substring(beginIndex);
System.out.println(str1); // -HIJKLMN.OPQRST
String str2 = str.substring(beginIndex, endIndex);
System.out.println(str2); // -HIJKLMN

例子2:首字母大写

String name = "myxq";
// 1.获取第一个字母 m
String res = name.substring(0, 1);
// 2.把获取的第一个字母变成大写
res = res.toUpperCase();
// 3.获取除第一个字母外后,后面所有内容yxq
String lastString = name.substring(1);
// 4.把大写的字母与其拼接
System.out.println(res + lastString); // 整合
String str = name.substring(0, 1).toUpperCase() + name.substring(1);
System.out.println(str);

字符串的前缀和后缀

// 判断是否以指定的后缀结束。
boolean endsWith(String suffix) // 判断是否以指定的前缀开始。
boolean startsWith(String prefix) // 判断从指定索引开始的子字符串是否以指定前缀开始
boolean startsWith(String prefix, int toffset)

例子:

String str = "Hello.java";
boolean b1 = str.startsWith("Hello");
boolean b2 = str.endsWith(".class");
boolean b3 = str.startsWith("llo", 2);
System.out.println(b1); // true
System.out.println(b2); // false
System.out.println(b3); // true

字符串的分割和合并

// 分割
String[] split(String regex) // 分割
String[] split(String regex, int limit) // 合并(1.8之后)
static String join(CharSequence delimiter, CharSequence... elements)

例子:

// 分割
String str = "An-apple-a-day-keeps-the-doctor-away";
String[] split = str.split("-");
System.out.println(Arrays.toString(split)); // limit 参数控制模式应用的次数,如果该限制 n>0,则模式将被最多应用 n-1 次,数组的长度将不会大于 n,
String[] split2 = str.split("-", 3);
System.out.println(Arrays.toString(split2)); // 合并
String join = String.join(" ", split);
System.out.println(join);

正则分割

// 提取字符串中的数字
String str = "2019年成绩:语文88分,数学98分,英语95分"; // 定义分割符:非数字 ( \D:非数字字符匹配。等效于 [^0-9]。)
String regex = "\\D+";
String[] split = str.split(regex);
System.out.println(Arrays.toString(split));

格式化字符串

// 使用指定的格式字符串和参数返回一个格式化字符串。
static String format(String format, Object... args)

例子:

int i = 25;
double d = 23.456;
String str = String.format("i=%-5d, d=%6.2f", i, d);
System.out.println(str); // i=25 , d= 23.46

正则表达式

正则表达式定义了字符串的模式。

正则表达式可以用来搜索、编辑或处理文本。

正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别。

正则表达式定义

正如他的名字一样,是描述了一个规则,通过这个规则可以匹配一类字符串。

学习正则表达式很大程度上就是学习正则表达式的语法规则。

正则表达式语法

(1)普通字符

字母、数字、汉字、下划线,以及没有特殊意义的标点符号,都是普通字符,表达式中的普通字符,在匹配一个字符串的时候,匹配与之相同的一个字符

(2)简单的转义字符

转义字符 说明
\n 换行符
\t 制表符
\\ \本身
\^, \$, \., \(, \), \{, \}, \?, \+, \*, \[, \] \| 匹配这些字符本身

(3)标准字符集合

能够与 “多种字符” 匹配的表达式。

注意区分大小写,大写是相反的意思。

字符集合 说明
. 任何字符(与行结束符可能匹配也可能不匹配)
\d 数字:[0-9]
\D 非数字: [^0-9]
\s 空白字符(空格,制表符,换行符等):[ \t\n\x0B\f\r]
\S 非空白字符:[^\s]
\w 单词字符(任意一个字母或数字或下划线):[a-zA-Z_0-9]
\W 非单词字符:[^\w]

小数点(.)可以匹配任意一个字符,如果要匹配包括 "\n" 在内的所有字符,一般用 [\s\S]

(4)自定义字符集合

[]方括号匹配方式,能够匹配方括号中任意一个字符。

字符集合 说明
[abc] a、b 或 c(简单类)
[^abc] 任何字符,除了 a、b 或 c(否定)
[a-zA-Z] a 到 z 或 A 到 Z,两头的字母包括在内(范围)
[a-d[m-p]] a 到 d 或 m 到 p:[a-dm-p](并集)
[a-z&&[def]] d、e 或 f(交集)
[a-z&&[^bc]] a 到 z,除了 b 和 c:[ad-z](减去)
[a-z&&[^m-p]] a 到 z,而非 m 到 p:[a-lq-z](减去)

正则表达式的特殊符号,被包含到中括号中,则失去特殊意义,除了^,-之外。

标准字符集合,除小数点外,如果被包含于中括号中,自定义字符集合将包含该集合,比如

[\d.\-+]将匹配:数字、小数点、-、+

(5)量词

修饰匹配次数的特殊符号

量词 说明
X? X,零次或一次
X* X,零次或多次
X+ X,一次或多次
X{n} X,恰好 n 次
X{n,} X,至少 n 次
X{n,m} X,至少 n 次,但是不超过 m 次

匹配次数中的贪婪模式(默认的,匹配字符越多越好)

Java 面向对象(八)

匹配次数中的非贪婪模式(匹配字符越少越好,修饰匹配次数的特殊符号后,再加上一个 "?" 问号

量词 说明
X?? X,零次或一次
X*? X,零次或多次
X+? X,一次或多次
X{n}? X,恰好 n 次
X{n,}? X,至少 n 次
X{n,m}? X,至少 n 次,但是不超过 m 次

Java 面向对象(八)

(6)字符边界

本组标记匹配的不是字符而是位置,符合某种条件的位置

字符边界 说明
^ 与字符串开始的地方匹配
$ 与字符串结束的地方匹配
\b 匹配一个单词边界

(7)选择符和分组

符号 说明
XY X 后跟 Y
X Y
(X) X,作为捕获组
(?:X) X,作为非捕获组

反向引用:

每一对(),会分配一个编号,使用()的捕获,根据左括号的顺序,从1开始自动编号,通过反向引用,可以对分组已捕获的字符串进行引用。

Java 面向对象(八)

(8)零宽断言

符号 说明
(?=X) 断言自身出现的位置的后面能匹配表达式X
(?!X) 断言此位置的后面不能匹配表达式X
(?<=X) 断言自身出现的位置的前面能匹配表达式X
(?<!X) 断言此位置的前面不能匹配表达式X

Java 面向对象(八)

java 正则表达式

java.util.regex 包主要包括以下三个类:

(1)Pattern 类

pattern 对象是一个正则表达式的编译表示。

Pattern 类没有公共构造方法。要创建一个 Pattern 对象,你必须首先调用其公共静态编译方法,它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。

(2)Matcher 类

Matcher 对象是对输入字符串进行解释和匹配操作的引擎。

与Pattern 类一样,Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。

(3)PatternSyntaxException

PatternSyntaxException 是一个非强制异常类,它表示一个正则表达式模式中的语法错误。

例子1:正则匹配

// 创建 Pattern 对象
String regex = "\\d+"; // 定义正则表达式:注意\变成\\
Pattern p = Pattern.compile(regex); // 创建 Matcher 对象
String input = "123456"; // 目标字符串
Matcher m = p.matcher(input); // 给定正则表达式与给定的整个输入匹配
boolean b = m.matches();
System.out.println(b);

例子2:正则查找

String str = "2019年成绩:语文88.5分,数学98.5分,英语95分";
String regex = "\\d+\\.\\d+";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(str);
while (m.find()) {
System.out.println(str.substring(m.start(), m.end()));
// System.out.println(m.group());
}

字符串匹配

// 返回此字符串是否匹配给定的正则表达式。
boolean matches(String regex)

例子:判断字符串是否全是数字(正则匹配)

// 定义正则表达式:注意\变成\\
String regex = "\\d+"; // 目标字符串
String input = "123456"; boolean b = input.matches(regex);
System.out.println(b); // true

字符串的替换

// 返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
String replace(char oldChar, char newChar) // 使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。
String replace(CharSequence target, CharSequence replacement) // 使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
String replaceAll(String regex, String replacement) // 使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
String replaceFirst(String regex, String replacement)

例子:正则替换

// 定义正则表达式
String regex = "[a-zA-Z]+"; // 目标字符串
String str = "第一节String 第二节Object 第三节Collection"; String newStr = str.replaceAll(regex, "***");
System.out.println(newStr); // 第一节*** 第二节*** 第三节***

字符串去空格

(1)去首尾空格

// 返回字符串的副本,忽略前导空白和尾部空白。
String trim()

(2)去全部空格(用字符串替换)

例子:

String str = "  A  B   CD E   ";

// 去首尾空格
String trim = str.trim();
System.out.println(trim); // 去全部空格
String replace = str.replace(" ", "");
System.out.println(replace);

常用类之StringBuilder

可变的字符串和不可变字符串

(1)不可变字符串String : 定义好之后,就不能再去改变了。(内存地址不可变)

(2)可变的字符:定义好之后,还可以进行修改。改变时,不会创建新的内存地址。

StringBuilder :方法前面没有synchronized ,效率比StringBuffer高一些

StringBuffer :方法前面多了一个synchronized 加锁,更安全,但效率会比StringBuilder低了一些

可变的字符串比不可变字符串性能高很多。

效率:String < StringBuffer < StringBuilder

性能测试:

static int N = 10000;

static void testString() {
long begin = System.currentTimeMillis(); String str = "";
for (int i = 0; i <= N; i++) {
str += i;
} long last = System.currentTimeMillis();
System.out.println(last - begin);
} static void testBuilder() {
long begin = System.currentTimeMillis(); StringBuilder str = new StringBuilder();
for (int i = 0; i <= N; i++) {
str.append(i);
} long last = System.currentTimeMillis();
System.out.println(last - begin);
} static void testBuffer() {
long begin = System.currentTimeMillis(); StringBuffer str = new StringBuffer();
for (int i = 0; i <= N; i++) {
str.append(i);
} long last = System.currentTimeMillis();
System.out.println(last - begin);
} public static void main(String[] args) {
testString();
testBuilder();
testBuffer();
}

StringBuilder 构造器和常用方法

构造方法

// 构造一个其中不带字符的字符串生成器,初始容量为 16 个字符。
StringBuilder() // 构造一个其中不带字符的字符串生成器,初始容量由 capacity 参数指定。
StringBuilder(int capacity) // 构造一个字符串生成器,包含与指定的 CharSequence 相同的字符。
StringBuilder(CharSequence seq) // 构造一个字符串生成器,并初始化为指定的字符串内容。
StringBuilder(String str)

常用方法

// 字符串追加
StringBuilder append(Object obj) // 字符串插入
StringBuilder insert(int offset, Object obj) // 删除此序列的子字符串中的字符
StringBuilder delete(int start, int end) // 删除此序列中指定位置的字符。
StringBuilder deleteCharAt(int index) // 用指定String中的字符替换此序列的子字符串中的字符。
StringBuilder replace(int start, int end, String str) // 字符串反转
StringBuilder reverse() // 设置指定索引处的字符
void setCharAt(int index, char ch) // 返回指定索引处的此序列中的char值
char charAt(int index) // 返回当前容量
int capacity() // 返回指定子字符串第一次出现的字符串中的索引
int indexOf(String str) // 从指定的索引处开始,返回指定子字符串第一次出现的字符串中的索引。
int indexOf(String str, int fromIndex) // 返回指定子字符串最后一次出现的字符串中的索引
int lastIndexOf(String str) // 从指定的索引处之前,返回指定子字符串最后一次出现的字符串中的索引。
int lastIndexOf(String str, int fromIndex) // 返回长度(字符数)
int length() // 字符串截取,截取范围:[beginIndex, 最后]
String substring(int start) // 字符串截取,截取范围:[beginIndex, endIndex)
String substring(int start, int end)

例子:

// 创建的可变字符串,初始容量为16
// 如果超过的话,它会自动扩容
StringBuilder builder = new StringBuilder();
System.out.println(builder.capacity()); // 链式编程
builder.append("ABCDeFG").append("123"); System.out.println(builder);
System.out.println(builder.charAt(4));
builder.setCharAt(4, 'E');
System.out.println(builder); builder.insert(7, 0);
System.out.println(builder); builder.deleteCharAt(7);
System.out.println(builder); // 字符串的反转
builder.reverse(); // 可变字符转成不可变
String s = builder.toString();
System.out.println(s);

StringBuilder 的本质

StringBuilder 的本质还是一个char类的数组

Java 面向对象(八)