底层实现
//String的底层:
private final char[] value;
String 对象的字符内容是存储在一个字符数组中的
说明:
i)private 意味着外面无法直接获取字符数组。
ii)final 意味着字符数组的引用不可改变。表示String类不可被继承。
iii)通过让 value 指向新的数组对象来实现修改 String对象,因此字符串的字符数组内容也不可变。
不可变性
String的不可变性体现:
i)当对字符串重新赋值时,需要重新指定内存区域赋值,不能已使用原有的value进行赋值。
ii)当对现有的字符串进行拼接操作时,也要重新指定内存区域赋值,不能使用原有的value进行赋值。
iii)当调用String的replace()方法修改指定字符或字符串时,也需要指定内存区域赋值,不能使用原有的value进行赋值。
String的实例化方式
方式一:字面量方式
通过字面量定义的方式(区别new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
字符串常量池是不会存储相同内容的字符串的。
//String是引用数据类型
//通过字面量定义的方式:此时的s1和s2的数据javaEE声明在方法区中的字符串常量池中。常量池中只有一个javaEE,所以s1和s2指向同一个地址。
String s1 = "javaEE";
String s2 = "javaEE";
System.out.println(s1 == s2);//true
方式二:new + 构造器
通过new + 构造器的方式,数据是在堆空间中开辟空间以后对应的地址值。地址值对应的内容还是在字符串常量池中。
//通过new + 构造器的方式:此时的s3和s4保存的地址值,数据是在堆空间中开辟空间以后对应的地址值。地址值对应的内容还是在字符串常量池中。
String s3 = new String("javaEE");
String s4 = new String("javaEE");
System.out.println(s1 == s3);//false
System.out.println(s1 == s4);//false
System.out.println(s3 == s4);//false
注意:
字面量是在字符串常量池中,new的是在堆空间中。
String的拼接
结论:
i)常量与常量的拼接结果在常量池,且常量池中不会存在相同内容的常量。
ii)只要其中有一个变量,结果就在堆中。
iii)如果拼接的结果调用intern()方法,返回值就在常量池中。
@Test
public void test3(){
String s1 = "javaEE";
String s2 = "hadoop";
String s3 = "javaEEhadoop";//s3和s4都是在常量池中,拼接的这种就是同一个
String s4 = "javaEE" + "hadoop";
String s5 = s1 + "hadoop";//这里s5就相当于new了,因为出现了变量s1
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 = s5.intern();//返回值得到的s8使用的常量池中已经存在的“javaEEhadoop”
System.out.println(s3 == s8);//true
}
面试题
面试题一
String s = new String(“abc”);方式创建对象,在内存中创建了几个对象?
两个:一个是堆空间中new结构,另一个是char[]对应的常量池中的数据:“abc”。
常量池中不会出现两个相同的abc,若有现成的直接用现成的。否则,创建新的。
不能使用s.属性改变abc的值,只能在常量池中新建一个。
面试题二
public class StringTest {
String str = new String("good");
char[] ch = {'t', 'e', 's', 't'};
public void change(String str, char ch[]) {
str = "test ok";
ch[0] = 'b';
}
public static void main(String[] args) {
StringTest ex = new StringTest();
ex.change(ex.str, ex.ch);//String不可变性
System.out.println(ex.str);//good
System.out.println(ex.ch);//best
}
}
原理:java值传递机制与String不可变性
Java里方法的参数传递方式只有一种:值传递。 即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响。
- 形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参
- 形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参