Thinking in Java 整理笔记:字符串

来源:互联网 发布:西安 java 软件公司 编辑:程序博客网 时间:2024/06/05 19:17
不可变String:
1.String对象时不可变的:指的是每一个看起来会修改String值的方法,实际上都是创建了一个全新的String对象,以包含修改后的字符串内容。(用=号赋值时实际上是创建了另一个对象)

重载“+”与StringBuilder :
2.不可变性会带来一定的效率问题。为String对象重载的“+”操作符就是例子。当对String对象使用重载的+操作符时,编译器会先创建了一个StringBuilder对象,用以构造最终的String,并为每个字符串调用一次StringBuilder的append()方法,最后调用其toString方法生成最后的String对象
如:
   String s= "123456 12355";
   s+="222";
JVM字节码如下:
      0: ldc           #32                 // String 123456 12355
      2: astore_1
      3: new           #16                 // class java/lang/StringBuilder
      6: dup
      7: aload_1
      8: invokestatic  #34                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
     11: invokespecial #20                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
     14: ldc           #40                 // String 222
     16: invokevirtual #25                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     19: invokevirtual #29                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
     22: astore_1
     23: return
3.操作符在以下情况需要进行优化(在循环内部需要对String对象用到操作符+时):
   String s= "123456 12355";
   for(int i = 1;i<5;i++)
        s+=i;
其JVM字节码如下:
       0: ldc           #32                 // String 123456 12355
       2: astore_1
       3: iconst_1
       4: istore_2
       5: goto          30
       8: new           #16                 // class java/lang/StringBuilder
      11: dup
      12: aload_1
      13: invokestatic  #34                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
      16: invokespecial #20                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
      19: iload_2
      20: invokevirtual #40                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      23: invokevirtual #29                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      26: astore_1
      27: iinc          2, 1
      30: iload_2
      31: iconst_5
      32: if_icmplt     8
      35: return
其中8行,32行表示条件循环,可见每循环一次都要创建一个StringBuilder对象
解决此方法如下:
   StringBuilder s= new StringBuilder("123456 12355");
   for(int i = 1;i<5;i++)
        s.append(i);
   System.out.println(s.toString());
其JVM字节码如下:
       0: new           #16                 // class java/lang/StringBuilder
       3: dup
       4: ldc           #32                 // String 123456 12355
       6: invokespecial #20                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
       9: astore_1
      10: iconst_1
      11: istore_2
      12: goto          24
      15: aload_1
      16: iload_2
      17: invokevirtual #34                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      20: pop
      21: iinc          2, 1
      24: iload_2
      25: iconst_5
      26: if_icmplt     15
      29: getstatic     #37                 // Field java/lang/System.out:Ljava/io/PrintStream;
      32: aload_1
      33: invokevirtual #29                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      36: invokevirtual #43                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      39: return
可以看出在循环内部使用StringBuilder对象的append方法添加并在最后使用toString方法生成成String对象
(在这种情况下就需要在循环内部避免使用+操作符,用append取代)

4.StringBuilder提供了丰富而全面的方法,包括insert()、repleace()、substring()甚至reverse(),但是最常用的还是append()和toString()。还有delete方法,参数是字符索引。
PS:这里注意StringBuilder是Java SE5引入的,在这之前Java用的是StringBuffer。候着是线程安全的,因此开销会大些。

5.如果想在toString()方法中返回this(想要打印出对象的内存地址时),如果代码如下:
  public String toString(){
       return "ssss"+this;
  }
则会产生编译器报错,此时因为+操作符,编译器会将this转换为String类型,如此又调用了toString()方法,产生死循环。若想要输出对象内存地址,应该调用Object.toString()方法,也就是不该使用this,而是应该调用super.toString()方法

6.重复强调1中声明:当需要改变字符串的内容时,String类的方法都会返回一个新的String对象。同时如果内容没有发生改变,String的方法只是返回指向原对象的引用而已。

格式化输出:
7.Java SE5 引入的format方法可用于PrintStream或PrintWriter对象,其中也包括System.out对象。模仿的是C语言的printf()

8.在Java中所有新的格式化功能都由java.util.Formatter类处理。可以讲Formatter看作一个翻译器,构造器经过重载可以接受多种输出目的地,最常用的是PrintStream(),OutputStream和File
   Formatter f = new Formatter(System.out);
   f.format("%s hahaha %d","My name is",5);
//My name is hahaha 5

9.格式化说明符:
%[argument_index$][flags][width][.precision]conversion
其中需要注意的是:最小尺寸由width实现(默认右对齐,不过可以通过使用-width改变方向)

10.String.format()接受与Formatter.format()方法一样的参数,但返回一个String对象

正则表达式:
11.需要注意的是,在其他语言中,\\表示“我想要在正则表达式中插入一个普通的(字面上的)反斜线,请不要给它任何特殊的意义。”而在Java中,\\的意思是“我要插入一个正则表达式的反斜线”(就会转义,无义变有义,如\\d,有义变无义,如\\+)。如果想要一个普通的反斜线,则应该\\\\。但是换行和制表符之类的只需要使用单反斜线:\n\t。

12.量词:略

13.接口CharSequencecong CharBuffer、String、StringBuffer、StringBuilder类之中抽象出了字符序列的一般化定义,这些类都实现了该接口:
interface CharSequence{
    charAt(int i);
    length();
    subSequence(int start,int end);
    toString();
}

14.Java中与正则表达式有关的类有:Scanner、Pattern、Match、String

15.String.matchs()用于正则完整的匹配字符串,返回true或者false;其还自带了split()方法用于分割以及replaceXX系列的方法用以正则替换。

16.Pattern和Matcher:static Pattern.compile(String regex) 编译生成Pattern对象,调用Pattern对象的matcher(内容)方法。

Matcher常用拥有的方法:
find()、group()、start()、end():
   while (m.find()) {
        System.out.println(m.group()+" at position "+m.start()+" - "+m.end());
   }
matches()判断整个字符串匹配,lookingAt()判断字符串的始部分是否能够匹配
reset(内容)将现有Matcher对象应用于一个新的字符序列(可以被用来做动态匹配,每次reset(新内容))

Pattern常用的方法:
static boolean matches(String regex, CharSequence input) 同上
split()
compile(String regex,int flag):其中
flag的取值范围如下:
Pattern.CANON_EQ,当且仅当两个字符的"正规分解(canonical decomposition)"都完全相同的情况下,才认定匹配。比如用了这个标志之后,表达式"a\u030A"会匹配"?"。默认情况下,不考虑"规范相等性(canonical equivalence)"。

Pattern.CASE_INSENSITIVE(?i) 默认情况下,大小写不敏感的匹配只适用于US-ASCII字符集。这个标志能让表达式忽略大小写进行匹配。要想对Unicode字符进行大小不明感的匹 配,只要将UNICODE_CASE与这个标志合起来就行了。

Pattern.COMMENTS(?x) 在这种模式下,匹配时会忽略(正则表达式里的)空格字符(不是指表达式里的"\\s",而是指表达式里的空格,tab,回车之类)。注释从#开始,一直到这行结束。可以通过嵌入式的标志来启用Unix行模式。

Pattern.DOTALL(?s) 在这种模式下,表达式'.'可以匹配任意字符,包括表示一行的结束符。默认情况下,表达式'.'不匹配行的结束符。

Pattern.MULTILINE(?m)在这种模式下,'^'和'$'分别匹配一行的开始和结束。此外,'^'仍然匹配字符串的开始,'$'也匹配字符串的结束。默认情况下,这两个表达式仅仅匹配字符串的开始和结束。

Pattern.UNICODE_CASE(?u) 在这个模式下,如果你还启用了CASE_INSENSITIVE标志,那么它会对Unicode字符进行大小写不明感的匹配。默认情况下,大小写不敏感的匹配只适用于US-ASCII字符集。

Pattern.UNIX_LINES(?d) 在这个模式下,只有'\n'才被认作一行的中止,并且与'.','^',以及'$'进行匹配。

17.Scanner的构造器可以接受任何类型的输入对象,包括File、InputStream、String、或者Readable(Java SE5新接口,表示’具有read()方法的某种东西‘,如BufferedReader)。

18.有了Scanner,所有的输入、分词以及翻译的操作都隐藏在不同类型的nextXXX方法中。普通的next方法返回下一个String。所有基本类型(除char外)都有对应的next方法。所有的next方法,只有在找到一个完整的分词后才会返回。
PS:需要注意的是,Scanner的使用并不需要添加Try区,因为Scanner有一个假设,在输入结束时会抛出IOException,所以Scanner会把IOException吞掉。通过ioException()方法可以找到最近发生的异常。

19.默认情况下,Scanner根据空白字符对输入进行分词。也可以Scanner使用useDelimiter()来设置定界符(支持正则),同时还有一个delimiter()方法返回当前正在作为定界符使用的Pattern对象

20.Scanner的next()方法配合正则是,会找到下一个匹配该模式的输入部分。

0 0