要了解String类型的特性,首先了解java的基础知识。
一、 基本数据类型和引用数据类型
JVM中使用栈来存储方法以及非全局的变量,对于基本类型来说,栈中既存变量又存值,基本类型有8种:boolean, byte,short,int, long,float, double, char, 不是基本类型的类型称为引用类型,对于引用类型来说,栈中存变量及对象内存地址,对象内存地址指向实际对象实例。String 就是引用数据类型。基本数据类型之间的比较使用"==", 引用类型比较通常有特定的方法如equals方法。
二、 字符串常量池
String 作为引用类型,正常来说创建应该是String str = new String("Hello");,但是String太常用,几乎每一个类型都包含这个类型的对象,这样创建String对象就会带来大量的时间和空间开销,影响程序的性能,所以JVM对String类型的创建做了优化,设计了字符串常量池替代new创建String类型,变成了String str = "Hello";String类型在创建时首先会去字符串常量池中查找是否存在该字符串,如果没有则创建改字符串,如果有,则将引用指向该字符串。这样设计多个变量就会指向同一个字符串对象,一个变量修改就会影响另外的引用,为了消除这种影响,String类型被设计为final修饰。字符串常量池存在JVM方法区,所以里面的字符串不会被轻易回收。
三、 方法参数的值传递和引用传递
在java中,方法参数的值传递和引用传递对应基本数据类型和引用数据类型,基本数据类型参数是值传递,引用数据类型是参数传递。值传递的特点是方法传参数时,会将原变量(基本数据类型变量和值存一起)拷贝一个副本传入方法,方法中对拷贝的副本操作并不会影响原变量。引用传递也会将原变量拷贝一个副本传入方法,但是由于变量存的是引用地址,所以方法中对变量的修改会影响原变量。String是引用数据类型,也就有引用传递的特点,但是java中String是fina修饰的,也就是不可变的,传入方法也不会被修改。
四、 String的创建
public static void main(String[] args) { String str = "Hello"; String str2 = "Hello"; String str3 = new String("Hello"); String str4 = new String("Hello"); String str5 = str; System.out.println("str == str2:" + (str == str2)); System.out.println("str == str3:" + (str == str3)); System.out.println("str3 == str4:" + (str3 == str4)); System.out.println("str2 == str5:" + (str2 == str5)); System.out.println("str equals str3:" + (str.equals(str3))); }
- String str = "Hello" 在创建时会在字符创常量池创建字符串对象 "Hello",
- String str2 = "Hello" 在创建时会在字符创常量池查找"Hello", 发现已存在,会指向"Hello", str == str2为true。
- str3 和 str4 在创建时都会在堆中new一个新对象实例,所以str == str3 和 str3 == str4 都是false
- String str5 = str中,str5也是指向常量池字符串, 所以 str2 == str5 为true
- String重写了equals方法,比较是两个String对象的值,所以str.equals(str3)为true
执行结果:
str == str2:true
str == str3:false
str3 == str4:false
str2 == str5:true
str equals str3:true
图解String的创建:
5. String类型的参数传递
public static void main(String[] args) { int ii = 10; Map<String, String> map = new HashMap<>(); map.put("key", "Hello"); String str = "Hello"; String str2 = new String("Hello"); change1(ii); System.out.println("ii: " + ii); change2(map); System.out.println("map: " + JSON.toJSONString(map)); change3(str); System.out.println("str: " + str); change4(str2); System.out.println("str2: " + str2); } public static void change1(int ii) { ii = 50; } public static void change2(Map<String, String> map) { map.put("key", "world"); } public static void change3(String str) { str = "你好!"; } public static void change4(String str2) { str2 = new String("你好!"); }
- ii变量是基本数据类型int,chang1方法执行是拷贝了ii的副本并修改并不能改变ii的值。
- map变量是引用数据类型,chang2方法中操作的map和原map变量都指向同一个HashMap对象,所以chang2方法对map的修改会影响到原map
- str和str2都是引用数据类型,但是String不可被修改,所以str = "你好!" 和 str2 = new String("你好!") 都是创建一个新的对象,所以不会影响到原变量。
执行结果:
ii: 10 map: {"key":"world"} str: Hello str2: Hello
图解执行change方法前后如下图: