龙空技术网

Java中String操作字符串杂记

Invictus 92

前言:

此刻各位老铁们对“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取字符串中的字符