String类底层维护了一个final修饰的char数组(jdk8,jdk9后变成了byte数组),所以String一旦创建就不能更改,属于字符串常量,任何字符串常量都是一个String类的匿名对象。
==和equals()的区别
== 比较基本类型数据的时候比较的是值的大小,比较引用数据类型的时候比较的是对象的地址。
equals()是Object类中的方法,Object中equals方法的实现还是依靠==,所以比较的是两个对象的地址。但是String类对equals()进行了重写,它会先通过 == 判断两个对象的地址是否一致,一致的话就返回true,如果不一致,就进行字符串内容的对比,如果内容相同就返回true。
直接赋值实例化和构造器实例化的区别
因为String类底层是用数组来存储字符串的,所以受数组长度的限制,字符串不可变。
不管是哪种实例化方式,它们的引用都是存储在栈中的。如果是编译期创建好的就存储在常量池中,如果是运行期间创建好的就存储在堆(动态常量池)中。对于equals相等的字符串,在常量池中永远只有一份,在堆中可以有多份。
-
直接赋值实例化
String str = “ABC”;
编译期已经创建好了,字符串就存储在常量池中。如果常量池中已存在“ABC”,就让str直接引用,如果不存在,就在常量池中创建一个。
-
构造器实例化
String str2 = new String(“ABC”);
运行期间创建字符串对象,因为是new出来的,所以字符串对象会存储在堆中。当new了一个“ABC”时,会先去常量池看看是否已有,如果没有就在常量池中创建一个“ABC”对象,然后在堆中创建一个常量池中“ABC”的拷贝对象,返回堆中“ABC”对象的地址。
-
String intern()
str1 = str.intern();
str.intern()会返回一个内容与str相同,但是来源于字符串池的字符串。当调用intern()方法时,如果池中已经包含了该字符串,那么返回池中的这个字符串,否则,在池中创建该字符串并返回。
-
做点小题:
String str1 = new String("ABC"); String str2 = new String("ABC"); System.out.println(str1 == str2)//false String str3 = "ABC"; String str4 = "ABC"; String str5 = "A" + "BC";//在定义str5对象时采用的全部是字符串常量,编译器就会把它们当做一个整体,放到常量池中。 System.out.println(str3 == str4)//true System.out.println(str3 == str5)//true String a = "ABC"; String b = "A"; String c = b + "BC"; System.out.println(a == c)//false //a和b都是字符串常量所以在编译期都已经确定了,会存放在常量池。c中有个变量b,变量的内容是运行时分配的,所以会造成内容不确定的问题,会保存在堆中。因此结果是false。 String d = new String("d"); String d1 = "d"; System.out.println(d1 == d.intern())//true
字符串修改分析
字符串的对象内容理论上是可以修改的,它所做的修改并不是修改具体的堆内存,而是创建新的堆内存并引用,之前的堆内存就成了垃圾。所以要避免频繁的进行字符串的修改,那样会造成大量的垃圾。对于频繁修改的需求,可以借助StringBuffer或StringBuilder类。
String的方法
String有length(),数组的是length属性。String方法太多了,用了查文档。