(点击跳转即可哦)
认识String类
目录
JDK中String类的声明
public final class String
为何String类 被final修饰?
被final修饰的类无法被继承,所以String类不存在子类。这样的话就可以保证所有使用JDK的人用到的String类是同一个String类。
假设现在String允许继承,每个人都可以继承String类,修改它的方法等,多个完全不同的子类都可以向上转型为String,那别人拿来使用时,根本不知道String对象的行为是什么。
继承和方法覆写 在带来灵活性的同时,也会带来很多子类行为不一致导致的问题。
1 创建字符串的四种方式
- 直接赋值
String str1 = "hello word";
- 通过构造方法产生对象
String str2 = new String("hello word");
- 通过字符数组产生对象
char[] data = new char[]{'a', 'b','c'};
String str3 = new String(data);
- 通过String的静态方法valueOf(任意数据类型) -> 转为字符串
String str4 = String.valueOf(13);
字面量: 直接写出来的数值就称之为字面量
10 -> int字面量
1.5 -> double字面量
true -> boolean字面量
“abc” -> String字面量 ,就是一个字符串的对象
String str = "hello word;
右边的就是字符串字面量,也是字符串的对象。
String str = "hello word";
String str1 = str;
str1 = "Hello";
System.out.println(str);
输出结果:
hello word
为何输出结果是 “hello word”呢?
因为 “Hello” 也是字符串的字面量,是一个新的字符串对象,str1 实际上指向了新的字符串对象"Hello", str 指向的仍然是原字符串对象"hello word"。
String是不可变的。
2 字符串比较相等
所有引用数据类型在比较是否相等时,使用equals方法比较,JDK常用类,都已经覆写了equals方法,直接使用即可。
引用数据类型使用“==”,比较的仍然是数值(地址是否相等)
equals
在比较时,是会进行区分大小写的比较。
equalsIgnoreCase
在比较时,是不区分大小写的比较。
当在实际中,牵扯到用户输入就一定要做判空处理。
//这个变量由用户从浏览器输入 String user = null; System.out.println(user.equals("张三")); //有可能会报空指针异常 System.out.println("张三".equals(user));
因为我们要比较的特定内容本身就是字符串的字面量,一定不是空对象,把要比较的内容放在equals的前面,就可以方便处理user为空的问题。
3 关于字符串常量池问题
当使用直接赋值法产生字符串对象时,JVM会维护一个字符串的常量池,若该对象在常量池中还不存在,则产生一个新的字符串对象加入到字符串常量池中。
当继续使用直接赋值法产生字符串对象时,JVM发现该引用指向的内容已经在常量池中存在了,则此时不再新建字符串对象,而是复用已有的对象。
String str1 = "hello";
String str2 = "hello";
String str3 = "hello";
“hello” 是字符串常量,
第一次出现时,此时字符串常量池中不存在,新建一个该对象加入常量池。
str2 和 str3 仍然使用直接赋值法产生对象,但是该内容已经在常量池中存在,此时并不会产生新的对象,而是复用已有对象。
String str1 = new String("hello");
String str2 = new String("hello");
String str3 = new String("hello");
第一行代码——先产生“hello”字符串常量,此时字符串常量池中没有,把"hello"放入字符串常量池,然后new 了一个新的对象,在堆中开辟了新的空间,存储了"hello"。
第二行代码——还是先产生了"hello"字符串常量,但是此时字符串常量池中已经有了该字符串,直接复用常量池中的"hello",继续new,在堆中开辟新的空间,存储"hello"。
第三行代码——和第二行代码一样。
所以,这三行代码产生了四个字符串对象,其中 一个在常量池中,其余三个在堆中。
手工入池:String类提供的intern方法
intern方法
public native String intern();
调用intern方法会将当前字符串引用 指向的对象 保存到字符串常量池中。
- 若当前常量池中已经存在了该对象,则不在产生新的对象,返回常量池中的String对象。
- 若当前常量池中不存在该对象,则将该对象入池,返回入池后的地址。
看这个代码会输出什么?
String str1 = new String("hello");
str1.intern();
String str2 = "hello";
System.out.println(str1 == str2);
输出结果为:
false
因为在第一行代码中,先在字符串常量中加入了一个"hello"的字符串常量,然后又new 了一个对象,在堆中存储了一个 “hello”,
第二行代码:常量池中已经存在了"hello",不会再产生新的对象,直接返回常量池中"hello" 的地址。但是此时没有对象接收返回的地址,所以str1 指向的还是在new 的那个地址。
str1 = str1.intern();
此时,str1 就接收到了常量池中的"hello"的地址。
再看这个代码:
char[] data = new char[]{'a','b',c'};
String str1 = new String(data);
str1.intern();
String str2 = "abc";
System.out.println(str1 == str2);
输出结果:
true
此时是因为第二行代码 传入的参数是一个字符串数组,此时没有产生字符串对象。所以此时intern
的操作是 当前常量池中不存在该对象,则将该对象入池,返回入池后的地址。此时的入池操作是直接将str1 指向的字符串对象移动到常量池中,str1此时就指向的字符串就已经在常量池中了。
4 字符串的不可变性
所谓的字符串不可变指的是 字符串对象的内容不能变,而不是字符串引用不能变。
为何字符串的对象无法修改内容,而其他类的对象能修改内容?
字符串其实就是一个字符数组 -> char[], 字符串保存的值实际上在数组中保存。而String类中定义的是
private final char value[];
被private final 修饰,所以字符串对象的内容就无法修改, String类的外部根本拿不到value这个数组。
5 如何修改字符串内容
1 在运行时通过反射破坏 value数组的封装。(不推荐,反射是所有框架的基础)
2 更换使用 StringBuilder 或者StringBuffer类,已经和String不是一个类型了
StringBuffer 使用方法和StringBuilder 完全一样,线程安全,性能较差。
使用 StringBuilder类
若需要频繁进行字符串的拼接,使用StringBilder类的append方法。
StringBuilder
类可以修改对象的内容。
public static void main(String[] args) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("张三");
stringBuilder.append("李四");
System.out.println(stringBuilder);
}
关于StringBuilder类的具体使用:
StringBuilder 类和 String类是两个独立的类,StringBuilder就是为了解决字符串拼接问题产生的。
因为String的对象无法修改内容,为了方便字符串的拼接操作,产生了StringBuilder类,StringBuilder类的对象是可以修改内容的。
StringBuilder -> String
调用StringBuilder类的toString方法
public static void main(String[] args) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("张三");
stringBuilder.append("李四");
//变为String类
String str = stringBuilder.toString();
}
String -> StringBuilder
使用StringBuilder 的构造方法
StringBuilder stringBuilder = new StringBuilder("hello");
使用StringBuilder的append方法
stringBuilder.append("word");
StringBuilder类的其他方法
因为StringBuilder类可以修改内容,因此具备一些String类不具备的修改内容的功能,除了拼接append方法外。
- 字符串反转操作,StringBuilder 类提供的 reverse();
public static void main(String[] args) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("张三");
stringBuilder.append("李四");
stringBuilder.reverse();
System.out.println(stringBuilder);
}
- 删除指定范围的数据,delete(int start, int end); 删除从start索引开始end之前所有内容。[start,end)
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("abcdefg");
stringBuilder.delete(4,7);
System.out.println(stringBuilder);
输出结果:
abcd
- 插入操作,insert(int start, 各种数据类型); 将新元素插入当前StringBuilder类的对象,插入后新数值的起始索引为 start
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("abcdefg");
stringBuilder.insert(4,"插入");
System.out.println(stringBuilder);
输出:
abcd插入efg
请解释String、StringBuilder、StringBuffer的区别?
-
String的对象是无法修改的,其他两个类的对象都是可以修改的
-
StringBuffer是线程安全的操作,性能较差;StringBuilder是线程不安全,性能较高。
6 String类 字符串的常见方法使用
1 字符串比较
//区分大小写的比较
public boolean equals(Object anObject)
//不区分大小写的比较
public boolean equalsIgnoreCase(String anotherString)
//比较两个字符串大小关系,逐个字符进行比较,返回当前对象字符与比较对象字符的差
public int compareTo(String anotherString)
compareTo 方法
String a = "abc";
String b = "ABC";
System.out.println(a.compareTo(b));
输出:
32
A的ASCII码值:A -> 65, a的ASCII码值:a -> 97
97-65 = 32
System.out.println(b.compareTo(a));
此时输出就是 -32 ,
65 - 97 = -32
String类 也实现了Conparable接口,覆写了compareTo方法。
字符串的compareTo方法是 按照字符串内部的每个数组进行ASCII的比较。
按照"字典序"排列字符串,就是按照字符串内部的字符的ASCII码大小进行排序。
2 字符和字符串的相互转换
字符串的内部实际上就是使用字符数组来存储的
char -> String, 通过String 的构造方法
char[] ch = new char[]{'a','b','c'};
String str = new String(ch);
将字符数组的部分内容转为字符串对象
public String(char value[], int offset,int count)
offset
: 字符数组开始的索引
count
: 转换的字符个数
看具体使用代码:
char[] ch = new char[]{'a','b','c','d'};
String str = new String (ch,1,2);
Systm.out.println(str);
输出:
bc
String -> char
1 取出字符串中指定索引的字符
//索引从0开始,返回索引位置的字符
public char charAt(int index)
看代码:
String str = "word";
System.out.println(str.charAt(1));
输出:
o
2 将字符串中的内容转为字符数组 String => char[]
//将字符串变为字符数组 返回一个字符数组
public char[] toCharArray()
产生了一个新的字符数组,将字符串的内容复制过去了
看代码:
String str = "hello";
char[] ch = str.toCharArray();
System.out.println(Arrays.toString(ch));
输出:
[h, e, l, l, o]
如何判断一个字符串的对象是否为纯数字组成
public static boolean isNumber(String str){
char[] ch = str.toCharArray();
//循环遍历ch 中的每个字符,判断这个字符是否为数字字符
//['0',....'9']
for(char i : ch){
// if(i < '0' || i > '9'){
// //找到反例
// return false;
// }
if(!Character.isDigit(i)){
return false;
}
}
return true;
}
isDigit
是char 包装类(Character)的方法,判断字符是否为数字,若是返回true,否则返回false.
3 字符串和字节之间的相互转换,将字符串保存到文件中或是通过网络传输都要用到字节数组
String -> byte[]
//将字符串以字节数组的形式返回,按照当前默认的字符编码转为字节
public byte[] getBytes()
//编码转换处理,按照指定的编码格式转为字节数组
public byte[] getBytes(String charsetName)throws UnsupportedEncodingException
看代码:
String str1 = "中国";
byte[] by = str1.getBytes();//JDK 1.8 ,默认编码格式为 UTF-8
System.out.println(Arrays.toString(by));
输出:
[-28, -72, -83, -27, -101, -67]
在UTF8编码下,一个汉字3个字节
在GBK编码下,一个汉字2个字节
byte[] -> String
按照ASCII码转换为 String
//将字节数组变为字符串,通过String的构造方法
public String (byte byte[])
//将部分字节数组中的内容变为字符串,通过String的构造方法
public String(byte byte[],int offset,int length)
看代码:
byte[] by = {97, 98,99,100};
String str1 = new String(by);
String str2 = new String(by,1,2);
System.out.println(str1);
System.out.println(str2);
输出:
abcd
bc
4 字符串查找操作
从一个完整的字符串之中可以判断指定内容是否存在,对于查找方法由如下定义:
常用:contains
,startsWith
,endsWith
//判断一个子字符串是否存在
public boolean contains(CharSequence s)
//从头开始查找指定字符串的位置,查到了返回位置的开始索引,查不到返回-1
public int indexOf(String str)
//从指定位置开始寻找子字符串位置
public int indexOf(String str,int fromIndex)
//由后向前查找子字符串位置
public int lastIndexOf(String str)
//从指定位置由后向前查找子字符串位置
public int lastIndexOf(String str,int fromIndex)
//判断是否以指定字符串开头
public boolean startsWith(String prefix)
//从指定位置开始判断是否以指定字符串开头
public boolean startsWith(String prefix, int toffset)
//判断是否以指定字符串结尾
public boolean endsWith(String suffix)
看代码:
String str = "hello word! java";
System.out.println(str.contains("llo")); // true
System.out.println(str.indexOf("llo")); // 2
System.out.println(str.indexOf("llo",3));// -1
System.out.println(str.lastIndexOf("l")); //3
System.out.println(str.lastIndexOf("o",6)); //4
System.out.println(str.startsWith("hel")); //true
System.out.println(str.startsWith("hello",3)); //false
System.out.println(str.endsWith("java")); //true
5 字符串替换操作
//替换所有的指定内容,regex -> 替换前的, replacement -> 替换后的
public String replaceAll(String regex, String replacement)
//替换首个内容
public String replaceFirst(String regex, String replacement)
注意:String类的所有针对字符串的操作方法都不会修改原字符串,而是产生一个新的字符串!!! 因为字符串的不可变性
看代码:
String str = "hello word";
System.out.println(str.replaceAll("o", "~"));
System.out.println(str.replaceFirst("l", "~"));
输出:
hell~ w~rd
he~lo word
6 字符串的拆分操作
//将字符串全部拆分
public String[] split(String regex)
若字符串中没有指定拆分的子字符串,拆分后仍然得到原字符串
//将字符串部分拆分,该数组长度就是limit极限
public String[] split(Stirng regex, int limit)
看代码:
String str = "hello word!!! hello java!!!";
String[] str1 = str.split(" ");
String[] str2 = str.split(" ",2);
String[] str3 = str.split(" ",5);//极限为4,输出会分割为 4 个,不会报错
System.out.println(Arrays.toString(str1));
System.out.println(Arrays.toString(str2));
System.out.println(Arrays.toString(str3));
输出:
[hello, word!!!, hello, java!!!]
[hello, word!!! hello java!!!]
[hello, word!!!, hello, java!!!]
再看这个代码:
String str = "198.168.100.1";
String[] str1 = str.split(".");
System.out.println(Arrays.toString(str1));
此时输出:
[]
这是因为你的分割字符是一个特殊字符,需要进行转义处理“\\”.
String str = "198.168.100.1";
String[] str1 = str.split("\\.");
System.out.println(Arrays.toString(str1));
此时输出就是正确的
[198, 168, 100, 1]
7 字符串的截取处理
//从指定索引截取到结尾
public String substring(int beginIndex)
//截取部分内容
public String substring(int beginIndex, int endIndex)
范围: [start,end),左闭右开
看代码:
String str = "hello java";
String str1 = str.substring(3);
String str2 = str.substring(6,8);
System.out.println(str1);
System.out.println(str2);
输出:
lo java
ja
8 其他常用方法
//去掉字符串中的左右开格,保留中间空格
public String trim()
//字符串转大写
public String toUpperCase()
//字符串转小写
public String toLowerCase()
//字符串入池操作
public native String intern()
//字符串连接,等同于 “+”
public String concat(String str)
//取得字符串长度
public int length()
//判断是否为空字符串,但不是null,而是长度为0
public boolean isEmpty()
String str = null;
此时str.isEmpty(); 方法就不能使用了
写一个方法判断传入的字符串为空(要么是null,要么长度为0)
public static boolean isNullEmpty(String str){
// if(str == null || str.isEmpty()){
// return true;
// }
// return false;
return str == null || str.isEmpty();
}
写一个方法将字符串的首字母大写处理
public static String firstUpper(String str){
//若字符串为 null 或长度为0 返回null
if(str == null || str.isEmpty()){
return null;
}
//若字符串长度为1 ,直接进行字符串转大写操作
if(str.length() == 1){
return str.toUpperCase();
}
return str.substring(0,1).toUpperCase() + str.substring(1);
}
一定要记住,所有针对String的操作均无法修改原字符串的内容,都是产生了新的字符串做处理。
要是对大家有所帮助的话,请帮我点个赞吧。