前言:
此刻各位老铁们对“java取字符串中的字符”大体比较着重,咱们都想要剖析一些“java取字符串中的字符”的相关内容。那么小编在网络上收集了一些关于“java取字符串中的字符””的相关知识,希望我们能喜欢,大家一起来了解一下吧!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对象的引用。
注:自Jdk7起原本存放在永久代的字符串常量池被移至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的操作及字符串常量池的实现原理,理解尚欠缺太多,文章是在基于自身理解层次撰写,如有错误,敬请评论指正,相互交流学习。
谢谢!
未完待续。
标签: #java取字符串中的字符