Thinking in java Chapter13字符串

java的基本理念是"结构不佳的代码不能够运行"

发现错误的理想时机是编译阶段,然而,编译期间并不能找出所有的错误,余下的问题必须在运行时期解决。

1 不变的字符串

  1. 字符串不变; 它们的值在创建后不能被更改。 字符串缓冲区支持可变字符串。 因为String对象是不可变的,它们可以被共享

  2. String对象是不可变的,每当把String对象作为方法的参数传递时,都会复制一份引用.该引用所指的对象其实一直待在单一的物理位置上,从未动过.

  3. 给String对象赋值本质上是改变该String对象引用的指向.
package strings;

public class Immutable {
    public static String upcase(String s){
        return s.toUpperCase();
    }

    public static void main(String[] args) {
        String q = "howdy";
        System.out.println(q);
        String qq = upcase(q);
        System.out.println(qq);
        System.out.println(q);
    }
}
/*
howdy
HOWDY
howdy
 */

2 重载 " + " 与StringBuilder

  1. String对象是不可变的,你可以给一个String对象加任意多的别名.因为String对象具有只读特性,所以指向它的任何引用都不可能改变它的值,因此,也就不会对其它引用有什么影响

2.不可变性会带来一定的效率问题,为String对象重载的"+"操作符就是一个例子.//此种方式每+一次就会多一个String对象

  1. String的"+"操作经过编译器优化后是利用的StringBuilder对字符串进行拼接,性能不如直接使用StringBuilder拼接字符串要好.
    如下例子中,StringBuilder是在循环体内构造的,意味着每经过循环,都会创建一个新的StringBuilder对象。
package strings;

public class Concatenation {
    public static void main(String[] args) {
        String mango = "mango";
        String s = "abc" +mango + "def" +47;
        System.out.println(s);
    }
}
javap -c Concatenation.class 
Compiled from "Concatenation.java"
public class strings.Concatenation {
  public strings.Concatenation();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  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.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      15: aload_1
      16: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: ldc           #7                  // String def
      21: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      24: bipush        47
      26: invokevirtual #8                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      29: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()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.println:(Ljava/lang/String;)V
      40: return
}
package strings;

public class WhitherStringBuilder {
    public String implicit(String[] fields){
        String result = "";
        for (int i = 0; i < fields.length; i ++)
            result += fields[i];
        return result;
    }
    public String explicit(String[] fields){
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < fields.length; i++)
            result.append(fields[i]);
        return result.toString();
    }

    public static void main(String[] args) {

    }
}


javap -c WhitherStringBuilder.class 
Compiled from "WhitherStringBuilder.java"
public class strings.WhitherStringBuilder {
  public strings.WhitherStringBuilder();
    Code:
       0: aload_0   // 将this引用推送至栈顶,即压入栈      
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public java.lang.String implicit(java.lang.String[]);
    Code:
       0: ldc           #2   //将编号为#2的字符串推送至栈顶               // String 
       2: astore_2      //将栈顶引用类型值存入第三个本地变量
       3: iconst_0      //将int型0推送至栈顶
       4: istore_3      //将栈顶int型数值存入第四个本地变量
       5: iload_3       //将第4个引用本地变量(即数组引用)压入栈顶
       6: aload_1       //将第2个int本地变量压入栈顶
       7: arraylength   //获得数组的长度并压入栈顶 
       8: if_icmpge     38      //比较栈顶两int数值的大小如果小于0跳转到第38行
      11: new           #3                  // class java/lang/StringBuilder 新建一个StringBuilder对象编号#3,并将其引用压入栈顶
      14: dup                               //复制栈顶数值(在这里是引用)并将复制数值压入栈顶
      15: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V //调用超类构造方法,实例初始化方法,私有方法
      18: aload_2                           //将第三个本地引用推送至栈顶
      19: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;//调用实例化方法append
      22: aload_1                           //将第二个引用本地变量(即数组引用)压入栈顶
      23: iload_3                           //将第四个int本地变量压入栈顶
      24: aaload                            //将引用数组指定索引的值推送至栈顶(即二个本地引用变量所代表的数组的下标为第四个int本地变量的值)
      25: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;//调用实例化方法append
      28: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      31: astore_2                          //将栈顶引用类型值存入第三个本地变量
      32: iinc          3, 1                //将第4个int本地变量加1
      35: goto          5                   //无条件跳转到5行
      38: aload_2                           //将第二个本地引用类型的本地变量推送至栈顶
      39: areturn                           //返回栈顶引用型本地变量,并退出方法

  public java.lang.String explicit(java.lang.String[]);
    Code:
       0: new           #3                  // class java/lang/StringBuilder
       3: dup
       4: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
       7: astore_2
       8: iconst_0
       9: istore_3
      10: iload_3
      11: aload_1
      12: arraylength
      13: if_icmpge     30
      16: aload_2
      17: aload_1
      18: iload_3
      19: aaload
      20: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      23: pop
      24: iinc          3, 1
      27: goto          10
      30: aload_2
      31: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      34: areturn

  public static void main(java.lang.String[]);
    Code:
       0: return
}

toString 字符串操作简单,则信赖编译器
若使用循环,则自己创建StringBuilder

package strings;

import java.util.Random;

public class UsingStringBuilder {
    public static Random rand = new Random(47);
    public String toString(){
        StringBuilder result = new StringBuilder("[");
        for (int i = 0;i < 25; i++){
            result.append(rand.nextInt(100));
            result.append(", ");
        }
        result.delete(result.length()-2,result.length());// 删除最后的", "
        result.append("]");
        return result.toString();
    }

    public static void main(String[] args) {
        UsingStringBuilder usb = new UsingStringBuilder();
        System.out.println(usb);
    }
}
/*
[58, 55, 93, 61, 61, 29, 68, 0, 22, 7, 88, 28, 51, 89, 9, 78, 98, 61, 20, 58, 16, 40, 11, 22, 4]
 */

3 无意识的递归

1.容器类都有toString()方法,它生成的结果可以表达容器类自身,以及容器所包含的对象.

2.如果想打印对象的内存地址,在toString()方法中不能使用this关键字的返回字符串,要返回String对象,this关键字会调用toString()方法,从而会产生递归调用 this.toString()->this.toString() ...

要想正确打印地址,必须调用Object.toString()方法

package strings;

import java.util.ArrayList;
import java.util.List;

public class InfiniteRecursion {
    public String toString(){
//        return this.toString(); //这里会递归调用
        return super.toString(); //正确的方法是 调用Object的toString()方法
    }

    public static void main(String[] args) {
        List<InfiniteRecursion> v = new ArrayList<InfiniteRecursion>();
        for (int i = 0; i < 10; i++)
            v.add(new InfiniteRecursion());
        System.out.println(v);
    }
}

/*
[strings.InfiniteRecursion@5e481248, strings.InfiniteRecursion@66d3c617, strings.InfiniteRecursion@63947c6b, strings.InfiniteRecursion@2b193f2d, strings.InfiniteRecursion@355da254, strings.InfiniteRecursion@4dc63996, strings.InfiniteRecursion@d716361, strings.InfiniteRecursion@6ff3c5b5, strings.InfiniteRecursion@3764951d, strings.InfiniteRecursion@4b1210ee]

 */

4 String 上的操作

需要改变字符串内容时,String类的方法都会返回一个新的String对象
如果内容不改变,只是返回指向原对象的引用而已。

可以节约存储空间以及避免额外开销

5 格式化输出

5.1 printf

5.2 format()

format()方法模仿自printf(), 可用于PrintStream或PrintWrter对象,其中也包括System.out对象

5.3 Formatter类

在java中所有新的格式化功能都由java.util.Formatter类处理,可以将Formatter看做是一个翻译器,它将你的格式化字符串与数据翻译成需要的结果,formatter的构造器器经过重载可以接受多种输出的目的地

package strings;

import java.io.PrintStream;
import java.util.Formatter;

public class Turtle {
    private String name;
    private Formatter f;

    public Turtle(String name, Formatter f) {
        this.name = name;
        this.f = f;
    }

    public void move(int x, int y) {
        f.format("%s The Turtle is at (%d,%d)\n", name, x, y);
    }

    public static void main(String[] args) {
        PrintStream outAlias = System.out;
        PrintStream outAliasErr = System.err;
        Turtle tommy = new Turtle("Tommy", //所有的tommy都将输出到System.out
                new Formatter(System.out));
        Turtle terry = new Turtle("Terry", //所有的Terry都将输出到System.out一个别名中, 这种最常用
                new Formatter(outAlias));
        Turtle teddy = new Turtle("teddy", //所有的Terry都将输出到System.err一个别名中, 这种最常用
                new Formatter(outAliasErr));
        tommy.move(0, 0);
        terry.move(4, 8);
        tommy.move(3, 4);
        terry.move(2, 5);
        tommy.move(3, 3);
        terry.move(3, 3);
        teddy.move(5,5);
    }
}
/*
Tommy The Turtle is at (0,0)
Terry The Turtle is at (4,8)
Tommy The Turtle is at (3,4)
Terry The Turtle is at (2,5)
Tommy The Turtle is at (3,3)
Terry The Turtle is at (3,3)
teddy The Turtle is at (5,5)
 */

5.4格式化说明符

Formatter类格式化抽象语法: %[argument_index$][flags][width][.precision]conversion

  • 用"-"标志来改变对齐方向(默认右对齐),添加了"-"表示左对齐
  • argument_index控制输出顺序 当有多个参数要输出时(比如format("s: %2$s %s\n", "sdffads","fds");)输出结果为s: fds sdffads
  • width: 控制一个域的最小尺寸,
  • flags
  • precision(精度): 用来指明最大尺寸,用于String时,它表示打印String时输出字符的最大数量.用于浮点数时,表示小数显示的位数(默认6位),小数过多则舍入,过少则在尾部补零.用于整数时,会出发异常.
package strings;

import java.util.Formatter;

public class Receipt {
    private double total = 0;

    private Formatter f = new Formatter(System.out);

    public void printTitile(){
        f.format("%-15s %5s %10s\n","Item","Qty","Price");
        f.format("%-15s %5s %10s\n","----","---","-----");

    }
    public void print(String name,int qty,double price){
        f.format("%-15.15s %5d %10.2f\n",name,qty,price);
        total += price;
    }
    public void printTotal(){
        f.format("%-15s %5s %10.2f\n","Tax","",total*0.06);
        f.format("%-15s %5s %10s\n","","","-----");
        f.format("%-15s %5s %10.2f\n","Total","",total*1.06);

    }
    public static void main(String[] args) {
        Receipt receipt = new Receipt();
        receipt.printTitile();
        receipt.print("Jack's Magic Beans",4,4.25);
        receipt.print("Princess Peas",3,5.1);
        receipt.print("Three Bears Porridge",1,14.29);
        receipt.printTotal();
    }
}
/*
Item              Qty      Price
----              ---      -----
Jack's Magic Be     4       4.25
Princess Peas       3       5.10
Three Bears Por     1      14.29
Tax                         1.42
                           -----
Total                      25.06
 */
作业3

package strings.e4;

import java.util.Formatter;

public class Receipt {
    public static final int ITEM_WIDTH = 15;
    public static final int QTY_WIDTH = 5;
    public static final int PRICE_WIDTH = 10;

    private static final String TITLE_FMT =
            "%-"+ITEM_WIDTH +"s %"+ QTY_WIDTH +"s %" +PRICE_WIDTH+"s\n";
    private static final String FMT =
            "%-"+ITEM_WIDTH +".15s %"+ QTY_WIDTH +"d %" +PRICE_WIDTH+".2f\n";
    private static final String TOTAL_FMT =
            "%-"+ITEM_WIDTH +"s %"+ QTY_WIDTH +"s %" +PRICE_WIDTH+".2f\n";

    private double total = 0;

    private Formatter f = new Formatter(System.out);

    public void printTitile() {
        f.format(TITLE_FMT, "Item", "Qty", "Price");
        f.format(TITLE_FMT, "----", "---", "-----");

    }

    public void print(String name, int qty, double price) {
        f.format(FMT, name, qty, price);
        total += price;
    }

    public void printTotal() {
        f.format(TOTAL_FMT, "Tax", "", total * 0.06);
        f.format(TITLE_FMT, "", "", "-----");
        f.format(TOTAL_FMT, "Total", "", total * 1.06);

    }

    public static void main(String[] args) {
        Receipt receipt = new Receipt();
        receipt.printTitile();
        receipt.print("Jack's Magic Beans", 4, 4.25);
        receipt.print("Princess Peas", 3, 5.1);
        receipt.print("Three Bears Porridge", 1, 14.29);
        receipt.printTotal();
    }
}
/*
Item              Qty      Price
----              ---      -----
Jack's Magic Be     4       4.25
Princess Peas       3       5.10
Three Bears Por     1      14.29
Tax                         1.42
                           -----
Total                      25.06
 */

5.5 Formatter转换

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

String.format()内部,它也是创建一个Formatter对象,然后将你传入的参数转给Formatter.

package strings;

public class DatabaseException extends Exception {
    public DatabaseException(int transactionID, int queryID,
                             String message) {
        super(String.format("(t%d, q%d) %s", transactionID,
                queryID, message));
    }
    public static void main(String[] args) {
        try {
            throw new DatabaseException(3, 7, "Write failed");
        } catch(Exception e) {
            System.out.println(e);
        }
    }
}
/*
strings.DatabaseException: (t3, q7) Write failed
 */

一个十六进制转储(dump)工具

package strings;

import net.mindview.util.BinaryFile;

import java.io.File;

public class Hex {
    public static String format(byte[] data) {
        StringBuilder result = new StringBuilder();
        int n = 0;
        for(byte b : data) {
            if(n % 16 == 0)
                result.append(String.format("%05X: ", n));
            result.append(String.format("%02X ", b));
            n++;
            if(n % 16 == 0) result.append("\n");
        }
        result.append("\n");
        return result.toString();
    }
    public static void main(String[] args) throws Exception {
        if(args.length == 0)
            // Test by displaying this class file:
            System.out.println(
                    format(BinaryFile.read("/Users/erin/JavaProject/thinking_in_java_example/target/classes/strings/Hex.class")));
        else
            System.out.println(
                    format(BinaryFile.read(new File(args[0]))));
    }
}
/*
00000: CA FE BA BE 00 00 00 34 00 62 0A 00 05 00 32 07 
00010: 00 33 0A 00 02 00 32 08 00 34 07 00 35 0A 00 36 
00020: 00 37 0A 00 38 00 39 0A 00 02 00 3A 08 00 3B 0A 
*/
作业6
package strings.e6;

import java.util.Locale;

class DataHolder{
    private int i = 5;
    private long l = 55L;
    private float f = 55.2f;
    private double d = 44.22;
//    public static final String FMT = "i: %d , l: %d , f: %.2f , d: %.2f " ;

    public String toString(){
        StringBuilder result = new StringBuilder("DataHolder:\n");
        result.append(String.format(Locale.CHINA,"i: %d\n",i));
        result.append(String.format(Locale.CHINA,"l: %d\n",i));
        return result.toString();
//        return String.format(FMT,i,l,f,d);
    }

}
public class E6 {
    public static void main(String[] args) {
        DataHolder dataHolder = new DataHolder();
        System.out.println(dataHolder);
    }
}

6 正则表达式

6.1基础

1.一般来说正则表达式就是以某种方式来描述字符串,因此你可以说如果一个字符串中含有这些东西,那么它就是我正在找的东西."例如,要找一个数字,它可能有一个负号在最前面,那么你就写一个负号加上一个问号,就像这样: -?

  1. 在Java中使用正则表达式, \ 的意思是要插入一个正则表达式的反斜线, \\ 是插入一个普通的反斜线.

3.要表示"一个或多个之前的表达式",因该使用+,所以,如果要表示"可能有一个负号,后面紧跟一位或多位数字",可以这样: -?\d+

4.应用正则表达式的最简单途径,就是利用String类内建的功能,例如你可以检查一公分String是否匹配如上所述表达式

package strings;

public class IntegerMatch {
    public static void main(String[] args) {
        System.out.println("-1234".matches("-?\\d+"));
        System.out.println("5678".matches("-?\\d+"));
        System.out.println("+911".matches("-?\\d+"));
        System.out.println("+911".matches("(-|\\+|)?\\d+")); //在正则表达式中,括号有着将表达式分组的效果,而竖线则表示或操作
    }
}
/*
true
true
false
true
 */
  1. String还自带了一个非常有用的正则表达式工具-split()方法,其功能是"将字符从正则表达式配备的地方切开.String还有一个重载的版本,它允许你限制字符串分割的次数
package strings;

import java.util.Arrays;

public class Splitting {
    public static String knights =
            "Then, when you have found the shrubbery, you must " +
                    "cut down the mightiest tree in the forest... " +
                    "with... a herring!";
    public static void split(String regex) {
        System.out.println(
                Arrays.toString(knights.split(regex)));
    }
    public static void main(String[] args) {
        split(" "); // Doesn't have to contain regex chars
        split("\\W+"); // Non-word characters
        split("n\\W+"); // 'n' followed by non-word characters
    }
}
/*
[Then,, when, you, have, found, the, shrubbery,, you, must, cut, down, the, mightiest, tree, in, the, forest..., with..., a, herring!]
[Then, when, you, have, found, the, shrubbery, you, must, cut, down, the, mightiest, tree, in, the, forest, with, a, herring]
[The, whe, you have found the shrubbery, you must cut dow, the mightiest tree i, the forest... with... a herring!]
 */

6.Stirng自带的最后一个正则表达式工具是"替换",你可以只替换正则表达式第一个配备的字串,或是替换所有匹配的地方

package strings;

import static net.mindview.util.Print.print;

public class Replacing {
    static String s = Splitting.knights;
    public static void main(String[] args) {
        print("\\\\");
        print(s.replaceFirst("f\\w+","located"));
        print(s.replaceAll("shrubbery|tree|herring","banana"));
    }
}
/*
\\
Then, when you have located the shrubbery, you must cut down the mightiest tree in the forest... with... a herring!
Then, when you have found the banana, you must cut down the mightiest banana in the forest... with... a banana!*/
``

作业9
package strings.e9;

import strings.Splitting;

public class E9 {
static String s = Splitting.knights;

public static void main(String[] args) {
    System.out.println(s.replaceAll("[aeiouAEIOU]", "_"));
    System.out.println(s.replaceAll(
            "(?i)[aeiou]","_"));
}

}
/*
"Then, when you have found the shrubbery, you must " +
"cut down the mightiest tree in the forest... " +
"with... a herring!";
Th_n, wh_n y__ h_v_ f_nd th shr_bb_ry, y__ m_st c_t d_wn th_ m_ght__st tr__ n th f_r_st... w_th... _ h_rr_ng!
*/

```

6.2 创建正则表达式

猜你喜欢

转载自www.cnblogs.com/erinchen/p/11903466.html