public final class String
implements java .io. Serializable , Comparable<String>, CharSequence {}
首先,熟悉一下 String类 如上。里面细节请自行翻阅源码。
主要围绕下一段代码来理解学习String操作字符串:(Java版本:Jdk8)
public static void testStringConstantPool(){
String string = new StringBuilder("lin").append("jiao").toString();
//(1)true
System.out.println(string.intern().equals(string));
//(2)true
System.out.println(string.intern()==string);
String string2 = new StringBuilder("ja").append("va").toString();
//(3)true
System.out.println(string2.intern().equals(string2));
//(4)false
System.out.println(string2.intern()==string2);
}
字符串常量池
诞生时间:编译时
所处区域:堆
储存内容:堆内的字符串对象的引用和字符串常量。
String::intern()是一个本地方法,它的作用是如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象的引用;否则,会将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。
注:自 Jdk 7起原本存放在永久代的字符串常量池被移至Java堆中,而Jdk8也由元空间代替了永久代的存在。
(1)(3)处比较是String对象的内容。
(2)(4)处比较的是String对象的引用。
Jdk7及以后版本,intern()方法实现就不需要再拷贝字符串的实例到永久代,字符串常量池既然已经移到Java堆中,那只需要在常量池里记录一下首次出现的实例引用即可,因此在面代码中(1)(2)处intern()返回的引用和由StringBuilder创建的字符串实例是同一个。而对strig2进行的==比较返回false,这是因为”java”这个字符串在执行 StringBuilder ().toString()之前就已经出现过了,字符串常量池中已经有它的引用,不符合intern()方法要求的“首次出现”原则,而”linjiao”是首次出现,因此==结果返回true。
String string = new StringBuilder(“linjiao”)是通过构造函数,在堆上创建一个对象。而”linjiao”首次出现。
String string2 = new StringBuilder(“java”),字符串“java”不是首次出现 ,字符串常量池已经有它。new StringBuilder(“java”)返回的是堆中引用,string2.intern()返回的是 字符串对象实例的引用。
字符串常量池里的内容是在类加载时完成,经过验证,准备阶段之后在堆中生成字符串对象实例,然后将该字符串对象实例的引用存到string pool中 (记住:string pool中存的是引用值而不是具体的实例对象,具体的实例对象是在堆中开辟的一块空间存放的) 。 在HotSpot VM里实现的string pool功能的是一个StringTable类,它是一个 哈希表 ,里面存的是驻留字符串(也就是我们常说的用双引号括起来的字符串内容)的引用(而不是驻留字符串实例本身),也就是说在堆中的某些字符串实例被这个StringTable引用之后就等同被赋予了”驻留字符串”的身份。这个StringTable在每个HotSpot VM的实例只有一份,被所有的类共享。
//在堆中分配一个StringBuilder类对象空间,并将该对象堆地址压入操作数栈
0: new #2 // class java/lang/StringBuilder
//复制操作数栈顶数据并压入操作数栈,该指令使得操作数栈中有两个StringBuilder对象的引用值
* 3: dup
//将常量池中的字符串常量"lin"的驻留字符串引用地地址压入操作数栈
* 4: ldc #3 // String lin
//调用StringBuilder的初始化方法,弹出操作数栈栈顶的两个对象地址,用拘留字符串的引用值初
//始化new指令创建的StringBuilder对象,然后将这个对象的引用地址压入操作数栈
//即:拘留字符串的引用值 = new StringBuilder("");
//string.intern()=拘留字符串的引用值
* 6: invokespecial #4 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
* 9: ldc #5 // String jiao
* 11: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
* 14: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
// 弹出操作数栈顶数据存放在局部变量区的第一个位置上。此时存放的是new指令创建出的,
//已经被初始化的StringBuilder对象的地址 (此时的栈顶值弹出存入局部变量中去)。
* 17: astore_1
* 18: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
* 21: aload_1
* 22: invokevirtual #9 // Method java/lang/String.intern:()Ljava/lang/String;
* 25: aload_1
* 26: invokevirtual #10 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
* 29: invokevirtual #11 // Method java/io/PrintStream.println:(Z)V
* 32: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
* 35: aload_1
* 36: invokevirtual #9 // Method java/lang/String.intern:()Ljava/lang/String;
* 39: aload_1
* 40: if_acmpne 47
* 43: iconst_1
* 44: goto 48
* 47: iconst_0
* 48: invokevirtual #11 // Method java/io/PrintStream.println:(Z)V
* 51: new #2 // class java/lang/StringBuilder
* 54: dup
* 55: ldc #12 // String ja
* 57: invokespecial #4 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
* 60: ldc #13 // String va
* 62: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
* 65: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
* 68: astore_2
* 69: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
* 72: aload_2
* 73: invokevirtual #9 // Method java/lang/String.intern:()Ljava/lang/String;
* 76: aload_2
* 77: invokevirtual #10 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
* 80: invokevirtual #11 // Method java/io/PrintStream.println:(Z)V
* 83: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
* 86: aload_2
* 87: invokevirtual #9 // Method java/lang/String.intern:()Ljava/lang/String;
* 90: aload_2
* 91: if_acmpne 98
* 94: iconst_1
* 95: goto 99
* 98: iconst_0
* 99: invokevirtual #11 // Method java/io/PrintStream.println:(Z)V
* 102: return
通过分析:String a = “a”;与String b = new String(“b”);这两个的区别,扩展理解String。
1、直接定义的String a = “a”,“a”是储存在堆中字符串对象实例空间中,而字符串对象实例的引用返回给a对象;new String(“a”)是通过构造函数初始化创建,存储在堆中,堆引用值返回给对象;
2、常量池中相同的字符串只会有一个,但是new String(),每new一个对象就会在堆中新建一个对象,不管这个值是否相同;
String a = “linjiao” ;String b = “linjiao”; a b都指向字符串常量池中的“a”,所以 a==b 为 true;
String a = new String(“linjiao”) 与String b = new String(“alinjiao);是在堆中创建两个对象new String() “a”是常量池中的”a”,这两个对象的值都为 a,所以a==b 返回false;a.equals(b)返回true;
编译过程会把会把字符串“linjiao”放到在常量池中。用构造器创建的对象,是生成不同的对象。每new一次 JVM 就会在堆中创建一个对象。String a,b只是内容相同罢了。用equals()或者System.out.print(a.intern()==b.intern());就返回true了。(intern() 方法返回字符串对象的规范化表示形式),==比较的是两个对象的引用。
3、String a = “a”在编译阶段就会在内存中创建;
String a = new String(“a”)是在运行时才会在堆中创建对象。
4、String a=”abcdef”;与System.out.print(a==”abcdef”);结果:true
运行出现的字符串常量,若是在常量池中出现过,则JVM会认为同一个对象,以节省内存开销,所以这两个字符串会被认为是同一个对象。
5、String a=”linjiao”;
String b=””;
String c=a+b;
System.out.print(c==”linjiao”);
结果:false
原因:编译时,先将”abcedf”放在常量池中,而c的值则是在运行时在堆里创建的。所以为false。
String的综合运用:编写一个方法,输入一个字符串,统计出每个字符的个数,并输出。
分析:借助Map key-value的原理实现。key不变,value++。
HashMap/TreeMap,这里使用TreeMap。
public static void statisticsString(String string){
//使用带有排序功能的TreeMap
TreeMap<Character,Integer> treeMap = new TreeMap<>();
//将treeMap中的key全部取出来,然后存储到set中
//Set可以去重
Set set = treeMap.keySet();
//将所需要统计的字符串转换成一个字符数组
char [] chars = string.toCharArray();
//通过for循环逐一统计每个字符出现的次数
for (int i = 0; i < chars.length; i++) {
if (!set.contains(chars[i])){
treeMap.put(chars[i],1);
}else{
treeMap.put(chars[i],treeMap.get(chars[i])+1);
}
}
printStr2(treeMap);
}
编写一个方法,输入一个字符串,判断其是否对称,如“abdba”;
public static boolean test_boolean(String string){
int len = string.length();
if(len==0) return false;
int head = 0;
int tail = len-1;
int temp = 0;
for (head = 0; head < len/2; head++,tail--) {
//以头尾元素进行比较,出现不等返回false
if (string.charAt(head)!=string.charAt(tail)){
return false;
}
}
//没有出现不等返回true
return true;
}
对String的操作及字符串常量池的实现原理,理解尚欠缺太多,文章是在基于自身理解层次撰写,如有错误,敬请评论指正,相互交流学习。
谢谢!
未完待续。