希望通过博客和大家相互交流,相互学习,如有错误,请评论区指正
文章目录
- String类
-
- 定义字符串
- 打印字符串
- 字符串常量池
- 字符串比较
-
- 1. equals方法比较字符串相等
- 2. 忽略大小写比较字符串相等
- 3. 比较字符串大小关系
- 字符串不可变
-
- 利用反射修改原有对象的内容
- 字符、字符串、字节
-
- 字符与字符串
-
-
- 1. 字符数组转字符串
- 2. 取得字符串中指定位置的字符
- 3. 将字符串转换为字符数组
-
- 字节与字符串
-
-
- 1. 将byte数组变为字符串
- 2. 将字符串转换为byte数组
- 3. char[ ]和byte[ ]区分
-
- 字符串替换
-
-
- 1. 替换原字符串中的指定字符
-
- 字符串截取
- 字符串查找
-
-
- 1. 判断源串中是否包含子串
- 2.模式匹配indexOf
- 3. 判断是否以指定字符串开头
- 4. 判断是否以指定字符串结尾
-
- 字符串拆分
- 其他字符串操作
-
- 1. 去掉字符串两端空格
- 2. 字符串转大写
- 3. 字符串转小写
- 4. 字符串求长度
- 5. 判断是否为空字符串
- StringBuffer 和 StringBuilder
-
-
- 使用
- 两者的区别
-
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);
}
运行结果:
关于运行结果为什么是这样,我们通过图来说明
注意:
- 在JDK1.7之后字符串常量池被放到了堆中,并且内容是不重复的
- 常量在编译期就已经准备好了
使用 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
反汇编:
String的内容无法修改,而StringBuffer的内容是可以修改的,因此在频繁修改字符串的情况下我们就可以考虑使用StringBuilder或StringBuffer
两者的区别
看看他们的append底层实现
比较明显的区别就是StringBuffer的append方法前面有synchronized修饰,说明它是线程安全的
String 和 StringBuilder适用于单线程情况
StringBuffer与StringBuilder大部分功能是相似的
String的内容不可修改,StringBuffer与StringBuilder的内容可以修改
StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作
转载请注明:【JavaSE】OJ 必备 String 类(字符串不可变、String类及常用方法) | 胖虎的工具箱-编程导航