String类
一、String类的理解和创建对象
-
结构剖析
-
String 对象用于保存字符串,也就是一组字符序列;
-
字符串常量对象是用双引号括起来的字符序列。例如:jack"字符串常量;
-
字符串的字符使用Unicode字符编码,一个字符(无论汉字还是字母)占两个字节;
-
String类 有很多构造器,构造器的重载
常用的有:
String s1 = new String();
String s2 = new String(String original);
String s3 = new String(char[] a);
String s4 = new String(char[] a,int startIndex,int count);
String s5 = new String(byte[] b); -
String类 实现了接口 Serializable【String 可串行化:可以在网络传输】
String类 实现了接口 Comparable【String类对象可以比较大小】 -
String 是 final类,不能被其他类继承;
-
String类 有属性 private final char value[]; 用于存放字符串内容,即String本质是字符数组;
-
一定要注意:value 是一个 final类型,不可以修改:即value数组对象不可以指向新的地址,但是单个字符内容是可以变化的;
-
-
创建剖析
创建String对象的两种方式:
方式一:直接赋值 String s = "hspedu";
方式二:调用构造器 String s = new String("hspedu");
- 方式一:先从常量池查看是否有 "hsp" 数据空间,如果有直接指向;如果没有则重新创建,然后指向。s 最终指向的是常量池的空间地址。
- 方式二:先在堆中创建空间,里面维护了value属性,指向常量池的 "hsp" 空间;如果常量池没有"hsp",重新创建,如果有,直接通过value指向,最终指向的是堆中的空间地址。
![jvm的内存:String的内存布局]
二、字符串的特性
-
String 是一个final类,代表不可改变的字符序列。
-
字符串是不可变的。一个字符串对象一旦被分配,其内容是不可变的。
//以下语句创建了几个对象 String s1 = "hello"; s1 = "haha"; //创建了2个对象
-
编译器会判断创建的常量池对象,是否有引用指向。
//以下语句创建了几个对象 String a = "hello" + "abc"; //只有一个对象 //String a = "hello" + "abc"; => //优化,等价于 String a = "helloabc"; //1. 编译器不傻,做一个优化,判断创建的常量池对象,是否有引用指向。 //2. String a = "hello" + "abc"; => String a = "helloabc";
-
String c = a + b; 变量相加,创建过程;
public class StringExercise { public static void main(String[] args) { //判断创建了几个对象 String a = "hello"; String b = "abc"; // c 的创建过程: //1. 先创建了一个StringBuilder sb = StringBuilder(); //2. 执行 sb.append("hello"); //3. sb.append("abc"); //4. String c = sb.toString(); //这里的toString()方法源码: /* public String toString() { return new String(value, 0, count);//这里new了一个String对象 } */ //最后其实是 c 指向堆中对象(String)value[] -> 池中 "helloabc" String c = a + b; String d = "helloabc"; System.out.println(c == d); } }
底层是StringBuilder sb = StringBuilder();
sb.append("hello");
sb.append("abc");
sb是在堆中,并且append是在原来字符串的基础上追加的。
重要规则:String c1 = "ab" + "cd"; 常量相加,看的是池。
String c1 = a + b; 变量相加,是在堆中。
三、 String类的常用方法
-
intern()方法:
如果池中已经包含一个等于此String对象的字符串(用equals(Object)方法确定),则返回池中的字符串。否则将此 String对象添加到池中,并返回此String对象的引用。intern()方法最终返回的是常量池的地址(对象)。
-
equals()方法: 比较内容是否相同,区分大小写;
-
equalsIgnoreCase()方法: 忽略大小写判断内容是否相等;
-
length()方法: 获取字符的个数,字符串的长度;
-
indexOf()方法: 获取字符/子串在字符串对象中第一次出现的索引;
-
lastIndexOf()方法: 获取字符/子串在字符串对象中最后一次出现的索引;
-
substring()方法: 截取指定范围内的子串;
public class StringMethod01 {
public static void main(String[] args) {
//1. equals 比较内容是否相同,区分大小写
String str1 = "hello";
String str2 = "Hello";
System.out.println(str1.equals(str2));//false
//2. equalsIgnoreCase 忽略大小写判断内容是否相等
String username = "johN";
if("john".equalsIgnoreCase(username)){
System.out.println("Success!");
}else{
System.out.println("Failure!");
}
//3. length 获取字符的个数,字符串的长度
System.out.println("韩顺平".length());
//4. indexOf 获取字符/子串在字符串对象中第一次出现的索引
String s1 = "wer@terwe@g";
int index = s1.indexOf('@');
int index2 = s1.indexOf("we");
System.out.println(index);//3
System.out.println(index2);//0
//5. lastIndexOf 获取字符/子串在字符串对象中最后一次出现的索引
s1 = "wer@terwe@g@";
index = s1.lastIndexOf('@');
index2 = s1.lastIndexOf("we");
System.out.println(index);//11
System.out.println(index2);//7
//6. substring 截取指定范围内的子串
String name = "hello,张三";
//下面name.substring(6) 从索引6开始截取后面所有的内容
System.out.println(name.substring(6));//截取后面的字符
//name.substring(0,5) 表示从索引0开始截取,截取到索引5但不包括索引5,[0,5)
System.out.println(name.substring(2,5));//ll0
}
}
- toUpperCase()方法: 转换成大写;
- toLowerCase()方法: 转换成小写;
- concat()方法: 拼接字符串;
- replace()方法: 替换字符串中的字符;
- split()方法: 分割字符串,对于某些分割字符我们需要转义比如 | , \ 等等;
- toCharArray()方法: 转换成字符数组;
- compareTo()方法: 比较两个字符串的大小;
- format()方法: 格式字符串;
public class StringMethod02 {
public static void main(String[] args) {
//1. toUpperCase 转换成大写
String s = "heLLo";
System.out.println(s.toUpperCase());
//2. toLowerCase 转换成小写
System.out.println(s.toLowerCase());
//3. concat 拼接字符串
String s1 = "宝玉";
s1 = s1.concat("林黛玉").concat("薛宝钗").concat("together");
System.out.println(s1);
//4. replace 替换字符串中的字符
//s1.replace() 方法执行后返回的结果,才是替换后的
//注意对 s1 即原来的字符串无任何影响
String s2 = s1.replace("林黛玉","jack");
System.out.println(s2);
System.out.println(s1);
//5. split 分割字符串,对于某些分割字符我们需要转义比如 |, \\ 等等
String poem = "锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦";
//以 "," 为标准对 poem 进行分割返回一个数组
String[] split = poem.split(",");
System.out.println("======分割后内容======");
for (int i = 0; i < split.length; i++) {
System.out.println(split[i]);
}
poem = "E:\\aaa\\bbb";
String[] split2 = poem.split("\\\\");
System.out.println("======分割后内容======");
for (int i = 0; i < split2.length; i++) {
System.out.println(split2[i]);
}
//6. toCharArray 转换成字符数组
s = "happy";
char[] chs = s.toCharArray();
for (int i = 0; i < chs.length; i++) {
System.out.println(chs[i]);
}
//7. compareTo 比较两个字符串的大小
//如果前者大,返回正数,后者大,则返回负数,如果相等,返回0
//源码:
/*
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
*/
//(1) 如果 长度相同,并且每个字符也相同,就返回0
//(2) 如果长度相同或不同,但是在进行比较时,字符有不同,可以区分大小就返回字符间的差值
//(3) 如果长度不同,前面部分相同,就返回 str1.len - str2.len;
String a = "jcck";
String b = "jack";
System.out.println(a.compareTo(b));//返回值是 'c' - 'a'
//8. format 格式字符串
/*
占位符有:
%S 字符串 %c 字符 %d 整型 %.2f 浮点型
*/
String name = "john";
int age = 10;
double score = 98.3 / 3;
char gender = '男';
//将所有的信息都拼接在一个字符串
String info = "我的名字是" + name + "年龄是" + age +
"成绩是" + score + "性别是" + gender;
System.out.println(info);
//1. %s %c %d %.2f 称为占位符
//2. 这些占位符由后面的变量来替换
//3. %s 表示后面由字符串来替换
//4. %d 是整数来替换
//5. %.2f 表示使用小数来替换,替换后,只会保留小数点两位
//6. %c 使用char 类型来替换
String formatStr = "我的名字是%s,年龄是%d,成绩是%.2f,性别是%c";
String info2 = String.format(formatStr,name,age,score,gender);
System.out.println(info2);
}
}