目录
1.字符串
特点:字符串一旦被初始化,就不可以被改变,存放在方法区中的常量池中。
虚拟机必须为每个被装载的类型维护一个常量池。常量池就是该类型所用到常量的一个有序集合,包括直接常(string,integer和 floating point常量)和对其他类型,字段和方法的符号引用。
先思考这几个问题:
String s1 = "abc";
String s2 = "abc";
String s3 = new String("abc");
String s4 = new String("abc");
1.字符串常量池:JAVA虚拟机(JVM)中存在着一个字符串池,其中保存着很多String对象; 并且可以被共享使用,因此它提高了效率。 由于String类是final的,它的值一经创建就不可改变。 字符串池由String类维护,我们可以调用intern()方法来访问字符串池。intern()
方法的作用是在常量池中查找值等于(equals)当前字符串的对象,如果找到,则直接返回这个对象的地址;如果没有找到,则将当前字符串拷贝到常量池中,然后返回拷贝后的对象地址。下面的代码可以解释intern()
的功能。
String a = "123";
String b = new String("123");
String c = new String("123");
System.out.println(a == b); // false
System.out.println(a == b.intern()); // true
System.out.println(c == b.intern()); // false
System.out.println(c.intern() == b.intern()); // true
String s1 = "abc"; 在字符串池创建了一个对象, String s2 = "abc";字符串pool已经存在对象“abc”(共享),所以创建0个对象,累计创建一个对象,此时s1==s2为true这表示指向的是同一个对象,s1.equals(s2)为ture,表示字符串的值相等,字符串中equals比较的是字符串内容是否相同。字符串的equals方法在这里是个特殊,因为Object的equals方法表示的是比较两个对象是否相等,其实内部比较的就是两个对象地址。
2.关于new String(""), String s3 = new String("abc");此时创建了两个对象,一个存放在字符串池中,一个存在与堆区中,还有一个对象引用s3存放在栈中,引用s3指向堆区的那个对象。String s4 = new String("abc");此时字符串池中已经存在“abc”对象,所以只在堆中创建了一个对象 ,当然还是有一个引用存在栈中。s3==s4为false, s3和s4栈区的地址不同,指向堆区的不同地址;s3.equals(s4)为true,因为他们都的值相同。s1==s3为false,一个存在常量区,一个存在堆区。s1.equals(s3)为true,因为值相等。
3.关于常量编译的问题,
String str1 = "ab" + "cd"; //1个对象
String str11 = "abcd";
由于常量的值在编译的时候就被确定(优化)了,在这里,"ab"和"cd"都是常量,所以编译后str1==str11为true.
4.String str2 = "ab"; //1个对象
String str3 = "cd"; //1个对象
String str4 = str2+str3;
String str5 = "abcd";
局部变量str2,str3存储的是存储两个拘留字符串对象(intern字符串对象)的地址。第三行代码原理(str2+str3): 运行期JVM首先会在堆中创建一个StringBuilder类, 同时用str2指向的拘留字符串对象完成初始化, 然后调用append方法完成对str3所指向的拘留字符串的合并, 接着调用StringBuilder的toString()方法在堆中创建一个String对象, 最后将刚生成的String对象的堆地址存放在局部变量str4中。 而str5存储的是字符串池中"abcd"所对应的拘留字符串对象的地址。 str4与str5地址当然不一样了。 内存中实际上有五个字符串对象: 三个拘留字符串对象(str2,str3,str5)、一个String对象和一个StringBuilder对象。str4==str5为false.
5.JAVA编译器对string + 基本类型/常量 是当成常量表达式直接求值来优化的。
运行期的两个string相加,会产生新的对象的,存储在堆(heap)中
String str6 = "b";
String str7 = "a" + str6;
String str67 = "ab";
System.out.println("str7 = str67 : "+ (str7 == str67));为false str6为变量,在运行期才会被解析。
常量变量的编译优化:
final String str8 = "b";
String str9 = "a" + str8;
String str89 = "ab";
System.out.println("str9 = str89 : "+ (str9 == str89)); 由于str8是基本类型或常量,java编译器编译的时候把他当成常量表达式直接求值优化的。所以str9==str89为true.
6.对final用法的理解:
a.append("222");// 编译通过
可见,final只对引用的"值"(即内存地址)有效,它迫使引用只能指向初始指向的那个对象,改变它的指向会导致编译期错误。至于它所指向的对象的变化,final是不负责的。
7.深入理解String的用法:
在执行到双引号包含字符串的语句时,如String a = "123",JVM会先到常量池里查找,如果有的话返回常量池里的这个实例的引用,否则的话创建一个新实例并置入常量池里。如果是 String a = "123" + b (假设b是"456"),前半部分"123"还是走常量池的路线,但是这个+操作符其实是转换成[SringBuffer].Appad()来实现的,所以最终a得到是一个新的实例引用,而且a的value存放的是一个新申请的字符数组内存空间的地址(存放着"123456"),而此时"123456"在常量池中是未必存在的。而new出来会在常量池中。
要注意: 我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,创建了String类的对象str。担心陷阱!对象可能并没有被创建!而可能只是指向一个先前已经创建的对象。只有通过new()方法才能保证每次都创建一个新的对象
8.StringBuffer与StringBuilder的区别,它们的应用场景是什么?
StringBuffer:字符串变量(线程安全)
StringBuilder:字符串变量(线程不安全)
9.对字符串不可变性的深入理解:
无论是sub操、concat还是replace操作都不是在原有的字符串上进行的,而是重新生成了一个新的字符串对象。也就是说进行这些操作后,最原始的字符串并没有被改变。
在这里要永远记住一点:“String对象一旦被创建就是固定不变的了,对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象”。
Java中的常量池,实际上分为两种形态:静态常量池和运行时常量池。
所谓静态常量池,即*.class文件中的常量池,class文件中的常量池不仅仅包含字符串(数字)字面量,还包含类、方法的信息,占用class文件绝大部分空间。
而运行时常量池,则是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池。
String a = "chenssy";
String b = "chenssy";
String c = new String("chenssy");
String常用的方法compareTo
语法:Str1.compareTo(Str2);
其返回的是一个int类型值。若Str1等于参数字符串Str2字符串,则返回0;若该Str1按字典顺序小于参数字符串Str2,则返回值小于0;若Str1按字典顺序大于参数字符串Str2,则返回值大于0。
java中的compareto方法,返回参与比较的前后两个字符串的asc码的差值,看下面一组代码
String a="a",b="b";
System.out.println(a.compareto.b);
则输出-1;
若a="a",b="a"则输出0;
若a="b",b="a"则输出1;
单个字符这样比较,若字符串比较长呢??
若a="ab",b="b",则输出-1;
若a="abcdef",b="b"则输出-1;
也就是说,如果两个字符串首字母不同,则该方法返回首字母的asc码的差值;
如果首字母相同呢??
若a="ab",b="a",输出1;
若a="abcdef",b="a"输出5;
若a="abcdef",b="abc"输出3;
若a="abcdef",b="ace"输出-1;
即参与比较的两个字符串如果首字符相同,则比较下一个字符,直到有不同的为止,返回该不同的字符的asc码差值,如果两个字符串不一样长,可以参与比较的字符又完全一样,则返回两个字符串的长度差值
参考博文:
深入理解Java:String - ^_TONY_^ - 博客园 博大精深的一篇博客