【java基础】1. String类详解
程序员文章站
2022-07-05 19:53:57
文章目录1. String2. String类型详解2.1. String类2.2. String的创建方式2.3. 不同的创建方式在内存中的行为2.3.1 字面量方式创建2.3.1.1 字面量拼接陷阱2.3.2 构造器(new)创建3. String的不可变性3.1 String作为参数的不可变性1. StringString 是一个引用数据类型String可以和8种基本数据类型变量进行运算,且运算只能用"+"进行运算,运算后的结果为String类型2. String类型详解2.1. Str...
文章目录
1. String
- String 是一个引用数据类型
- String可以和8种基本数据类型变量进行运算,且运算只能用"+"进行运算,运算后的结果为String类型
2. String类型详解
2.1. String类
- String类被fianl修饰,不可被继承
- String的底层实际上是将字符串以字符的形式存放在一个char[]类型的数组中
- 存放字符串的数组名称为:
private final char value[];
- 存放字符的char[]数组是fianl修饰的,这也就体现了String字符串定义后的不可变性,在创建后不可修改
- 如果要搞清楚String,首先一定要知道字符串存放在内存的哪里
- 不论是以什么方式创建的String,其字符串存放的地址一定是在方法区的常量池中
2.2. String的创建方式
String str1 = "0000"; //字面量方式 创建
String str2 = new String("0000"); //构造器的方式创建
String str3 = new String(new char[]{'0','0','0','0'}); //构造器方式创建
- 主要方式是两种,字面量方式创建和构造器方式创建
2.3. 不同的创建方式在内存中的行为
2.3.1 字面量方式创建
String str1 = "0000"; //字面量方式 创建
String str2 = "0000"; //字面量方式 创建
System.out.println(str1==str2); //true
- 字面量方式创建会在内存中生成0或1个对象,如果常量池中不存在该字符串,就创建一个,如果存在就直接引用
- str1使用字面量的方式创建时,此时方法区的常量池中还没有"0000"这个字符串,所以会先在方法区中的常量池中创建"0000"这个字符串,然后将"0000"这个字符串的地址值给str1引用
- 然后再声明str2,同样是使用字面量的方式创建,由于之前在创建str1时,常量池中已经有了"0000",所以此时会直接将"0000"的地址值给str2,不会再重新在常量池中创建新的"0000"
- 最后将str1与str2通过
==
进行比较,==
比较的地址值,此时str1与str2的地址值相同,所以结果为ture
2.3.2 构造器(new)创建
String str3 = new String("0000"); //构造器创建
String str4 = new String("0000"); //构造器创建
System.out.println(str3 == str4); //false
- 构造器创建会在内存中生成1或2个对象
- str3使用构造器方式创建,由于此前常量池中没有"0000"这个字符串,所以会在常量池中创建"0000"字符串,然后在堆中创建String对象,将常量池中的"0000"的地址值给String对象的value[]引用,最后将String对象在堆中的地址给str3进行引用,一共在内存中生成了2个对象
- str4使用构造器方式创建,由于之前创建str3时,在常量池中已经生成了"0000"这个字符串,所以不会在常量池中重复创建,不过会在堆中再重新生成一个String对象,将"0000"的地址值给String对象的value[]引用,一共只生成了一个String的对象
- 最后用
==
进行对比,结果为false,因为对比的是两个在堆中生成的String类,地址值是不相等的,但是两个String对象内部的value[]是相等的
2.3.3 字面量拼接陷阱
String str1 = "0000";
String str2 = "1111";
String str3 = "00001111";
String str4 = "0000"+"1111";
String str5 = str1+"1111";
String str6 = "0000"+str2;
String str7 = str1+str2;
System.out.println(str3 == str4); //true
System.out.println(str3 == str5); //false
System.out.println(str3 == str6); //false
System.out.println(str3 == str7); //false
System.out.println(str3 == str7.intern()); //true
- 主题: 当进行拼接时,只有拼接的字符串全都是常量时,才是字面量方式创建,直接引用常量池中的地址,如果拼接时含有变量时,就会在堆中创建,引用的是堆中String对象的地址
- str3==str4之所以为true,就是因为str4拼接时都是常量拼接,所以是直接引用了常量池中的字符串常量的地址
- 其他的为false是因为,它们都是使用了变量进行拼接,所以都会在堆中创建String对象,然后引用String对象的地址
- String.intern()方法,返回的是字符串在常量池中的地址,所以str3==str7.intern()的结果为true
3. String的不可变性
- String类加上了fianl关键字,表示其不可被继承,并且String类中的value[]属性加上了final关键字表示value[]不可被继承和修改,但是对于数组来说,不能被修改的只是它指向的地址值,我们还是可以对数组中的元素进行修改的,为了保证元素无法被修改,value[]属性前加上了prevate进行修饰。也就是说,我们无法通过任何方法对value[]进行实质性的操作,在String类中,也没有暴露出对value[]直接操作的api,为的就是保持其不可变性与安全性
String str5 = "0000";
String str6 = "0000";
str5 = str5+"1111";
-
上面的代码中可以看到,str5与str6是相等的,常量池中只有一个"0000"字符串常量
-
此时将str5与"1111"进行拼接,并不是将常量池中的"0000"后面追加"1111",而是在常量池中重新创建一个"00001111"的字符串,将地址给str5
-
str6引用的地址值依旧还是"0000"的地址值没有改变
-
如果String是可变的,当str5与str6同时引用"0000"后,str5修改后不是创建新的字符串,而是直接将"0000"进行修改,那么也会导致引用了同一个地址的str6也被修改
3.1 String作为参数的不可变性,与"引用传递"
class test{
public static String str=new String("hello");
public static char myChar[]={'w','o','r','l','d'};
public static char myChar2[] = myChar;
public static void changed(String str,char[] myChar){
str = "hi";
myChar[0] = 'F';
}
}
/**
* String不可变性
*/
public class StringFinal {
public static void main(String[] args) {
System.out.println(test.str); //hello
System.out.println(test.myChar); //world
System.out.println(test.myChar2); //world
test.changed(test.str,test.myChar);
System.out.println(test.str); //hello
System.out.println(test.myChar); //Forld
System.out.println(test.myChar2); //Forld
}
}
-
结论
- 在上面的例子中,有声明为字符串的静态变量str,与声明为数组的静态变量myChar
- 我们创建一个方法changed(),将str与myChar作为参数传进去
- 调用changed()对str与myChar进行修改
- 修改后打印发现,原本的静态变量myChar与myChar2已经被改变,而静态变量str并没有被改变
-
分析: 为什么静态变量myChar的内容被改变了
- 首先引用数据类型在进行参数传递的时候,是引用传递,也就是传递的是堆中对象的地址值
- 调用changed()方法时,myChar作为参数传递到方法中,传递的不是myChar本身,而是myChar所引用的对象的地址
- 所以在方法中被修改的并不是静态变量myChar,而是myChar所引用的char数组对象本身,只不过静态变量myChar的引用是指向这个char数组对象的,所以myChar被修改了
-
分析: 为什么静态变量myChar2的内容也被改变了
- 在changed()方法中,我们并没有将myChar2作为参数传进去,对它进行修改,但是它还是被改变了
- 因为在定义myChar2时,我们使用了myChar2 = myChar ,所以myChar2与mychar引用的是同一个对象
- 正如我们上面所说,修改的不是变量本身,而是对变量所引用的实际堆中的对象进行了修改
- myChar2与myChar引用了同一个对象,所以当堆中的对象被修改时,myChar与myChar2的值就同时被修改了
-
分析:为什么str没有被修改
- 静态变量str作为String类型,在进行参数传递时也是引用传递,那它为什么没有被修改呢
- 因为String是不可变的,它是重新在常量池中创建了一个"hi"的字符串常量,所以在方法中的那个str相当于是重新引用了"hi"这个字符串的地址值,但是作为静态变量的str引用的还是"hello"的地址值
- 我曾经不理解,为什么既然方法中的str重新引用了"hi"的地址值,静态变量str却没有重新引用"h1"的地址值呢?
- 这就是我们之前所说的"引用传递",传递过来的是静态变量str引用的String对象的地址值,而不是静态变量str本身,方法中的形参str与静态变量str只是名称相同而已,但他们绝不是同一个参数,例如我们将changed()方法的形参名称可以改一下
public static void changed(String strMd,char[] myChar)
,就能看出来,方法中的叫strMd,而静态变量叫str,它俩只是引用的同一个String对象而已,但并不是同一个参数
本文地址:https://blog.csdn.net/small_clear/article/details/110948252