String:字符串:
- String 类声明了final,不可被继承
- String 实现了Serializable接口,可以被序列化
- String 实现了Comparable接口,可以比较大小
- String 内部定义了final char[],底层数据结构是char型数组,用于存储字符串
- String 代表不可变的字符序列。即字符串的不可变性。
- 对字符串重新赋值时,需要在方法区常量池中重新开辟内存空间赋值,不能对原有的值赋值。
- 对字符串进行拼接操作时,也需要在方法区常量池中重新开辟内存空间赋值,不能对原有的值赋值。
- 使用String的replace方法进行字符或字符串替换时,也需要在方法区常量池中重新开辟内存空间赋值,不能对原有的值赋值。
- 常量池中不会存储相同的值。
String 创建方式:
1. 字面量方式:字符串值是存储在方法区的常量池中。
String str1 = "aaa";
String str2 = "aaa";
2. 实例化方式:此时变量str3和str4保存的是在堆空间的地址值。
String str3 = new String("aaa");
String str4 = new String("aaa");
常见的面试题:
System.out.println(str1 == str2);// true 因为str1和str2都是通过字面量方式创建的,所以值是存储在方法区中的常量池中,而常量池中不会存储重复的值,所以str1和sstr2指向的都是同一个。所以是true。
System.out.println(str1 == str3);// false 因为str1是在常量池中,而str3是实例化的,存储在堆空间中。所以是false
System.out.println(str3 == str4);// false 因为str3和str4都是实例化的,所以堆空间的地址值不同,所以是false
class Person{
public String name;
public int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
Person tom1 = new Person("Tom", 21);
Person tom2 = new Person("Tom", 21);
System.out.println(tom1.name == tom2.name); // true 因为Person 中的name的值是通过字面量方式创建的,值 “Tom” 是存储在方法区中的常量池中的。对象tom1的name和对象tom2的name的地址值都是同一个。所以为true
System.out.println(tom1.name.equals(tom2.name)); // true 有两点原因:第一点就是上面说的对象tom1的name和对象tom2的name的地址值都是同一个。第二点就是String 类重写了equals方法,比较的是两个值是否相等。所以还是true.
tom1.name = "Juck";
System.out.println(tom2.name); // "Tom" 因为会在常量池中新开辟一个空间用来存储“Juck”,然后将新的地址值赋给tom1.name,所以tom2.name 还是“Tom”。这就是字符串的不可变性。
String aaa = new String("aaa");这段代码会在内存中创建几个对象?
会创建两个。new String()会在堆内存中创建一个,另一个是在常量池中,创建的是值“aaa”。
String str1 = "Hello";
String str3 = "HelloWorld";
String str4 = str1 + "World";
String str5 = "Hello" + "World";
System.out.println(str3 == str4); // false 变量参与拼接的结果是存储在堆内存中,相当于每次都新实例化了,地址值不同。所以为false。
System.out.println(str3 == str5); // true 因为常量与常量拼接的结果还是存储在常量池中,而常量池中不会有相同的值。str3与str5的地址值是相同的,所以为true。
String str6 = str4.intern();
System.out.println(str3 == str6); // true 当调用 intern()方法时,会将返回值存储到常量池中,如果常量池中已经存在,则将已有的地址值赋给变量。
字符串常用方法:
String str = "Hello World ";
str.length(); // 获取字符串长度,即char[]的长度。
str.charAt(1); // 返回指定索引处的字符
if(str.isEmpty()) // 判断字符串是否为空,即判断char[]的长度是否为0
System.out.println("true");
else
System.out.println("false");
str.toLowerCase(); // 将字符串转小写
str.toUpperCase(); // 将字符串转大写
str.trim(); // 去除首位空格
str.equals(" hello World "); // 比较字符串内容是否相等
str.equalsIgnoreCase(" hello World "); // 忽略大小写的情况下比较字符串内容是否相等
str.concat("bbb"); // 将指定字符串拼接在后面
str.compareTo("aac"); // 比较字符串大写,实际是从左往右将每个字符对应的二进制数子进行比较,如果返回值大于0,则大,反之则小。
str.substring(2); // 从指定索引开始截取字符串
str.substring(2, 4); // 从指定索引开始截取到指定结束索引。注意:左闭右开 [2, 4)。
str.contains("ll"); // 判断字符串中是否包含指定字符串
str.indexOf("ll"); // 返回指定字符或字符串在字符串中第一次出现的索引位置,如果返回值是-1,则代表没有。