美文网首页程序员Android知识我爱编程
『读书笔记』—— Thinking_In_Java_13_字符串

『读书笔记』—— Thinking_In_Java_13_字符串

作者: 下位子 | 来源:发表于2018-04-15 16:55 被阅读129次

Thinking in java 读书笔记系列

不可变 String

String 对象是不可变的,具有只读特性。

public class Immutable {  
    public static String upcase(String s) {  
        return s.toUpperCase();  
    }  
  
    public static void main(String[] args) {  
        String q = "howdy";  
        print(q); // howdy  
        String qq = upcase(q);  
        print(qq); // HOWDY  
        print(q); // howdy(原有 String 没有改变)  
    }  
}  
// out:
// howdy  
// HOWDY  
// howdy 

重载运算符 + 与 StringBuilder

重载

一个操作符在应用于特定的类时, 被赋予特殊意义。用于String 的 + 和 += 是java中仅有的两个重载过的操作符,而java 并不允许程序员重载任何操作符

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

字符串连接符 + 的性能非常低下。。因为为了生成最终的 string, 会产生大量需要垃圾回收的中间对象.

通过 javap 来反编译 Concatenation

Compiled from "Concatenation.java"  
public class chapter13.Concatenation {  
  public chapter13.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  
}  

编译器自动引入了 java.lang.StringBuilder 类,即使源代码中没有使用 StringBuilder, 但是显然StringBuilder 更加有效。

编译器对 String 的优化

// 利用 StringBuilder.append() 来重载 + 运算符  
public class WhitherStringBuilder {  
    public String implicit(String[] fields) { // 方法一:使用多个String对象  
        String result = "";  
        for (int i = 0; i < fields.length; i++) // (效率低)隐式创建 StringBuilder  
            result += fields[i];   
        return result;  
    } // 因为 StringBuilder是在循环内创建的,这意味着 每经过循环一次,就会创建一个新的 StringBuilder对象  
  
  
    public String explicit(String[] fields) {  // 方法二:使用StringBuilder,因为效率高  
        StringBuilder result = new StringBuilder(); // (效率高)显式创建 StringBuilder  
        for (int i = 0; i < fields.length; i++)  
            result.append(fields[i]);  
        return result.toString();  
    }  
}

StringBuilder 其他特点

可以为StringBuilder 预先指定大小,如果知道最终的字符串长度,可以预先指定StringBuilder的大小, 以避免多次 重新分配缓冲。

如果要在toString() 方法中使用循环的话,最好自己创建一个StringBuidler 对象。

insert, replace, substring, reverse, 最常用的方法是 appendtoString() 方法。

StringBuilder 线程不安全,效率高, StringBuffer 线程安全,效率低。

无意识的递归

所有java的根基类都是 Object, 所以容器类都有 toString() 方法。 容器的toString() 方法都能够表达容器自身和容器所包含的 对象。

// 无限递归 使得 java虚拟机栈被顶满, 然后抛出异常  
public class InfiniteRecursion {  
    @Override  
    public String toString() {  
         // toString() 中的this关键字是 引起无限递归的原因  
//      return " InfiniteRecursion address: " + this + "\n"; // Exception in thread "main" java.lang.StackOverflowError  
        return " InfiniteRecursion address: " + super.toString() + "\n";  
    }  
  
    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);  
    }  
}    
/* 
[ InfiniteRecursion address: chapter13.InfiniteRecursion@15db9742 
,  InfiniteRecursion address: chapter13.InfiniteRecursion@6d06d69c 
,  InfiniteRecursion address: chapter13.InfiniteRecursion@7852e922 
,  InfiniteRecursion address: chapter13.InfiniteRecursion@4e25154f 
,  InfiniteRecursion address: chapter13.InfiniteRecursion@70dea4e 
,  InfiniteRecursion address: chapter13.InfiniteRecursion@5c647e05 
,  InfiniteRecursion address: chapter13.InfiniteRecursion@33909752 
,  InfiniteRecursion address: chapter13.InfiniteRecursion@55f96302 
,  InfiniteRecursion address: chapter13.InfiniteRecursion@3d4eac69 
,  InfiniteRecursion address: chapter13.InfiniteRecursion@42a57993 
] 
*/

这里发生了自动类型转换:InfiniteRecursion类型转换为 String 类型。 this前面的是字符串,后面是换行符, 所以 this 转换为 String, 即调用了 this.toString() 方法, 于是就发生了 递归调用 toString() 方法,无限递归使得 java 虚拟机栈被顶满; 然后抛出异常;把this换做 super.toString() 方法后 执行成功

String 上的操作

String 对象的基本方法

String的基本方法.png String基本方法.png

当需要改变字符串的内容, String 类的方法都会返回一个新的 String 对象,如果没有改变,则返回原对象的引用。

格式化输出

通过 System.out.format() 输出格式。


// System.out.format() 输出格式  
public class SimpleFormat {  
    public static void main(String[] args) {  
        int x = 5;  
        double y = 5.332542;  
        // The old way:  
        System.out.println("Row 1: [" + x + " " + y + "]");  
        // The new way:  
        System.out.format("Row 1: [%d %f]\n", x, y); // format() 方法的荔枝  
        // or  
        System.out.printf("Row 1: [%d %f]\n", x, y); // printf() 方法荔枝  
    }  
}    
/* 
Row 1: [5 5.332542] 
Row 1: [5 5.332542] 
Row 1: [5 5.332542] 
*/ 

具体转换字符格式

字符 含义
d 整数型(10进制)
c Unicode 字符
b Boolean 值
s String
f 浮点数
x 整数(16进制)
h 散列码(16进制)
% 字符 % 或类型转换字符前缀

具体例子:

/* Formatter 对各种数据类型转换的荔枝 */  
public class Conversion {  
    public static void main(String[] args) {  
        Formatter f = new Formatter(System.out);  
  
        char u = 'a';    
        System.out.println("u = 'a'"); // u = 'a'  
          
        f.format("%%s: %s\n", u); // %s: a  
          
        f.format("%%c: %c\n", u); // %c: a  
        f.format("%%b: %b\n", u); // %b: true  
        f.format("%%h: %h\n", u); // %h: 61  
//       f.format("d: %d\n", u); //  java.util.IllegalFormatConversionException: d != java.lang.Character  
//       f.format("f: %f\n", u); // java.util.IllegalFormatConversionException: f != java.lang.Character  
//       f.format("e: %e\n", u); // java.util.IllegalFormatConversionException: e != java.lang.Character  
//       f.format("x: %x\n", u); // java.util.IllegalFormatConversionException: x != java.lang.Character  
  
        int v = 121;  
        System.out.println();  
        System.out.println("v = 121"); // v = 121  
          
        f.format("%%d: %d\n", v); // %d: 121  
        f.format("%%c: %c\n", v); // %c: y  
        f.format("%%b: %b\n", v); // %b: true   
        f.format("%%s: %s\n", v); // %s: 121  
        f.format("%%x: %x\n", v); // %x: 79  
        f.format("%%h: %h\n", v); // %h: 79   
//       f.format("f: %f\n", v); // java.util.IllegalFormatConversionException: f != java.lang.Integer  
//       f.format("e: %e\n", v); // java.util.IllegalFormatConversionException: e != java.lang.Integer  
  
        BigInteger w = new BigInteger("50000000000000");  
        System.out.println();  
        System.out.println("w = new BigInteger(\"50000000000000\")"); // w = new BigInteger("50000000000000")  
        f.format("%%d: %d\n", w); // %d: 50000000000000  
        f.format("%%b: %b\n", w); // %b: true  
        f.format("%%s: %s\n", w); // %s: 50000000000000  
        f.format("%%x: %x\n", w); // %x: 2d79883d2000  
        f.format("%%h: %h\n", w); // %h: 8842a1a7  
//       f.format("c: %c\n", w); // java.util.IllegalFormatConversionException: c != java.math.BigInteger  
//       f.format("f: %f\n", w); // java.util.IllegalFormatConversionException: f != java.math.BigInteger  
//       f.format("e: %e\n", w); // java.util.IllegalFormatConversionException: e != java.math.BigInteger  
  
        double x = 179.543;  
        System.out.println();  
        System.out.println("x = 179.543"); // x = 179.543  
        f.format("%%b: %b\n", x); // %b: true  
        f.format("%%s: %s\n", x); // %s: 179.543  
        f.format("%%f: %f\n", x); // %f: 179.543000  
        f.format("%%e: %e\n", x); //%e: 1.795430e+02, 科学表示法  
        f.format("%%h: %h\n", x); // %h: 1ef462c  
//       f.format("d: %d\n", x); // java.util.IllegalFormatConversionException: d != java.lang.Double  
//       f.format("c: %c\n", x); // java.util.IllegalFormatConversionException: c != java.lang.Double  
//       f.format("x: %x\n", x); // java.util.IllegalFormatConversionException: x != java.lang.Double  
  
        Conversion y = new Conversion();  
        System.out.println();  
        System.out.println("y = new Conversion()"); // y = new Conversion()  
        f.format("%%b: %b\n", y); // %b: true  
        f.format("%%s: %s\n", y); // %s: chapter13.Conversion@4aa298b7  
        f.format("%%h: %h\n", y); // %h: 4aa298b7  
//       f.format("d: %d\n", y); // java.util.IllegalFormatConversionException: d != chapter13.Conversion  
//       f.format("c: %c\n", y); // java.util.IllegalFormatConversionException: c != chapter13.Conversion  
//       f.format("f: %f\n", y); // java.util.IllegalFormatConversionException: f != chapter13.Conversion  
//       f.format("e: %e\n", y); // java.util.IllegalFormatConversionException: e != chapter13.Conversion  
//       f.format("x: %x\n", y); // java.util.IllegalFormatConversionException: x != chapter13.Conversion  
  
        boolean z = false;  
        System.out.println();  
        System.out.println("z = false"); // z = false  
        f.format("%%b: %b\n", z); // %b: false  
        f.format("%%s: %s\n", z); // %s: false  
        f.format("%%h: %h\n", z); // %h: 4d5  
//       f.format("d: %d\n", z); // java.util.IllegalFormatConversionException: d != java.lang.Boolean  
//       f.format("c: %c\n", z); // java.util.IllegalFormatConversionException: c != java.lang.Boolean  
//       f.format("f: %f\n", z); // java.util.IllegalFormatConversionException: f != java.lang.Boolean  
//       f.format("e: %e\n", z); // java.util.IllegalFormatConversionException: e != java.lang.Boolean  
//       f.format("x: %x\n", z); // java.util.IllegalFormatConversionException: x != java.lang.Boolean  
    }  
} 

正则表达式

要想学好正则表达式,基本上要记住的就是一堆语法。

废话不多说,直接看如何创建正则表达式。

字符

正则表达式 说明
B 指定字符B
\xhh 十六进制值为0xhh的字符
\uhhhh 十六进制表示为0xhhhh的Unicode字符
\t 制表符Tab
\n 换行符
\r 回车
\f 换页
\e 转义

字符类

正则表达式 说明
. 任意字符
[abc] 包含a、b和c的任何字符
[^abc] 除了a、b和c之外的任何字符(否定)
[a-zA-Z] 从a到z或从A到Z的任何字符(范围)
[abc[hij]] 任意abchij字符
[a-z&&[hij]] 任意h、i或j(相交)
\s 空白符(空格、tab、换行和回车)
\S 非空白符
\d 数字[0-9]
\D 非数字[^0-9]
\w 词字符([a-zA-Z0-9])
\W 非词字符

逻辑操作符

正则表达式 说明
XY Y跟在后面
X|Y X或Y
(X) 捕获组

量词

贪婪型 勉强型 占有型 如何匹配
X? X?? X?+ 一个或零个X
X* X*? X*+ 零个或多个X
X+ X+? X++ 一个或多个X
X{n} x{n}? X{n}+ 恰好n次X
X{n,} X{n,}? X{n,}+ 至少n次X
X{n,m} X{n,m}? X{n,m}+ X至少n次,且不超过m次

表达式 X 通常必须用圆括号括起来。

Pattern 和 Matcher

  1. Pattern.compile(regex) 编译 regex 并产生 Pattern 对象。
  2. Pattern.matcher(检索的字符串)生成一个 Matcher 对象。

基本用法

public class TestRegularExpression {  
    public static void main(String[] args) {  
        String[] array = {"aabbcc", "aab", "aab+", "(b+)"};  
          
        for (String arg : array) {  
            System.out.println();  
            print("Regular expression: \"" + arg + "\"");  
            Pattern p = Pattern.compile(arg); // step1: Pattern 表示编译后的匹配模型Pattern.(编译后的正则表达式)  
            Matcher m = p.matcher("aabbcc"); // step2: 模型实例 检索 待匹配字符串并 生成一个匹配对象Matcher, Matcher有很多方法  
            while (m.find()) {  
                print("Match \"" + m.group() // 待匹配的字符串  
                                 + "\" at positions "   
                                 + m.start() // 字符串匹配regex的起始位置  
                                 + "-" + (m.end() - 1)); // 字符串匹配regex的终点位置  
            }  
        }  
    }  
} 

Matcher 方法

boolean matches(); //判断 输入字符串 是否匹配正则表达式regex;  
boolean lookingAt(); //判断输入字符串(不是整个)的开始部分是否匹配 regex;  
boolean find(); //用于 在 CharSequence 输入字符串中查找多个匹配;  
boolean find(int start);  //用于在 CharSequence 输入字符串的start 位置开始查找多个匹配;  
String group(); //用于返回匹配regex的输入字符串的子串;

常用的 Pattern 标记

public static Pattern compile(String regex, int flags) {  
    return new Pattern(regex, flags);  
} 
// Pattern.CASE_INSENSITIVE:  不区分大小写;
// Pattern.MULTILINE: 允许多行,即不以换行字符作为分隔符;
// Pattern.COMMENTS: 模式中允许空格和注释, 不以空格和注释作为分隔符

Pattern.split()

将字符串分割成符合 regex 的字符数组。

public class SplitDemo {  
    public static void main(String[] args) {  
        String input = "This!!unusual use!!of exclamation!!points";  
        print(Arrays.toString(Pattern.compile("!!").split(input))); // split(input, 0); 对匹配次数不做任何限制  
  
        /* (只匹配前2个 !! ) */  
        /* 注意:分割边界在分割结果中被删除 */  
        print(Arrays.toString(Pattern.compile("!!").split(input, 3))); // 限定匹配次数,limit限制将输入字符串分割成数组的数组大小  
    }  
}    
/* 
[This, unusual use, of exclamation, points] 
[This, unusual use, of exclamation!!points] 
*/  

扫描输入

使用 BufferedReader 实现。

public class SimpleRead {  
    public static BufferedReader input = new BufferedReader(new StringReader(  
            "Sir Robin of Camelot\n22 1.61803"));  
  
    public static void main(String[] args) {  
        try {  
            System.out.println("\n1.What is your name?");  
            String name = input.readLine();  
            System.out.println(name); // Sir Robin of Camelot  
              
            System.out.println("\n2.input: <age> <double>");  
            String numbers = input.readLine();  
            System.out.println("input.readLine() = " + numbers); // 22 1.61803  
              
            String[] numArray = numbers.split(" ");  
            int age = Integer.parseInt(numArray[0]); // 22  
            double favorite = Double.parseDouble(numArray[1]); // 1.61803  
              
            System.out.format("Hi %s.\n", name);  
            System.out.format("In 5 years you will be %d.\n", age + 5);  
            System.out.format("My favorite double is %f.", favorite / 2);  
        } catch (IOException e) {  
            System.err.println("I/O exception");  
        }  
    }  
}  
// output:
/* 
 1.What is your name? 
Sir Robin of Camelot 
 
2.input: <age> <double> 
input.readLine() = 22 1.61803 
Hi Sir Robin of Camelot. 
In 5 years you will be 27. 
My favorite double is 0.809015. 
 */ 

使用 Scanner 实现

public class BetterRead {  
    public static void main(String[] args) {  
        // Scanner 可以接受任何类型的 Readable 输入对象  
        Scanner stdin = new Scanner(SimpleRead.input);  
        System.out.println("What is your name?");  
        // 所有的输入,分词以及翻译的操作都隐藏在不同类型的 next 方法 中.  
        String name = stdin.nextLine(); // nextLine() 返回 String  
        System.out.println(name);  
          
        System.out.println("How old are you? What is your favorite double?");  
        System.out.println("(input: <age> <double>)");  
          
        // Scanner 直接读入 integer 和 double 类型数据  
        int age = stdin.nextInt();  
        double favorite = stdin.nextDouble();  
        System.out.println(age);  
        System.out.println(favorite);  
          
        System.out.format("Hi %s.\n", name);  
        System.out.format("In 5 years you will be %d.\n", age + 5);  
        System.out.format("My favorite double is %f.", favorite / 2);  
    }  
} 
// output:
/* 
What is your name? 
Sir Robin of Camelot 
How old are you? What is your favorite double? 
(input: <age> <double>) 
22 
1.61803 
Hi Sir Robin of Camelot. 
In 5 years you will be 27. 
My favorite double is 0.809015. 

总结

过去, Java 对字符串操作的支持相当不完善。不过随着近几个版本的升级,我们可以看到, Java 已经从其他语言中吸取了许多成熟的经验。到目前为止,它对字符串操作的支持已经很完善了。不过,有时你还要在细节上注意效率的问题,例如恰当地使用 StringBuilder 等。

感谢

thinking-in-java(13) String字符串

相关文章

  • 『读书笔记』—— Thinking_In_Java_13_字符串

    Thinking in java 读书笔记系列 不可变 String String 对象是不可变的,具有只读特性。...

  • 《Redis实战》—读书笔记—第3章

    《Redis实战》—读书笔记—第3章 2017年10月04日00:50:03 3.1 字符串 字符串可以存储3种类...

  • redis设计与实现读书笔记

    参考文档:redis设计与实现读书笔记 第二版 一、数据结构和对象 1.关于字符串 redis底层存储字符串结构为...

  • php7字符串底层实现

    本篇为《php7底层设计与源码实现》的读书笔记php7 的字符串以zend_string 为载体,实现字符串的功能...

  • 明解C语言 读书笔记

    明解C语言读书笔记 1.puts 可按顺序输出作为实参的字符串,并在结尾换行,puts(“……”)与printf(...

  • 读书笔记-字符串,数组

    字符串String 字符串用' '或" "括起来的字符表示如果字符串内部既包含'又包含"怎么办?可以用转义字符\来...

  • Javascript知识点整合

    字符串 单行字符串: ‘字符串’或“字符串” 多行字符串: `多行字符串` 字符串操作: 字符串连接‘+’号 长度...

  • 正则表达式

    最近学习正则表达式,留下读书笔记,以备查阅。 作用 数据验证,例如验证一串字符串中是都包含手机号码、信用卡号码等;...

  • C++基础字符串

    字符串的构造 字符串特性描述 字符操作 字符串赋值 字符串连接 字符串比较 字符串查找 字符串替换 字符串删除 字...

  • iOS中的NSString与NSMutableString

    字符串的创建 字符串读写 字符串的比较 字符串的搜索 字符串截取 字符串替换 字符串与路径 字符串转换 NSMut...

网友评论

    本文标题:『读书笔记』—— Thinking_In_Java_13_字符串

    本文链接:https://www.haomeiwen.com/subject/duadkftx.html