String类
文章目录

1、String的几点说明
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
}
//源码分析:
String str = new String();//char[] value = new char[0];
String str1 = new String("abc");//char[] value = new char[]{'a','b','c'};
- String 声明为 final 的,不可被继承
- String 实现了 Serialization 接口:表示字符串是支持序列化的
- String 实现了 Comparable 接口:表示String 可以比较大小
- String 内部定义了final char[] value 用于存储字符串数组,final不可变的字符型数组,所以重新赋值时,要新造一个
2、实例化和内存解析
@Test
//1. 实例化方式和内存解析:
public void test1(){
//方式一:构造器,字符串对象在堆中,字符串值在常量池中,变量指向堆中对象的地址
String s1 = new String("JavaEE");
char[] c1 = new char[]{'J', 'a', 'v', 'a', 'E', 'E'};
String s2 = new String(c1);
String s3 = new String(c1, 3, 3);
byte[] b1 = new byte[]{97, 98, 65, 66};
String s4 = new String(b1);
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
System.out.println(s4);
//方式二:字面量,字符串值在常量池中,变量在栈中,指向常量池中的字符串地址
//字符串常量池中是不会存储相同内容的字符串的
String s5 = "JavaEE";
String s6 = "JavaEE";
System.out.println(s5 == s6);//true。s4、s5指向同一个地址,常量池中字符串的地址
System.out.println(s1 == s5);//false
System.out.println(s1 == s2);//false
System.out.println(s2 == s5);//false
}
/*
* String:代表不可变的字符序列,不可以在原有的上面对value重新赋值
*
* 1. 当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。
* 2. 当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
* 3. 当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
*/
@Test
public void test2(){
// 1.当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。
String s1 = "abc"; //字面量
String s2 = "abc";
System.out.println(s1 == s2);//true
s1 = "hello";
System.out.println(s1 == s2);//false
}
@Test
public void test3(){
// 2. 当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);//true
s1 += "def"; //也是新造的
System.out.println(s1 == s2);//false
}
@Test
public void test4(){
// 3. 当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);//true
s1 = s1.replace('a', 'm'); //也是新造的
System.out.println(s1 == s2);//false
}
/*
* 结论:
* 1.常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。
* 2.只要其中有一个是变量,结果就在堆中。
* 3.如果拼接的结果调用intern()方法,返回值就在常量池中
*/
@Test
public void test5(){
String s1 = "javaEE";
String s2 = "hadoop";
String s3 = "javaEEhadoop";
String s4 = "javaEE" + "hadoop";//常量池中
String s5 = s1 + "hadoop";//堆中
String s6 = "javaEE" + s2;//堆中
String s7 = s1 + s2;//堆中
System.out.println(s3 == s4);//true
System.out.println(s3 == s5);//false
System.out.println(s3 == s6);//false
System.out.println(s3 == s7);//false
System.out.println(s5 == s6);//false
System.out.println(s5 == s7);//false
System.out.println(s6 == s7);//false
String s8 = s6.intern();//返回值得到的s8使用的常量值中已经存在的“javaEEhadoop”
System.out.println(s3 == s8);//true
}
3、常用方法
/*
* 1. int length() :返回字符串的长度: return value.length
* 2. char charAt(int index) : 返回某索引处的字符return value[index]
* 3. boolean isEmpty() :判断是否是空字符串:return value.length == 0
*
* 4. String toLowerCase() :使用默认语言环境,将 String 中的所有字符转换为小写
* 5. String toUpperCase() :使用默认语言环境,将 String 中的所有字符转换为大写
*
* 6. String trim() :返回字符串的副本,忽略前导空白和尾部空白
*/
@Test
public void test1() {
String s1 = "HelloWorld";
System.out.println(s1.length());//10
System.out.println(s1.charAt(0));//H
System.out.println(s1.charAt(9));//d
//s1 = "";
System.out.println(s1.isEmpty());//false
String s2 = s1.toLowerCase();
System.out.println(s1);//s1不可变的,仍然为原来的字符串 HelloWorld
System.out.println(s2);//改成小写以后的字符串 helloworld
String s3 = " he llo world ";
String s4 = s3.trim();
System.out.println("-----" + s3 + "-----");//----- he llo world -----
System.out.println("-----" + s4 + "-----");//-----he llo world-----
}
/*
* 7. boolean equals(Object obj) :比较字符串的内容是否相同
* 8. boolean equalsIgnoreCase(String anotherString) :与equals方法类似,忽略大小写
* 9. String concat(String str) :将指定字符串连接到此字符串的结尾。 等价于用“+”
*
* 10.int compareTo(String anotherString) :比较两个字符串的大小
*
* 11.String substring(int beginIndex) :返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。
* 12.String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。
*/
@Test
public void test2() {
String s1 = "HelloWorld";
String s2 = "helloworld";
System.out.println(s1.equals(s2));//false
System.out.println(s1.equalsIgnoreCase(s2));//true
String s3 = "abc";
String s4 = s3.concat("def");
System.out.println(s4);//abcdef
String s5 = "abc";
String s6 = new String("abc");
String s51 = "abc";
String s61 = new String("abd");
System.out.println(s5.compareTo(s6));//0。涉及到字符串排序,涉及到字符串排序,equals返回的0,序低返回负数,序高返回正数
System.out.println(s51.compareTo(s61));//-1
String s7 = "北京尚硅谷教育";
String s8 = s7.substring(2);
System.out.println(s7);//String不可变的,仍然为原来的字符串 "北京尚硅谷教育"
System.out.println(s8);//"尚硅谷教育"
String s9 = s7.substring(2, 5);
System.out.println(s9);//"尚硅谷"
}
/*
* 13.boolean endsWith(String suffix) :测试此字符串是否以指定的后缀结束
* 14.boolean startsWith(String prefix) :测试此字符串是否以指定的前缀开始
* 15.boolean startsWith(String prefix, int toffset) :测试此字符串从指定索引开始的子字符串是否以指定前缀开始
*
* 16.boolean contains(CharSequence s) :当且仅当此字符串包含指定的 char 值序列时,返回 true
*
* 17.int indexOf(String str) :返回指定子字符串在此字符串中第一次出现处的索引
* 18.int indexOf(String str, int fromIndex) :返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始
* 19.int lastIndexOf(String str) :返回指定子字符串在此字符串中最右边出现处的索引
* 20.int lastIndexOf(String str, int fromIndex) :返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索
* 注:indexOf和lastIndexOf方法如果未找到都是返回-1
*/
@Test
public void test3(){
String str1 = "hellowworld";
boolean b1 = str1.endsWith("rld");
System.out.println(b1);//true
boolean b2 = str1.startsWith("He");
System.out.println(b2);//false
boolean b3 = str1.startsWith("ll",2);
System.out.println(b3);//true
String str2 = "wor";
System.out.println(str1.contains(str2));//true
System.out.println(str1.indexOf("lo"));//3
System.out.println(str1.indexOf("lol"));//-1
System.out.println(str1.indexOf("lo",5));//-1
String str3 = "hellorwoxld";
System.out.println(str3.lastIndexOf("or"));//7
}
/*
* 21.String replace(char oldChar, char newChar) :返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
* 22.String replace(CharSequence target, CharSequence replacement) :使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。
* 23.String replaceAll(String regex, String replacement) : 使用给定的replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
* 24.String replaceFirst(String regex, String replacement) : 使用给定的replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
*
* 25.boolean matches(String regex) :告知此字符串是否匹配给定的正则表达式
*
* 26.String[] split(String regex) :根据给定正则表达式的匹配拆分此字符串。
* 27.String[] split(String regex, int limit) :根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中
*/
@Test
public void test4(){
String str1 = "北京尚硅谷教育北京";
String str2 = str1.replace('北', '东');
System.out.println(str1);//北京尚硅谷教育北京
System.out.println(str2);//东京尚硅谷教育东京
String str3 = str1.replace("北京", "上海");//上海尚硅谷教育上海
System.out.println(str3);
String str = "12hello34world5java7891mysql456";
//把字符串中的数字替换成,,如果结果中开头和结尾有,的话去掉
String string = str.replaceAll("\\d+", ",").replaceAll("^,|,$", "");
System.out.println(string);//hello,world,java,mysql
str = "12345";
//判断str字符串中是否全部有数字组成,即有1-n个数字组成
boolean matches = str.matches("\\d+");
System.out.println(matches);//true
String tel = "0571-4534289";
//判断这是否是一个杭州的固定电话
boolean result = tel.matches("0571-\\d{7,8}");
System.out.println(result);//true
str = "hello|world|java";
String[] strs = str.split("\\|");
for (int i = 0; i < strs.length; i++) {
System.out.print(strs[i] + "\t");
}//hello world java
str2 = "hello.world.java";
String[] strs2 = str2.split("\\.");
for (int i = 0; i < strs2.length; i++) {
System.out.print(strs2[i] + "\t");
}//hello world java
}
4、String与其它类型的转换
4.1 String与基本数据类型、包装类之间的转换
Integer.parseInt(String): int
String.valueOf(int): String
@Test
public void test1(){
String str1 = "123";
Integer num = Integer.parseInt(str1);
//int num = Integer.parseInt(str1);//自动拆箱:Integer --> int
System.out.println(num.getClass());//class java.lang.Integer
System.out.println(num + 1);//自动拆箱:Integer --> int
String str2 = String.valueOf(num);//"123"
System.out.println(str2.getClass());//class java.lang.String
}
4.2 String与char[] 的转换
-
调用
String
的构造器 -
调用
String
的toCharArray()
@Test
public void test2(){
char[] c1 = new char[]{'J', 'a', 'v', 'a', 'E', 'E'};
String s1 = new String(c1);
System.out.println(s1);//JavaEE
char[] charArray = str1.toCharArray();
System.out.println(Arrays.toString(charArray));//[J, a, v, a, E, E]
}
4.3 String与byte[] 的转换
-
调用
String
的构造器 -
调用
String
的getBytes()
/*
* 编码:字符串 -->字节 (看得懂 --->看不懂的二进制数据)
* 解码:编码的逆过程,字节 --> 字符串 (看不懂的二进制数据 ---> 看得懂)
* 说明:解码时,要求解码使用的字符集必须与编码时使用的字符集一致,否则会出现乱码。
*/
@Test
public void test2() throws UnsupportedEncodingException {
String str1 = "abc123中国";
byte[] bytes = str1.getBytes();//使用默认的字符集,进行编码。
System.out.println(Arrays.toString(bytes));//[97, 98, 99, 49, 50, 51, -28, -72, -83, -27, -101, -67]
byte[] gbks = str1.getBytes("gbk");//使用gbk字符集进行编码。
System.out.println(Arrays.toString(gbks));//[97, 98, 99, 49, 50, 51, -42, -48, -71, -6]
String str2 = new String(bytes);//使用默认的字符集,进行解码。
System.out.println(str2);//abc123中国
String str3 = new String(gbks);
System.out.println(str3);//出现乱码: abc123�й�。原因:编码集和解码集不
String str4 = new String(gbks, "gbk");
System.out.println(str4);//没有出现乱码: abc123中国。原因:编码集和解码集一致!
}
5、StringBuffer和StringBulider
5.1 说明
StringBuffer
和StringBulider
都继承于AbstractStringBuilder
StringBuffer
和StringBulider
非常类似,均代表可变的字符序列 , 而且提供相关功能的方法也一样- value没有final声明, value可以不断扩容
- count记录有效字符的个数
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
* value没有final声明, value可以不断扩容
*/
char[] value;
/**
* The count is the number of characters used.
* count记录有效字符的个数
*/
int count;
}
5.2 构造器
/*
* 1. StringBuffer(): 初始容量为16的字符串缓冲区
* 2. StringBuffer(int size): 构造指定容量的字符串缓冲区
* 3. StringBuffer(String str): 将内容初始化为指定字符串内容
*/
@Test
public void test1(){
StringBuffer sb = new StringBuffer();
System.out.println(sb.length());//0
sb.append('a');
sb.append("bc");
System.out.println(sb.length());//3
StringBuffer sb1 = new StringBuffer(30);
System.out.println(sb1.length());//0
StringBuffer sb2 = new StringBuffer("abcd");
System.out.println(sb2.length());//4
}
5.3 方法
/*
* StringBuffer的常用方法:
* 1. StringBuffer append(xxx):提供了很多的append()方法,用于进行字符串拼接
* 2. StringBuffer delete(int start,int end):删除指定位置的内容
* 3. StringBuffer replace(int start, int end, String str):把[start,end)位置替换为str
* 4. StringBuffer insert(int offset, xxx):在指定位置插入xxx
* 5. StringBuffer reverse() :把当前字符序列逆转
* 6. int indexOf(String str)
* 6. String substring(int start,int end):返回一个从start开始到end索引结束的左闭右开区间的子字符串
* 7. int length()
* 8. char charAt(int n)
* 9. void setCharAt(int n ,char ch)
* 总结:
* 增:append(xxx)
* 删:delete(int start,int end)
* 改:setCharAt(int n ,char ch) / replace(int start, int end, String str)
* 查:charAt(int n)
* 插:insert(int offset, xxx)
* 长度:length();
* 遍历:for() + charAt() / toString()
*/
5.4 String 和 StringBuffer / StringBuilder之间的转换
String
-->StringBuffer / StringBuilder
:调用StringBuffer / StringBuilder
的构造器 ``StringBuffer(str)StringBuffer / StringBuilder
-->String
:- 调用
StringBuffer / StringBuilder
的构造器 ``String(StringBuffer buffer) - 调用
StringBuffer / StringBuilder
的toString()
方法
- 调用
6、Sting、StringBuffer和StringBulider三者的异同
/*
* String、StringBuffer、StringBuilder三者的异同?
* 1. String:不可变的字符序列;底层使用char[]存储
* 2. StringBuffer:可变的字符序列;线程安全的,效率低;底层使用char[]存储
* 3. StringBuilder:可变的字符序列;jdk5.0新增的,线程不安全的,效率高;底层使用char[]存储
* 源码分析:
* String str = new String();//char[] value = new char[0];
* String str1 = new String("abc");//char[] value = new char[]{'a','b','c'};
* StringBuffer sb1 = new StringBuffer();//char[] value = new char[16];底层创建了一个长度是16的数组。
* System.out.println(sb1.length());//16
* sb1.append('a');//value[0] = 'a';
* sb1.append('b');//value[1] = 'b';
* StringBuffer sb2 = new StringBuffer("abc");//char[] value = new char["abc".length() + 16];
* System.out.println(sb2.length());//3
* 扩容问题:如果要添加的数据底层数组盛不下了,那就需要扩容底层的数组。
* 默认情况下,扩容为原来容量的2倍 + 2,同时将原有数组中的元素复制到新的数组中。
* String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且浪费大量有限的内存空间。
* 开发中建议使用StringBuffer(int capacity) 或 StringBuilder(int capacity)
*/
/*
对比String、StringBuffer、StringBuilder三者的效率:
从高到低排列:StringBuilder > StringBuffer > String
*/
@Test
public void test3(){
//初始设置
long startTime = 0L;
long endTime = 0L;
String text = "";
System.out.println(text.length());//0
StringBuffer buffer = new StringBuffer();
System.out.println(text.length());//0
StringBuilder builder = new StringBuilder();
//开始对比
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
buffer.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuffer的执行时间:" + (endTime - startTime));//4
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
builder.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuilder的执行时间:" + (endTime - startTime));//2
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
text = text + i;
}
endTime = System.currentTimeMillis();
System.out.println("String的执行时间:" + (endTime - startTime));//1055
}
7、Java的值传递机制以及String的不可变性
@Test
//1. 基本数据类型的值传递:将数值复制一份
public void test1(){
int num1 = 10;
int num2 = 20;
change1(num1, num2);
System.out.println("num1 = " + num1 + "\t" + "num2 = " + num2);
}
@Test
//2. 引用数据类型的值传递:地址值传递,将地址值复制一份
//只要引用对象可变,指向它并对其做出修改
public void test2(){
StringBuilder s = new StringBuilder("good");
char[] c = new char[]{ 't', 'e', 's', 't' };
change2(s, c);
System.out.println(s);
System.out.println(Arrays.toString(c));
}
@Test
//3. 引用数据类型的值传递:地址值传递,将地址值复制一份
//在change方法,地址值复制了一份到形参s中,但由于String对象的不可变性,相当于新建了一个对象,
//但方法执行完后,形参就被回收了,相当于复制的那一份就被回收了,而原来的还是指向原来的对象,没变
public void test3(){
String s1 = new String("ab");
String s2 = "gh";
change3(s1);
System.out.println(s1);
change4(s1);
System.out.println(s1);
change3(s2);
System.out.println(s2);
change4(s2);
System.out.println(s2);
}
public void change1(int a, int b){
int temp = a;
a = b;
b = temp;
System.out.println("a = " + a + "\t" + "b = " + b);
}
public void change2(StringBuilder s, char[] c){
s.append('s');
c[0] = 'b';
}
public void change3(String s){
s = new String("cd");
}
public void change4(String s){
s = "ef";
}