目录
String类
1. String类的声明
//jdk源码:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc {
String类被final修饰,不允许被继承。即String类不存在子类,Java程序中有且仅有一个String类。
继承和方法重写在为程序带来灵活性的同时,也会带来多个子类行为不一致的问题。为保证所有JDK使用者都使用同一String类的固有实现,不允许自定义修改其中的具体实现,不希望体现个体差异性,故使用final终结器,防止String类被继承后进行方法重写。
2. 创建String字符串对象的四种方式
1)直接赋值:String str = "Hello BubbleoOoo"
2)构造方法中传入字符串:String str = new String("Hello BubbleoOoo");
3)构造方法中传入字符数组:将字符数组转为字符串
char[] data = new char[] {'a', 'b', 'c'}; String str = new String(data);
4)调用String类的静态方法valueOf(任意数据类型):将其他数据类型转为String类型
String str = String.valueOf(10);
3. String字符串的不可变性
一个String类字面量
""
即一个字符串对象,保存在字符串常量池中。new出来的字符串对象保存在堆中。
String字符串为不可变字符串,字符串对象一旦声明后即不可修改。程序中修改的仅仅是String类的引用,修改其指向的字符串对象地址。
public class StringTest {
public static void main(String[] args) {
String str1 = "hello world";
String str2 = str1; //str2引用也指向"hello world"
str2 = "hello"; //【仅修改str2引用指向的字符串对象,并未修改字符串对象本身,也不影响str1引用】
System.out.println(str2); //输出str2转为指向"hello"
System.out.println(str1); //输出str1仍然指向"hello world"
}
}
e.g. 下方例题输出结果为 good and gbc 。
【题解】
1)输出gbc的原因:change方法的形参ch为数组对象的引用。当main方法中的ex.ch
作为实参传递给change方法的形参ch
时,这两个引用都指向同一个数组对象的地址值。而在执行change方法的过程中,ch[0] = 'g';
通过形参引用ch
改变了数组对象本身,故当再次通过实参引用ex.ch
访问该数组时,访问到的是改变后的数组,即gbc。
2)输出good的原因:main方法中的ex.str在创建对象ex调用构造方法时完成初始化,指向堆中的"good"。当调用change方法时,会拷贝传入的实参引用ex.str
的地址值,使得此时形参引用str
也指向堆中的"good"。在执行change方法的过程中,形参引用str
的指向发生了转变,转为指向常量池中的"test ok",但原堆中对象"good"并未被改变。且实参的ex.str
仍然指向原堆中的"good"。待change方法执行结束,其形参引用str
随之销毁。
当引用数据类型的引用作为方法形参时,根据参数的值传递,会将实参引用的地址值拷贝一份给形参引用,使得两个引用指向同一个地址值。
方法的形参、方法内的局部变量均为临时变量。当方法被调用入栈时,其形参、局部变量入栈,待方法调用结束出栈时,这些临时变量都会随之销毁。
public class Example{
String str = new String("good");
char[] ch = {
'a' , 'b' , 'c' };
public static void main(String args[]){
//【main方法入栈】
Example ex = new Example(); //【ex.str入栈、ex.ch入栈】
//ex.str在创建对象调用构造方法时完成初始化,指向堆中的"good"
ex.change(ex.str,ex.ch); //【change方法入栈】【change方法的形参str、ch入栈】
//change方法的形参str由实参ex.str值传递而来,初始时也指向堆中的"good"
/* 执行change方法:改变形参str的指向为常量池中的"test ok",不影响堆中的"good"本身
直接改变了形参ch引用指向的堆中的数组本身 */
//【change方法及其形参出栈】
//此时change方法的形参引用str、ch都随方法的调用结束而销毁
System.out.print(ex.str + " and ");
//ex.str引用仍然指向堆中的"good",且"good"本身未发生改变
System.out.print(ex.ch);
//原实参ex.ch引用指向的数组本身已经发生了改变,访问到的即是改变后的数组gbc
} //【main方法出栈】
public void change(String str,char ch[ ]){
//形参str引用初始时指向堆中的"good"
str = "test ok"; //形参str引用转为指向常量池中的"test ok",但并非修改"good"对象本身
ch[0] = 'g'; //通过形参ch引用已经改变了堆中的数组对象本身
}
}
【总结】
注意当String引用作为实参通过值拷贝传递给形参时,形参和实参的String引用会指向同一个内存地址值。但当形参的Stiring引用发生改变时,只是改变了形参String引用的指向,不改变原String字符串对象(不可变性); 而原来实参的String引用仍然指向原地址值,即指向原String字符串对象。
String字符串不可变的原因
//jdk8源码:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
@Stable
private final char[] value;
...
}
//jdk17源码:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence, Constable, ConstantDesc {
@Stable
private final byte