只想把基础打好-String

下面说一些平时可能不太注意的知识点:

  1. String 对象具有只读特性,所以指向它的任何引用都不可能改变它的值,因此不会其它的引用有什么影响。String的"+"与"+="是java中仅有的两年重载过的操作符,而java并不允许程序员重载任何操作符。

  2. String的优化
    由于String的不可改变的特性,每次改变都会生成一个新的String对象。那如果我们会使用String的重载运算符"+"拼接字符串时不是会生成大量的中间对象?java设计师一开始也是这样做的(这也是软件设计中的一个教训:除非你用代码将系统实现,并将它动起来,否则你无法真正了解它会有什么问题),然后他们发现其性能相当糟糕。

public class Concatenation {
    public static void main(String[] args){
    String mango="mango";
    String s="abc"+mango+"def"+45;
    System.out.println(s);
    }
}

然后在class生成目录下使用javap -c Concatenation

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String mango
       2: astore_1
       3: new           #3                  // class java/lang/StringBuilder
       6: dup
       7: invokespecial #4                  // Method java/lang/StringBuilder."<
init>":()V
      10: ldc           #5                  // String abc
      12: invokevirtual #6                  // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      15: aload_1
      16: invokevirtual #6                  // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: ldc           #7                  // String def
      21: invokevirtual #6                  // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      24: bipush        45
      26: invokevirtual #8                  // Method java/lang/StringBuilder.ap
pend:(I)Ljava/lang/StringBuilder;
      29: invokevirtual #9                  // Method java/lang/StringBuilder.to
String:()Ljava/lang/String;
      32: astore_2
      33: getstatic     #10                 // Field java/lang/System.out:Ljava/
io/PrintStream;
      36: aload_2
      37: invokevirtual #11                 // Method java/io/PrintStream.printl
n:(Ljava/lang/String;)V
      40: return
}

其中dup与invokevirtural语句相当于java虚拟机上的汇编语言。从上可以看出编译器自动引用了StringBuilder类,使得那句代码更高效。但是编译器为我们优化的程序有限。随时可以用javap来分析你的程序。
当你为一个类重写toString时如果字符串比较简单那么可以依赖编译器,否则使用StringBuilder更高效。
3.无意识的递归:当重写一个类的的toString的时候,如果你希望打印对象的内存地址不要使用this关键字,比如:

@Override
    public String toString() {
        return "*** address:"+this;
    }

当调用this的时候又会调用这个方法的toString方法,所以在这里我们应该使用super.toString()的方法。

  1. String的matches方法可以进行正则表达式判断:
System.out.println("-3478".matches("-?\\d+"));

输出结果:

true

一般说来,比起功能有限的String类,我们更愿意使用Pattern和Matcher对象。

  1. Scanner定界符
    在默认情况下,Scanner根据空白字符对输入进行分词,但是你可以用正则表达式指定自己所需的定界符。
public class ScannerDelimiter {
    public static void main(String[] args) {
        Scanner scanner = new Scanner("23,5,6,78,23");
        scanner.useDelimiter("\\s*,\\s*");
        while (scanner.hasNext()) {
            System.out.println(scanner.nextInt());
        }
    }
}

运行结果:

23
5
6
78
23

在java引入正则表达式和Scanner类之前,分割字符串的唯一方法是使用StringTokenier来分词。不过,现在有了正则表式和Scanner,我们可以使用更加简单,更加简洁的方式来完成同样的工作了。基本上StringTokenizer已经弃用了。

推荐阅读更多精彩内容