【JavaSE】OJ 必备 String 类(字符串不可变、String类及常用方法)

在这里插入图片描述
希望通过博客和大家相互交流,相互学习,如有错误,请评论区指正

String类

在C语言当中是没有字符串类型的,通常是用字符数组来表示. Java当中是有字符串类型的,String类,在后期对字符串操作会很方便


从官方文档中可以看出,String类被final修饰,是不能被继承的

定义字符串

方式1:

String str = "hello";

方式2:

String str = new String("hello");

方式3:

char[] arr = {
    
    'h', 'e', 'l', 'l', 'o'};
String str = new String(arr);

String是java.lang底下的类,可以不用导入包

打印字符串

System.out.println(str);

字符串常量池

首先看如下代码:

public static void main(String[] args) {
    
    
    String str1 = "hello";
    String str2 = new String("hello");
    String str3 = "hello";
    System.out.println(str1 == str2);
    System.out.println(str1 == str3);
}

运行结果:

关于运行结果为什么是这样,我们通过图来说明

注意:

  1. 在JDK1.7之后字符串常量池被放到了堆中,并且内容是不重复的
  2. 常量在编译期就已经准备好了
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QFTBkgD0-1646610653409)(C:\Users\LY\AppData\Roaming\Typora\typora-user-images\image-20220111174335154.png)]
    使用 String 的 intern 方法来手动把 String 对象加入到字符串常量池中
public static void main(String[] args) {
    
    
    String str1 = "hello";
    String str2 = new String("hello").intern();
    System.out.println(str1 == str2);
}

如下图:

intern()会判断当前的字符串在常量池中是否存在,如果存在,把常量池中的引用复制给当前的引用类型变量

字符串比较

1. equals方法比较字符串相等

方法原型:

public boolean equals(Object anObject)
public static void main(String[] args) {
    
    
    String str1 = "hello";
    String str2 = "hello";
    System.out.println(str1 == str2);    // 方式1
    System.out.println(str1.equals(str2));    // 方式2
}

代码中给出了两种比较的方式,但只有第二种能够实现字符串的比较,方式1是在比较引用,方式2是调用String类中的equals方法来实现字符串比较相等

String源码:

Object源码:

其实可以看出,String类中的equals是重写了Object中的equals方法,如果没重写的话,默认是调用Object中的equals方法,比较的是两个引用

注意事项:

String str1 = null;
String str2 = "hello";
System.out.println(str2.equals(str1));     // 可以这么写
System.out.println(str1.equals(str2));     // 空指针异常
// 所以如果遇到可能为null的字符串,尽量写道equals的参数里面,这样就不会有空指针异常

2. 忽略大小写比较字符串相等

public boolean equalsIgnoreCase(String anotherString)
String str11 = "Hello";
String str12 = "hEllo";
System.out.println(str11.equalsIgnoreCase(str12));
// 运行结果
true

3. 比较字符串大小关系

要比较两个字符串的大小关系肯定是不能直接用两个引用去比较的,这样比没有意义

String源码

可以发现String类实现了Comparable接口

底层就是将字符串中的字符一一比较

若str1 > str2 返回正数

str1 < str2 返回负数

str1 == str2 返回0

实例:

String str1 = "Hello";
String str2 = "Welcome";
System.out.println(str1.compareTo(str2));
// 运行结果
-15

字符串不可变

如下代码:

str1 = "Welcome";
str1 += "to";
str1 += "Java";

通过这三行代码拼接字符串,字符串拼接的本质其实还是重新创建了对象

常量池中会一次存放出现的三个字符串,并不是String对象本身发生了变化,而是创建了新的String对象,str1指向了新的对象

利用反射修改原有对象的内容

反射是面向对象编程的一种重要特性, 有些编程语言也称为 “自省”

指的是程序运行过程中, 获取/修改某个对象的详细信息(类型信息, 属性信息等), 相当于让一个对象更好的 “认清
自己”

如下代码

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    
    
    String str1 = "Welcome";
    // 获取String类中的value字段,这个value和String源码中的value是匹配的
    Field field = String.class.getDeclaredField("value");
    // 将这个字段的访问属性设置为true
    field.setAccessible(true);
    // 获取 str 中的value属性
    char[] arr = (char[])field.get(str1);
    // 修改value的值
    arr[0] = 'b';
    System.out.println(str1);
}

运行结果:

字符、字符串、字节

字符与字符串

1. 字符数组转字符串
public String(char value[])
char[] arr2 = {
    
    'a', 'b', 'c'};
String str3 = new String(arr2);
System.out.println(str3);

运行结果

重载形式

public String(char value[], int offset, int count)
char[] arr3 = {
    
    'h','e','l','l','o'};
String str4 = new String(arr3, 1, 3);
System.out.println(str4);

// 运行结果
ell
2. 取得字符串中指定位置的字符
public char charAt(int index)
String str5 = "hello";
char ch = str5.charAt(1);  // 取str5中下标为1的字符
System.out.println(ch);
// 运行结果
e
3. 将字符串转换为字符数组
public char[] toCharArray()
String str6 = "hello";
char[] arr5 = str6.toCharArray();
System.out.println(Arrays.toString(arr5));
// 运行结果
[h, e, l, l, o]

字节与字符串

1. 将byte数组变为字符串
public String(byte bytes[])

重载形式

public String(byte bytes[], int offset, int length)
byte[] array = {
    
    97,98,99,100};
String string = new String(array);
System.out.println(string);
// 运行结果
abcd
2. 将字符串转换为byte数组

方法原型

public byte[] getBytes(String charsetName)
public byte[] getBytes(String charsetName) throws UnsupportedEncodingException

实例

String str7 = "abcde";
byte[] bytes = str7.getBytes();
System.out.println(Arrays.toString(bytes));
// 运行结果
[97, 98, 99, 100, 101]
3. char[ ]和byte[ ]区分

byte[ ] 是把 String 按照一个字节一个字节的方式处理, 这种适合在网络传输, 数据存储这样的场景下使用. 更适合
针对二进制数据来操作

char[ ] 是吧 String 按照一个字符一个字符的方式处理, 更适合针对文本数据来操作, 尤其是包含中文的时候

字符串替换

1. 替换原字符串中的指定字符
public String replace(char oldChar, char newChar)
String str1 = "Hello";
String str = str1.replace('l', 'b');   // 将str1中的'l'替换为'b'
System.out.println(str);

重载

public String replace(CharSequence target, CharSequence replacement)   // 支持了字符串替换

其他替换

public String replaceFirst(String regex, String replacement)   // 替换第一次出现的regex
public String replaceAll(String regex, String replacement)     // 替换所有regex

字符串截取

原型

public String substring(int beginIndex)     // 从字符串的beginIndex位置开始向后截取

实例:

String str = "hello";
str.substring(1);    // 从下标为1的位置向后截取
// 运行结果
ello

重载形式

public String substring(int beginIndex, int endIndex)   // 截取[beginIndex, endIndex)区间的字符串

字符串查找

1. 判断源串中是否包含子串

原型

public boolean contains(CharSequence s)

实例:

String str3 = "hello";
boolean flag = str3.contains("el");
boolean flag = str3.contains("el");
// 运行结果
true
2.模式匹配indexOf

Java底层的indexOf是用BF算法实现的

原型

public int indexOf(String str)    // 在源串中找str

重载

public int indexOf(String str, int fromIndex)    // 从fromIndex位置开始往后找

示例

String str4 = "aaabcaaaabcdeecd";
System.out.println(str4.indexOf("abcd"));
// 运行结果
8

lastIndexOf方法

原型

public int lastIndexOf(String str)

从后向前查找子字符串的位置

String str4 = "aaabcaaaabcdeecd";
System.out.println(str4.lastIndexOf("aaa"));
// 运行结果
6

重载

public int lastIndexOf(String str, int fromIndex)    // 从指定位置由后向前查找
3. 判断是否以指定字符串开头
public boolean startsWith(String prefix)         // 判断是否以指定字符串开头
public boolean startsWith(String prefix, int toffset)        // 从指定位置开始判断是否以指定字符串开头

实例

String str5 = "aaabcaaaabcdeecd";
System.out.println(str5.startsWith("aaa"));
System.out.println(str5.startsWith("aaa", 1));
System.out.println(str5.startsWith("aab", 1));
// 运行结果
true
false
true
4. 判断是否以指定字符串结尾

原型

public boolean endsWith(String suffix)

实例:

String str5 = "aaabcaaaabcdeecd";
System.out.println(str5.endsWith("cd"));
System.out.println(str5.endsWith("acd"));
// 运行结果
true
false

字符串拆分

public String[] split(String regex)    // 将字符串全部拆分; 空字符串的话就会按单个字符来拆分
public String[] split(String regex, int limit)    // 将字符串部分拆分,该数组长度就是limit极限
String str6 = "Welcome to java";
String[] s = str6.split(" ");     // 根据空格拆分字符串
System.out.println(Arrays.toString(s));
// 运行结果
[Welcome, to, java]

注意:

以下符号要在后面加\\才能达到目的

.   $   |   (   )   [   {   ^   ?   *   +

其他字符串操作

1. 去掉字符串两端空格

public String trim()

实例:

String strr = "    hel   lo  ";
System.out.println(strr.trim());
// 运行结果
hel   lo

2. 字符串转大写

public String toUpperCase()    // 将字符串中所有字符转为大写   (只对字母有效)

实例:

String strr1 = "Welcome To Java";
System.out.println(strr1.toUpperCase());
// 运行结果
WELCOME TO JAVA

3. 字符串转小写

public String toLowerCase()

实例:

String strr1 = "Welcome To Java";
System.out.println(strr1.toLowerCase());
// 运行结果
welcome to java

4. 字符串求长度

public int length()

5. 判断是否为空字符串

public boolean isEmpty()

StringBuffer 和 StringBuilder

使用

在前面对String类的对象通过+来拼接,其实是会产生临时变量的,如果多次拼接,就会产生大量临时对象,每次都在创建对象,然后改变引用的指向

通过StringBuffer和StringBuilder可以很方便的修改原对象并且不会产生临时变量

如下:

StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("hello");
stringBuilder.append("world");    // 返回当前对象的引用
System.out.println(stringBuilder);
// 运行结果
helloworld

这样就不会产生临时对象,并且是直接针对原对象修改

其实String的底层拼接就是被优化成了StringBuilder的append方法

如下代码:

String string = "a";
for (int i = 0; i < 10; i ++) {
    
    
    string += "b";
}
System.out.println(string);
// 运行结果
abbbbbbbbbb

反汇编:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YCjUFbZi-1646610653411)(C:\Users\LY\AppData\Roaming\Typora\typora-user-images\image-20220112230236529.png)]

String的内容无法修改,而StringBuffer的内容是可以修改的,因此在频繁修改字符串的情况下我们就可以考虑使用StringBuilder或StringBuffer

两者的区别

看看他们的append底层实现


比较明显的区别就是StringBuffer的append方法前面有synchronized修饰,说明它是线程安全的

String 和 StringBuilder适用于单线程情况

StringBuffer与StringBuilder大部分功能是相似的

String的内容不可修改,StringBuffer与StringBuilder的内容可以修改

StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作

猜你喜欢

转载自blog.csdn.net/weixin_46531416/article/details/123321462