字符串运行时常量池的所在地
JDK1.6之前,字符串常量池还未与其它常量池分离,都位于永久代中(方法区的实现方式),JDK1.7虽然还未废除永久代,但此时字符串常量池和静态变量已经离开了常量池,去了堆中。JDK1.8已经废除常量池,取而代之的是元空间(元空间不再位于虚拟机中,而是处于本地内存中),其它常量池和类信息都去了元空间,而字符串常量、静态变量、Class对象存在堆中。
String类型的各种初始化方式的不同之处
内容
方式一:String a = “aaa” ;
方式二:String b = new String(“aaa”);
方式三: String d3=new String (“aa”); String d4=“a”+d3;
方式四: String d = “aa” + “a”;
前两种方式都能创建字符串对象,但方式一要比方式二更优。因为当常量池中已经有了"aaa”实例,方式一不会再使用内存,而方式二还是会在堆中开辟一个新的内存来创建一个新对象,增加了内存消耗。方式三字符串的+操作其本质是创建了StringBuilder对象进行append操作,然后将拼接后的StringBuilder对象用toString方法处理成String对象,toString方法处理过程相当于new了一个新的对象,但无论常量池中是否有"aaa"字面量,都不会将该对象的内容加到常量池中。在常量池中没有"aaa"字面量时,方式四会直接在常量池创建"aaa"的实例。若已有,则返回该字面量的引用。
1.当常量池中没有"aaa"的实例
方式一会在常量池中创建一个“aaa”的实例,而方式二会new一个对象存放在堆内存中,并且还会在常量池中创建一个"aaa"的实例。方式一创建一个实例,方式二创建了两个实例。方式三会在堆中创建一个"aaa"的对象,但不会在常量池中创建实例。方式四只在常量池中创建一个"aaa"的实例。
2.常量池中已经有”aaa”
通过方式一创建对象,程序运行时会在常量池中查找”aaa”字符串,将找到的”aaa”字符串的地址赋给a。通过方式二创建对象,无论常量池中有没有”aaa”字符串,程序都会在堆内存中开辟一片新空间存放新对象。方式三会在堆中创建一个"aaa"的对象,不会在常量池中创建该字面量实例。方式四会得到"aaa"字面量的引用,不会重新创建实例。
示例
String a="11"; //只在常量池中产生字面量
String a1=new String("11"); //这个过程会在堆中产生一个对象,因为常量池中已有"11"实例,所以不会再在常量池中生成"11"实例
String a2="11"; //与a一样,都指向常量池中"11"的实例
System.out.println(a==a1); //不相等,因为a1指向堆,a指向堆中的常量池
System.out.println(a==a2); //相等,都指向常量池中"11"的字面量
String拼接的示例
String d = "44" + "44";//这种方式直接在常量池创建"4444"的示例,并返回对字面量的引用
String d1 = "4444"; //因为常量池已有"4444"的字面量,直接返回对该字面量的引用
String d2 = new String("4444"); //堆中创建对象,池中不创建实例
String d3=new String ("44");
String d4="44"+d3;//区别与d,该方式在堆中创建一个新的对象,不管常量池中是否有"4444"的实例,都不会在常量池中创建实例
System.out.println(d == d1);//true,都指向常量池
System.out.println(d == d2);//false 一个指向常量池,一个指向堆
System.out.println(d == d4);//false 一个指向常量池,一个指向堆
System.out.println(d2== d4);//false 指向堆中不同的对象
String类型对象的intern()方法详解
1.这个方法是常量池动态性的重要体现,在运行期间将新的变量放入常量池中。当字符串对象执行intern()方法之后,若常量池中已有该字符串的实例,则返回常量池中常量的引用,如无,则将堆中对象的引用存到常量池中,并返回该引用。(在jdk1.7之前的做法是将堆中的数据复制一份到常量池中,并返回常量池字面量的引用,1.7以及之后为了节约内存,改为将堆中该对象的引用存到常量池中,返回该引用)
下面是常量池没有该字符串常量的示例
String s = new String("11") + new String("11");
//上面这个代码只生成了“11”这个字面量,而没有生成"1111"的字面量,在堆中生成了内容为"1111"的对象
s.intern();
//s.intern()执行过程,会去常量池是否有"1111"这个字面量,若有,则返回常量池的地址,没有,则会把堆中“1111”对象地址存到常量池中,并返回这个对象地址,这里是没有,所以返回对象地址。
String s1 = "1111";
System.out.println(s == s1);
//s1定义的时候发现常量池中有了"1111"的对象的引用地址,所以没有返回"1111"这个字面量,而是拿到了这个引用地址。所以这里结果是true
下面是常量池有该字符串常量的示例
//印证,我先创建字面量
String c3="3333";
String c= new String("33") + new String("33"); //只生成了“11”这个字面量
c.intern(); //因为已经有了字面量,所以执行intern函数返回的是对字面量的引用
String c1 = "3333";
String c2=new String("3333");
System.out.println(c== c1);
//c指向堆中内容为"3333"的对象,c1指向常量池,所以false
System.out.println(c3==c1);
//因为c3初始化时,常量池已经有了"3333"的实例,所以c1其实和c3都指向常量池中该实例,所以为true
字符串的反转方法
-
StringBuilder类型的reverse();
-
通过Stirng中的charAt方法获取每一个字符然后通过字符拼接
-
通过toCharArray方法将字符串转换成数组再反转
-
递归
String与其它类型的转换的通用方法
String转其它类型----调用基本数据类型对应的包装类中的方法parseXXX(String)或valueOf(String)即可返回相应基本类型。
//String类型转成其它类型
String str="222";
Integer inte=Integer.parseInt(str);
Integer inte1=Integer.valueOf(str);
System.out.println(inte instanceof Integer); //true
System.out.println(inte1 instanceof Integer);//true
其它类型转String----一种方法是将基本数据类型与空字符串("")连接(+)即可获得其所对应的字符串;另一种方法是调用String 类中的valueOf()方法返回相应字符串。还有一种是使用toString方法
//其它类型转换成String类型
Integer in=123;
String str1=in.toString();
String str2=String.valueOf(in);
String str3=""+in;
System.out.println(str1 instanceof String); //true
System.out.println(str2 instanceof String); //true
System.out.println(str3 instanceof String); //true