1 . String类的理解(以JDK8为例说明)
1.1 类的声明 public final class String implements java.io.Serializable, Comparable<String>, CharSequence > final:String是不可被继承的 任命类都不可以去继承String > Serializable:可序列化的接口。凡是实现此接口的类的对象就可以通过网络或本地流进行数据的传输。 > Comparable:凡是实现此接口的类,其对象都可以比较大小。 1.2 内部声明的属性: jdk8中: private final char value[]; //存储字符串数据的容器 > final : 指明此value数组一旦初始化,其地址就不可变。 jdk9开始:为了节省内存空间,做了优化 private final byte[] value; //存储字符串数据的容器。
2. 字符串常量的存储位置
> 字符串常量都存储在字符串常量池(StringTable)中
> 字符串常量池不允许存放两个相同的字符串常量。
> 字符串常量池,在不同的jdk版本中,存放位置不同。
jdk7之前:字符串常量池存放在方法区
jdk7及之后:字符串常量池存放在堆空间。
3. String的不可变性的理解
① 当对字符串变量重新赋值时,需要重新指定一个字符串常量的位置进行赋值,不能在原有的位置修改 ② 当对现有的字符串进行拼接操作时,需要重新开辟空间保存拼接以后的字符串,不能在原有的位置修改 ③ 当调用字符串的replace()替换现有的某个字符时,需要重新开辟空间保存修改以后的字符串,不能在原有的位置修改
代码
package chapter11_api_teacher.src.com.atguigu01.string; import org.junit.Test; /** * ClassName: StringDemo * Description: * * @Author 尚硅谷-宋红康 * @Create 15:31 * @Version 1.0 */ public class StringDemo { @Test public void test1(){ String s1 = "hello"; //字面量的定义方式 String s2 = "hello"; System.out.println(s1 == s2);//true } /* * String的不可变性 * ① 当对字符串变量重新赋值时,需要重新指定一个字符串常量的位置进行赋值,不能在原有的位置修改 * ② 当对现有的字符串进行拼接操作时,需要重新开辟空间保存拼接以后的字符串,不能在原有的位置修改 * ③ 当调用字符串的replace()替换现有的某个字符时,需要重新开辟空间保存修改以后的字符串,不能在原有的位置修改 * */ @Test public void test2(){ String s1 = "hello"; String s2 = "hello"; s2 = "hi"; System.out.println(s1); //hello } @Test public void test3(){ String s1 = "hello"; String s2 = "hello"; s2 += "world"; System.out.println(s1); //hello System.out.println(s2);//helloworld } @Test public void test4(){ String s1 = "hello"; String s2 = "hello"; String s3 = s2.replace('l', 'w'); System.out.println(s1);//hello System.out.println(s2);//hello System.out.println(s3);//hewwo } }
4. String实例化(创建对象)的两种方式
第1种方式:String s1 = "hello"; //字面量的方式 第2种方式:String s2 = new String("hello"); //New方式
【面试题】 String s2 = new String("hello");在内存中创建了几个对象? 两个! 一个是堆空间中new的对象。另一个是在字符串常量池中生成的字面量。
5. String的连接操作:+
情况1:常量 + 常量: 结果仍然存储在字符串常量池中,返回此字面量的地址。注:此时的常量可能是字面量,也可能是final修饰的常量 情况2:常量 + 变量 或 变量 + 变量 :都会通过new的方式创建一个新的字符串,返回堆空间中此字符串对象的地址 情况3:调用字符串的intern():返回的是字符串常量池中字面量的地址 (了解)情况4:concat(xxx):不管是常量调用此方法,还是变量调用,同样不管参数是常量还是变量,总之,调用完concat()方法 都返回一个新new的对象。
package chapter11_api_teacher.src.com.atguigu01.string; import org.junit.Test; /** * ClassName: StringDemo1 * Description: * * @Author 尚硅谷-宋红康 * @Create 16:11 * @Version 1.0 */ public class StringDemo1 { @Test public void test1(){ String s1 = "hello"; String s2 = "hello"; String s3 = new String("hello"); String s4 = new String("hello"); System.out.println(s1 == s2);//true System.out.println(s1 == s3);//false System.out.println(s1 == s4);//false System.out.println(s3 == s4);//false System.out.println(s1.equals(s2));//true } /* * String s = new String("hello");的内存解析? 或: * * String s = new String("hello");在内存中创建了几个对象? * * */ @Test public void test2(){ Person p1 = new Person(); Person p2 = new Person(); p1.name = "Tom"; p2.name = "Tom"; p1.name = "Jerry"; System.out.println(p2.name);//Tom } /* * 测试String的连接符:+ * */ @Test public void test3(){ String s1 = "hello"; String s2 = "world"; String s3 = "helloworld"; String s4 = "hello" + "world"; String s5 = s1 + "world"; //通过查看字节码文件发现调用了StringBuilder的toString()---> new String() String s6 = "hello" + 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(); String s8 = s5.intern(); //intern():返回的是字符串常量池中字面量的地址 System.out.println(s3 == s8);//true } @Test public void test4(){ final String s1 = "hello"; final String s2 = "world"; String s3 = "helloworld"; String s4 = "hello" + "world"; String s5 = s1 + "world"; String s6 = "hello" + s2; String s7 = s1 + s2; System.out.println(s3 == s5);//true System.out.println(s3 == s6);//true System.out.println(s3 == s7);//true } @Test public void test5(){ String s1 = "hello"; String s2 = "world"; String s3 = s1.concat(s2); String s4 = "hello".concat("world"); String s5 = s1.concat("world"); System.out.println(s3 == s4);//false System.out.println(s3 == s5);//false System.out.println(s4 == s5);//false } } class Person{ String name; }
6. String的构造器和常用方法
6.1 构造器
* `public String() ` :初始化新创建的 String对象,以使其表示空字符序列。 * `public String(String original)`: 初始化一个新创建的 `String` 对象,使其表示一个与参数相同的字符序列;换句话说,新创建的字符串是该参数字符串的副本。 * `public String(char[] value) ` :通过当前参数中的字符数组来构造新的String。 * `public String(char[] value,int offset, int count) ` :通过字符数组的一部分来构造新的String。 * `public String(byte[] bytes) ` :通过使用平台的**默认字符集**解码当前参数中的字节数组来构造新的String。 * `public String(byte[] bytes,String charsetName) ` :通过使用指定的字符集解码当前参数中的字节数组来构造新的String。
package chapter11_api_teacher.src.com.atguigu01.string; import org.junit.Test; import java.io.UnsupportedEncodingException; /** * ClassName: StringMethodTest * Description: * * @Author 尚硅谷-宋红康 * @Create 16:49 * @Version 1.0 */ public class StringMethodTest { /* * String构造器的使用 * */ @Test public void test1(){ String s1 = new String(); String s2 = new String(""); String s3 = new String(new char[]{'a','b','c'}); System.out.println(s3); } /* * String与常见的其它结构之间的转换 * * 1. String与基本数据类型、包装类之间的转换(复习) * * 2. String与char[]之间的转换 * * 3. String与byte[]之间的转换(难度) * */ @Test public void test2(){ int num = 10; //基本数据类型 ---> String //方式1: String s1 = num + ""; //方式2: String s2 = String.valueOf(num); //String --> 基本数据类型:调用包装类的parseXxx(String str) String s3 = "123"; int i1 = Integer.parseInt(s3); } //String与char[]之间的转换 @Test public void test3(){ String str = "hello"; //String -->char[]:调用String的toCharArray() char[] arr = str.toCharArray(); for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } //char[] ---> String:调用String的构造器 String str1 = new String(arr); System.out.println(str1);//hello } //String与byte[]之间的转换(难度) /* * 在utf-8字符集中,一个汉字占用3个字节,一个字母占用1个字节。 * 在gbk字符集中,一个汉字占用2个字节,一个字母占用1个字节。 * * utf-8或gbk都向下兼容了ascii码。 * * 编码与解码: * 编码:String(看得懂) ---> 字节或字节数组(看不懂的) * 解码:字节或字节数组(看不懂的) ----> String(看得懂) * 要求:解码时使用的字符集必须与编码时使用的字符集一致!不一致,就会乱码。 * * */ @Test public void test4() throws UnsupportedEncodingException { String str = new String("abc中国"); //String -->byte[]:调用String的getBytes() byte[] arr = str.getBytes(); //使用默认的字符集:utf-8 for (int i = 0;i < arr.length;i++){ System.out.println(arr[i]); } System.out.println(); //getBytes(String charsetName):使用指定的字符集 byte[] arr1 = str.getBytes("gbk"); for (int i = 0; i < arr1.length; i++) { System.out.println(arr1[i]); } //byte[] ---> String: String str1 = new String(arr); //使用默认的字符集:utf-8 System.out.println(str1); String str2 = new String(arr,"utf-8");//显式的指明解码的字符集:utf-8 System.out.println(str2); //乱码 // String str3 = new String(arr,"gbk");//显式的指明解码的字符集:gbk // System.out.println(str3); String str4 = new String(arr1,"gbk"); System.out.println(str4); } }
6.2 系列1:常用方法
(1)boolean isEmpty():字符串是否为空
(2)int length():返回字符串的长度
(3)String concat(xx):拼接
(4)boolean equals(Object obj):比较字符串是否相等,区分大小写
(5)boolean equalsIgnoreCase(Object obj):比较字符串是否相等,不区分大小写
(6)int compareTo(String other):比较字符串大小,区分大小写,按照Unicode编码值比较大小
(7)int compareToIgnoreCase(String other):比较字符串大小,不区分大小写
(8)String toLowerCase():将字符串中大写字母转为小写
(9)String toUpperCase():将字符串中小写字母转为大写
(10)String trim():去掉字符串前后空白符
(11)public String intern():结果在常量池中共享
@Test public void test1(){ String s1 = ""; String s2 = new String(); String s3 = new String(""); System.out.println(s1.isEmpty()); System.out.println(s2.isEmpty()); System.out.println(s3.isEmpty()); String s4 = null; System.out.println(s4.isEmpty());//报空指针异常 String s5 = "hello"; System.out.println(s5.length());//5 } @Test public void test2(){ String s1 = "hello"; String s2 = "HellO"; System.out.println(s1.equals(s2)); System.out.println(s1.equalsIgnoreCase(s2)); String s3 = "abcd"; String s4 = "adef"; System.out.println(s3.compareTo(s4)); String s5 = "abcd"; String s6 = "aBcd"; System.out.println(s5.compareTo(s6)); System.out.println(s5.compareToIgnoreCase(s6)); String s7 = "张ab"; String s8 = "李cd"; System.out.println(s7.compareTo(s8)); String s9 = " he llo "; System.out.println("****" + s9.trim() + "*****"); }
6.3 系列2:查找
(11)boolean contains(xx):是否包含xx
(12)int indexOf(xx):从前往后找当前字符串中xx,即如果有返回第一次出现的下标,要是没有返回-1
(13)int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始
(14)int lastIndexOf(xx):从后往前找当前字符串中xx,即如果有返回最后一次出现的下标,要是没有返回-1
(15)int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索。
@Test public void test3(){ String s1 = "教育尚硅谷教育"; System.out.println(s1.contains("硅谷")); System.out.println(s1.indexOf("教育")); System.out.println(s1.indexOf("教育",1)); System.out.println(s1.lastIndexOf("教育")); System.out.println(s1.lastIndexOf("教育",4)); }
6.4 系列3:字符串截取
(16)String substring(int beginIndex) :返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。
(17)String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。
@Test public void test4(){ String s1 = "教育尚硅谷教育"; System.out.println(s1.substring(2)); System.out.println(s1.substring(2,5));//[2,5) }
6.5 系列4:和字符/字符数组相关
(18)char charAt(index):返回[index]位置的字符
(19)char[] toCharArray(): 将此字符串转换为一个新的字符数组返回
(20)static String valueOf(char[] data) :返回指定数组中表示该字符序列的 String
(21)static String valueOf(char[] data, int offset, int count) : 返回指定数组中表示该字符序列的 String
(22)static String copyValueOf(char[] data): 返回指定数组中表示该字符序列的 String (23)static String copyValueOf(char[] data, int offset, int count):返回指定数组中表示该字符序列的 String
6.6 系列5:开头与结尾
24)boolean startsWith(xx):测试此字符串是否以指定的前缀开始
(25)boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始
(26)boolean endsWith(xx):测试此字符串是否以指定的后缀结束
@Test public void test5(){ String s1 = "教育尚硅谷教育"; System.out.println(s1.charAt(2)); String s2 = String.valueOf(new char[]{'a', 'b', 'c'}); String s3 = String.copyValueOf(new char[]{'a', 'b', 'c'}); System.out.println(s2); System.out.println(s3); System.out.println(s2 == s3); System.out.println(s1.startsWith("教育a")); System.out.println(s1.startsWith("教育",5)); }
6.7 系列6:替换
(27)String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。 不支持正则。
(28)String replace(CharSequence target, CharSequence replacement):使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。
(29)String replaceAll(String regex, String replacement):使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
(30)String replaceFirst(String regex, String replacement):使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
@Test public void test6(){ String s1 = "hello"; String s2 = s1.replace('l', 'w'); System.out.println(s1); System.out.println(s2); String s3 = s1.replace("ll", "wwww"); System.out.println(s3); }