『Thinking in Java 读书笔记』—— 13-字符串
字数统计:
3,340 字
|
阅读时长:
17 分
| 本文总阅读量: 次
Thinking in java 读书笔记
不可变 String
String
对象是不可变的,具有只读特性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Immutable { public static String upcase (String s) { return s.toUpperCase(); } public static void main (String[] args) { String q = "howdy" ; print(q); String qq = upcase(q); print(qq); print(q); } }
重载运算符 + 与 StringBuilder 重载
一个操作符在应用于特定的类时, 被赋予特殊意义。用于String
的 + 和 += 是java中仅有的两个重载过的操作符,而java 并不允许程序员重载任何操作符
1 2 3 4 5 6 7 8 9 public class Concatenation { public static void main (String[] args) { String mango = "mango" ; String s = "abc" + mango + "def" + 47 ; System.out.println(s); } }
字符串连接符 + 的性能非常低下。。因为为了生成最终的 string
, 会产生大量需要垃圾回收的中间对象.
通过 javap 来反编译 Concatenation 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 Compiled from "Concatenation.java" public class chapter13 .Concatenation { public chapter13.Concatenation(); Code: 0 : aload_0 1 : invokespecial #1 4 : return public static void main(java.lang.String[]); Code: 0 : ldc #2 2 : astore_1 3 : new #3 6 : dup 7 : invokespecial #4 10 : ldc #5 12 : invokevirtual #6 15 : aload_1 16 : invokevirtual #6 19 : ldc #7 21 : invokevirtual #6 24 : bipush 47 26 : invokevirtual #8 29 : invokevirtual #9 32 : astore_2 33 : getstatic #10 36 : aload_2 37 : invokevirtual #11 40 : return }
编译器自动引入了 java.lang.StringBuilder
类,即使源代码中没有使用 StringBuilder
, 但是显然StringBuilder
更加有效。
编译器对 String 的优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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(); } }
StringBuilder 其他特点 可以为StringBuilder
预先指定大小,如果知道最终的字符串长度,可以预先指定StringBuilder
的大小, 以避免多次 重新分配缓冲。
如果要在toString() 方法中使用循环的话,最好自己创建一个StringBuidler
对象。
insert, replace, substring, reverse, 最常用的方法是 append
和 toString()
方法。
StringBuilder
线程不安全,效率高, StringBuffer
线程安全,效率低。
无意识的递归 所有java的根基类都是 Object, 所以容器类都有 toString()
方法。 容器的toString()
方法都能够表达容器自身和容器所包含的 对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class InfiniteRecursion { @Override public String toString () { 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
类型转换为 String
类型。 this前面的是字符串,后面是换行符, 所以 this
转换为 String
, 即调用了 this.toString()
方法, 于是就发生了 递归调用 toString() 方法 ,无限递归使得 java 虚拟机栈被顶满; 然后抛出异常;把this换做 super.toString() 方法后 执行成功
String 上的操作 String
对象的基本方法
当需要改变字符串的内容, String
类的方法都会返回一个新的 String
对象,如果没有改变,则返回原对象的引用。
格式化输出 通过 System.out.format()
输出格式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class SimpleFormat { public static void main (String[] args) { int x = 5 ; double y = 5.332542 ; System.out.println("Row 1: [" + x + " " + y + "]" ); System.out.format("Row 1: [%d %f]\n" , x, y); System.out.printf("Row 1: [%d %f]\n" , x, y); } }
具体转换字符格式
字符
含义
d
整数型(10进制)
c
Unicode 字符
b
Boolean 值
s
String
f
浮点数
x
整数(16进制)
h
散列码(16进制)
%
字符 % 或类型转换字符前缀
具体例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 public class Conversion { public static void main (String[] args) { Formatter f = new Formatter(System.out); char u = 'a' ; System.out.println("u = 'a'" ); f.format("%%s: %s\n" , u); f.format("%%c: %c\n" , u); f.format("%%b: %b\n" , u); f.format("%%h: %h\n" , u); int v = 121 ; System.out.println(); System.out.println("v = 121" ); f.format("%%d: %d\n" , v); f.format("%%c: %c\n" , v); f.format("%%b: %b\n" , v); f.format("%%s: %s\n" , v); f.format("%%x: %x\n" , v); f.format("%%h: %h\n" , v); BigInteger w = new BigInteger("50000000000000" ); System.out.println(); System.out.println("w = new BigInteger(\"50000000000000\")" ); f.format("%%d: %d\n" , w); f.format("%%b: %b\n" , w); f.format("%%s: %s\n" , w); f.format("%%x: %x\n" , w); f.format("%%h: %h\n" , w); double x = 179.543 ; System.out.println(); System.out.println("x = 179.543" ); f.format("%%b: %b\n" , x); f.format("%%s: %s\n" , x); f.format("%%f: %f\n" , x); f.format("%%e: %e\n" , x); f.format("%%h: %h\n" , x); Conversion y = new Conversion(); System.out.println(); System.out.println("y = new Conversion()" ); f.format("%%b: %b\n" , y); f.format("%%s: %s\n" , y); f.format("%%h: %h\n" , y); boolean z = false ; System.out.println(); System.out.println("z = false" ); f.format("%%b: %b\n" , z); f.format("%%s: %s\n" , z); f.format("%%h: %h\n" , z); } }
正则表达式 要想学好正则表达式,基本上要记住的就是一堆语法。
废话不多说,直接看如何创建正则表达式。
字符
正则表达式
说明
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
Pattern.compile(regex) 编译 regex
并产生 Pattern
对象。
Pattern.matcher(检索的字符串)生成一个 Matcher
对象。
基本用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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); Matcher m = p.matcher("aabbcc" ); while (m.find()) { print("Match \"" + m.group() + "\" at positions " + m.start() + "-" + (m.end() - 1 )); } } } }
Matcher 方法
1 2 3 4 5 boolean matches () ; boolean lookingAt () ; boolean find () ; boolean find (int start) ; String group () ;
常用的 Pattern 标记
1 2 3 4 5 6 public static Pattern compile (String regex, int flags) { return new Pattern(regex, flags); }
Pattern.split()
将字符串分割成符合 regex
的字符数组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class SplitDemo { public static void main (String[] args) { String input = "This!!unusual use!!of exclamation!!points" ; print(Arrays.toString(Pattern.compile("!!" ).split(input))); print(Arrays.toString(Pattern.compile("!!" ).split(input, 3 ))); } }
扫描输入 使用 BufferedReader
实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 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); System.out.println("\n2.input: <age> <double>" ); String numbers = input.readLine(); System.out.println("input.readLine() = " + numbers); String[] numArray = numbers.split(" " ); int age = Integer.parseInt(numArray[0 ]); double favorite = Double.parseDouble(numArray[1 ]); 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" ); } } }
使用 Scanner
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class BetterRead { public static void main (String[] args) { Scanner stdin = new Scanner(SimpleRead.input); System.out.println("What is your name?" ); String name = stdin.nextLine(); System.out.println(name); System.out.println("How old are you? What is your favorite double?" ); System.out.println("(input: <age> <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 ); } }
总结 过去, Java
对字符串操作的支持相当不完善。不过随着近几个版本的升级,我们可以看到, Java
已经从其他语言中吸取了许多成熟的经验。到目前为止,它对字符串操作的支持已经很完善了。不过,有时你还要在细节上注意效率的问题,例如恰当地使用 StringBuilder
等。
感谢 thinking-in-java(13) String字符串