不可变String
String
实际上是不可变的,每一个看起来会修改String的方法,实际上都是创建一个全新的String对象。public static void main(String[] args) { String q = "abc"; String qq = q.toUpperCase(); System.out.println(q); // abc System.out.println(qq); // ABC }
- 当传入
toUpperCase()
的时候,实际上传递的是引用的一个拷贝。 - 对于一个方法而言,参数是为该方法提供信息的,而不是想让该方法改变自己的。
- String对象是不可变的,可以给String对象添加任意多的别名。
String q = "abc"; String qqq = q; // 别名 System.out.println(q == qqq); // 地址相同,true
重载“+”与StringBuilder
- 通过”+“可以连接两个String
底层原理:虽然源代码中没有使用StringBuilder
类,但编译器却自作主张地使用了它,因为它更高效。编译器创建了一个StringBuilder
对象,用以构造最终的String,并在每个字符串调用一次StringBuilder
的append()
方法,最后调用toString()
生成结果。String s = "abc" + "mango" + "def" + 47; // abcmangodef47
String
和StringBuilder
比较
第一种方法,每经过一次循环,就会创建一个新的StringBuilder
对象,然后在append()
;第二种方法,只创建一个StringBuilder
对象,然后直接在StringBuilder
上append
显式地创建StringBuilder
还允许你预先为其指定大小。如果你已经知道最终的字符串大概有多长,那预先为其指定大小public static void main(String[] args) { String res = ""; long start = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { res += i; } System.out.println(System.currentTimeMillis() - start); // 65 StringBuilder result = new StringBuilder(); start = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { result.append(i); } System.out.println(System.currentTimeMillis() - start); // 0 }
StringBuilder
提供了丰富而全面的方法,包括insert()
、replace()
、subString()
、reverse()
,但最常用的还是append()
和toString()
。还有delete()
方法,删除指定位置坐标的内容。StringBuilder
和StringBuffer
,StringBuffer
是线程安全的,StringBuilder
速度更快
无意识的递归
ArrayList.toString()
,它会遍历ArrayList
中包含的所有对象,调用每个元素上的toString()
方法
String上的操作
String
、StringBuffer
、StringBuilder
实现了CharSequence接口,可实现通过类似于数组的一些操作public interface CharSequence { int length(); char charAt(int index); default boolean isEmpty() { return this.length() == 0; } CharSequence subSequence(int start, int end); public String toString(); public default IntStream chars() { ... } public default IntStream codePoints() { ... } @SuppressWarnings("unchecked") public static int compare(CharSequence cs1, CharSequence cs2) { ... } }
getChars()
:复制String
的部分内容,到一个char
数组上public static void main(String[] args) { String str = "fagdgadg"; char[] chars = new char[10]; str.getChars(1,3, chars, 2); System.out.println(Arrays.toString(chars)); // [ , , a, g, , , , , , ] }
equalsIgnoreCase()
:忽略大小写比较两个String
是否相等CompareTo()
:按照词典顺序比较String
的内容。contentEquals
:也是用于比较的方法,可以输入StringBuffer
以及所有实现了CharSequence
接口的类regionMatcher()
:比较指定区域是否相等String a = "abcdefghijklmn"; // 字符串的开始索引,另一个字符串,另一个字符串的开始索引,子字符串的长度 System.out.println(a.regionMatches(2, "cdefghijklmn", 0, 12));
trim()
:将String
两端的空白字符删除,返回一个新的String
对象valueOf
:返回一个参数的字符串,比如int
类型、double
类型String s = String.valueOf(1.2324);
intern()
:字符串在常量池都有一个唯一的字符串,通过intern()
取出该字符串在常量池中保存的字符串的引用String s0 = "123456"; String s1 = new String("123456"); String s2 = "123" + "456"; String s3 = new String("123") + "456"; System.out.println(s0 == s1); // false System.out.println(s0.intern() == s1.intern()); //true System.out.println(s0 == s2); //true System.out.println(s0.intern() == s2.intern()); //true System.out.println(s0 == s3); //false System.out.println(s0.intern() == s3.intern()); //true
格式化输出
System.out.format
:格式化输出字符串// +表示右对齐,15代表占用15个位置,5代表保留字符串前5个字符 System.out.format("%15.5s", "nihaoshijie"); // " nihao" System.out.println(); // - 表示左对齐 System.out.format("%-15.5s", "nihaoshijie"); // "nihao "
Formatter
类:Formatter
看作一个翻译器,它将格式化字符串与数据翻译成需要的结果。在创建Formatter
类中,需要给构造器传递一些信息,告诉它输出到哪里(流或者文件之类的)public class FormatterTest { private String name; private Formatter f; public FormatterTest(String name, Formatter f) { this.name = name; this.f = f; } public void move(int x, int y) { f.format("%s The Turtle is as (%d, %d)\n", name, x, y); } public static void main(String[] args) { PrintStream out = System.out; // System.out的引用 FormatterTest tommy = new FormatterTest("Tommy", new Formatter(out)); FormatterTest terry = new FormatterTest("Terry", new Formatter(System.out)); tommy.move(0, 0); terry.move(4, 8); tommy.move(3, 4); terry.move(2, 5); tommy.move(3, 3); terry.move(3, 3); } }
- 格式化说明符:控制空格与对齐
–%[argment_index$][flags][width][.precision]conversion
flag
:控制左对齐(-
)还是右对齐(+
或无)width
:控制一个域的最小尺寸,默认不足的补空格,也可以自己指定.precision
:控制输出字符的最大数量,应用于浮点数时,它表示小数部分显示出来的位数;应用于String
的时候,它表示打印出来String
输出字符的最大数量
public class Receipt { private double total = 0; private Formatter f = new Formatter(System.out); public void printTitle() { 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.printTitle(); receipt.print("Jack's Magic Beans", 4, 4.25); receipt.print("Princess", 3, 5.1); receipt.print("Three Bears Porridge", 1, 14.29); receipt.printTotal(); } } /* output Item Qty Price ---- --- ----- Jack's Magic Be 4 4.25 Princess 3 5.10 Three Bears Por 1 14.29 Tax 1.42 ----- Total 25.06 */
Formatter
转换字符 说明 d
整数型(十进制) c
unicode字符 b
Boolean值 s
String f
浮点数(十进制) e
浮点数(科学计数) x
整数(十六进制) h
散列码(十六进制) %
字符"%" 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); // 转换成字符串, "a" f.format("c: %c\n", u); // 字符,'a' f.format("b: %b\n", u); // true f.format("h: %h\n", u); // 'a'在ASCII码中的十六进制表示,61 int v = 121; System.out.println("v = 121"); f.format("d: %d\n", v); // 十进制, 121 f.format("c: %c\n", v); // 转换成字符,y,ASCII码十进制第121个为y f.format("b: %b\n", v); // true f.format("s: %s\n", v); // "121" f.format("x: %x\n", v); // 十六进制,79 f.format("h: %h\n", v); // 散列码,79 BigInteger w = new BigInteger("50000000000"); System.out.println("w = new BigInteger(\"50000000000\")"); f.format("d: %d\n", w); // 50000000000 f.format("b: %b\n", w); // true f.format("s: %s\n", w); // 50000000000 f.format("x: %x\n", w); // ba43b7400 f.format("h: %h\n", w); // a43b7555 double x = 179.543; System.out.println("x = 179.543"); f.format("b: %b\n", x); // true f.format("s: %s\n", x); // 179.543 f.format("f: %f\n", x); // 179.543000 (默认小数点后6位) f.format("e: %e\n", x); // 1.795430e+02 f.format("h: %h\n", x); // 1ef462c Conversion y = new Conversion(); // 类的实例化对象本身 System.out.println("y = new Conversion()"); f.format("b: %b\n", y); // true f.format("s: %s\n", y); // chapter13.Conversion@cc34f4d f.format("h: %h\n", y); // cc34f4d boolean z = false; System.out.println("z = false"); f.format("b: %b\n", z); // false f.format("s: %s\n", z); // false f.format("h: %h\n", z); // 4d5 } }
String.format()
:接受与Formatter
相同的参数,但是返回一个String
对象
其实在String.format
内部,它也是创建一个Formatter
对象,然后将你传入的参数转给该Formatter
String s = String.format("%-15.5s, %.2f", "nihaoshijie", 12.5668945); System.out.println(s); // "nihao , 12.57"
public class Hex { public static void main(String[] args) { StringBuilder s = new StringBuilder(); for (int i = 0; i < 100; i++) { s.append(String.format("%02X ", i)); if (i % 10 == 9) s.append("\n"); } System.out.println(s.toString()); } }
正则表达式
-
使用正则表达式,我们能够以编程的方式,构造复杂的文本模式,并对输入的字符串进行搜索。一旦找到了匹配这些模式的部分,你就能够随心所欲地对它们进行处理。
public class IntegerMatch { public static void main(String[] args) { System.out.println("-1234".matches("-?\\d+")); // 一个或零个"-",一个或多个数字 System.out.println("+991".matches("(-|\\+)?\\d+")); // 一个或零个符号"-"或"+",一个或多个数字 } }
-
转义字符
\\
,表示一个数字\\d
,表示一个反斜线\\\\
-
split()
或replace()
,可通过正则表达式匹配public class splitTest { public static void main(String[] args) { String s = "the, next, then, when, what, why"; System.out.println(Arrays.toString(s.split(" "))); // [the,, next,, then,, when,, what,, why] System.out.println(Arrays.toString(s.split("\\W+"))); // [the, next, then, when, what, why] } }
-
创建正则表达式
字符 含义 B
指定字符B \xhh
十六进制为 0xhh
的字符\uhhhh
十六进制表示为 0xhhhh
的Unicode字符\t
制表符Tab \n
换行符 \r
回车 \f
换页 \e
转义 字符类 含义 .
任意字符 [abc]
包含 a
、b
和c
的任何字符(和a|b|c
作用相同)[^abc]
除了 a
、b
和c
的任何字符[a-zA-Z]
从 a
到z
或从A
到Z
的任意字符[abc[hij]]
任意 a
、b
、c
、h
、i
、j
的字符[a-z&&[hij]]
从 a
到z
的范围内,满足h
、i
、j
的任意字符(两个条件的交集)\s
空白符(空格、tab、换行、换页、回车) \S
非空白符 \w
词字符 [a-zA-Z0-9]
\W
非词字符 [^\w]
\d
数字 [0-9]
\D
非数字 逻辑操作符 说明 XY
Y
跟在X
后面X|Y
X
或Y
(X)
捕获组 边界匹配符 说明 ^
一行的起始 $
一行的结束 \b
词的边界 \B
非词的边界 \G
前一个匹配的结束 System.out.println(s.matches("[rR]udolph")); System.out.println(s.matches("[rR][aeiou][a-z]ol.*")); // `.*`表示后面追加任意字符(0或多个) System.out.println(s.matches("R.*"));
-
量词:描述一个模式吸收输入文本的方式
– 贪婪型:量词总是贪婪的,贪婪表达式会为所有可能的模式发现尽可能多的匹配。
– 勉强型:用问号来指定,这个量词匹配满足模式所需的最少字符数。
– 占有型:只有Java有,比较少用贪婪型 勉强型 如何匹配 X?
X??
一个或零个 X
X*
X*?
零个或多个 X
X+
X+?
一个或多个 X
X{n}
X{n}?
恰好n个 X
X{n,}
X{n,}?
至少n个 X
X{n,m}
X{n,m}?
至少n个 X
,且不超过m次 -
Pattern
和Matcher
根据String
类型的正则表达式生成一个Pattern
对象,将想要检索的的字符串传入Pattern
对象的matcher()
方法,再生成一个Matcher
对象。
matcher()
中有许多匹配的方法:matches()
、lookingAt()
、find()
matches()
:用来判断整个输入字符串是否匹配正则表达式模式
lookingAt()
:用来判断该字符串的开始部分是否能够匹配模式public class PatternTest { public static void main(String[] args) { args = new String[]{ "abcabcabcdefabc", "abc+", "(abc)+", "(abc){2,}"}; if (args.length < 2) { // 如果小于2,则直接退出 System.exit(0); } System.out.println("Input: " + args[0]); for (String arg : args) { System.out.println("Regular expression: " + arg); Pattern p = Pattern.compile(arg); // 根据正则表达式生成一个Pattern Matcher matcher = p.matcher(args[0]); // 输入要被匹配的字符串,生成各种匹配结果的Matcher while (matcher.find()) { // 查找下一个匹配的,直到找不到 System.out.println("Match \"" + matcher.group() + "\" at position " + matcher.start() + "-" + matcher.end()); } System.out.println(matcher.matches()); // 尝试整个正则表达式进行匹配,返回boolean类型 System.out.println(matcher.lookingAt()); // 尝试将输入序列与模式匹配,从区域的开头开始。返回boolean类型 } } }
public class Finding { public static void main(String[] args) { Pattern compile = Pattern.compile("\\w+"); // 所有单词 Matcher m = compile.matcher("Evening is full of the linnet's wings"); while (m.find()) { // 逐个查找,直到找不到匹配的了 System.out.print(m.group() + " "); } System.out.println(); int i = 0; while (m.find(i)) { // 频繁重新设置查找开始点 System.out.print(m.group() + " "); i++; } } }
-
Groups
:组是用括号划分的正则表达式,可以根据组的编号来引用某个组
–public int grouopCount()
返回该匹配器的模式中的分组数目
–public String group()
返回前一次匹配操作的第0组
–public String group(int i)
返回在前一次匹配操作期间指定的组号的内容,如果没有匹配输入字符串的任何部分,则将会返回null
–public int start(int group)
返回在前一次匹配操作中寻找到的组的起始索引
–public int end(int group)
返回在前一次匹配操作中寻找到的组的最后一个字符索引加一的值public class Groups { static public final String POEM = "Twas brillig, and the slithy toves\n" + "Did fadf fafafj faklfer jkju jkl\n" + "af 34 54536 ,6563 244\n" + "44 242 24543, g dg sf\n" + "254 da, gsf fgsfa"; public static void main(String[] args) { Matcher m = Pattern.compile("(?m)(\\S+)\\s+(\\S+)\\s+(\\S+)$").matcher(POEM); // 寻找所有句子的最后三个词 while (m.find()) { // m.group有4个元素,组号为0表示整个表达式,组号1表示被第一个括号扩起的组,以此类推 for (int i = 0; i <= m.groupCount(); i++) { System.out.print("[" + m.group(i) + "]"); } System.out.println(); } } }
-
start()
和end()
start()返回先前匹配的起始位置的索引,end返回所匹配的最后字符的索引加一的值。匹配操作失败之后,调用start()
或end()
将会产生IllegalStateException
public class StartEnd { public static String input = "As long as there is injustice, whenever a\n" + "Targathian baby cries out, wherever a distress\n" + "signal sounds among the stars ... We'll be there.\n" + "This fine ship, and this fine crew ... \n" + "Never give up! Never surrender!"; private static class Display { private boolean regexPrinted = false; private String regex; public Display(String regex) { this.regex = regex; } void display(String message) { if (!regexPrinted) { System.out.println(regex); regexPrinted = true; } System.out.println(message); } } static void examine(String s, String regex) { Display d = new Display(regex); Pattern p = Pattern.compile(regex); Matcher m = p.matcher(s); while (m.find()) { d.display("find() '" + m.group() + "' start = " + m.start() + " end = " + m.end()); } if (m.lookingAt()) { // 没有重置 d.display("lookingAt() start = " + m.start() + " end = " + m.end()); } if (m.matches()) { // 没有重置 d.display("matches() start = " + m.start() + " end = " + m.end()); } } public static void main(String[] args) { for (String in : input.split("\n")) { System.out.println("input : " + in); for (String regex : new String[]{ "\\w*ere\\w*", "\\w*ever", "T\\w+", "Never.*?!"}) { examine(in, regex); } } } } /* output input : As long as there is injustice, whenever a \w*ere\w* find() 'there' start = 11 end = 16 \w*ever find() 'whenever' start = 31 end = 39 input : Targathian baby cries out, wherever a distress \w*ere\w* find() 'wherever' start = 27 end = 35 \w*ever find() 'wherever' start = 27 end = 35 T\w+ find() 'Targathian' start = 0 end = 10 lookingAt() start = 0 end = 10 input : signal sounds among the stars ... We'll be there. \w*ere\w* find() 'there' start = 43 end = 48 input : This fine ship, and this fine crew ... T\w+ find() 'This' start = 0 end = 4 lookingAt() start = 0 end = 4 input : Never give up! Never surrender! \w*ever find() 'Never' start = 0 end = 5 find() 'Never' start = 15 end = 20 lookingAt() start = 0 end = 5 Never.*?! find() 'Never give up!' start = 0 end = 14 find() 'Never surrender!' start = 15 end = 31 lookingAt() start = 0 end = 14 matches() start = 0 end = 31 */
注意:
find()
可以在输入的任意位置定位正则表达式,而lookingAt()
和matches()
只有在正则表达式与输入的最开始处就开始匹配时才会成功。 -
split()
将输入字符串断开成字符串对象数组// 通过Pattern分割 @Test public void test() { String input = "This!!unusual use!!of exclamation!!points"; Pattern c = Pattern.compile("!!"); String[] split = c.split(input); System.out.println(Arrays.toString(split)); // 限制分割字符串的个数 String[] split1 = c.split(input, 3); System.out.println(Arrays.toString(split1)); }
-
替换操作
–replaceFirst(String replacement)
以参数字符串replacement
替换掉第一个匹配成功的部分
–replaceAll(String replacement)
以参数字符串replacement
替换掉所有匹配成功的部分
–appendReplacement(StringBuilder sbuilder, String replacement)
执行渐进式的替换。实现执行一次或多次appendReplacement()
之后,调用此方法可以将输入字符串余下的部分复制到sbuf
中public class replaceTest { @Test public void test() { String s = "agfkrjqekrhkfjfkjfkfhjfnfnvfjalkjdfklflwuirhnvn"; Matcher m = Pattern.compile("[aeoui]").matcher(s); StringBuilder sbud= new StringBuilder(); while (m.find()) { m.appendReplacement(sbud, m.group().toUpperCase()); } System.out.println(sbud); // AgfkrjqEkrhkfjfkjfkfhjfnfnvfjAlkjdfklflwUI m.appendTail(sbud); // AgfkrjqEkrhkfjfkjfkfhjfnfnvfjAlkjdfklflwUIrhnvn System.out.println(sbud); } }
m.appendReplacement
一点一点地替换,并且把结果一点一点地append()
到StringBuilder
中。到最后,将剩余部分也加入到StringBuilder
中 -
reset()
public static void main(String[] args) { Matcher m = Pattern.compile("[frb][aiu][gx]").matcher("fix the rug with bags"); while (m.find()) { System.out.println(m.group() + " "); } System.out.println("******************reset*********************"); m.reset(); // 重置 while (m.find()) { System.out.println(m.group() + " "); } System.out.println("******************reset*********************"); m.reset("bags the fix with rug"); // 重置 while (m.find()) { System.out.println(m.group() + " "); } }
扫描输入
Scanner
的构造器可以接受任何类型的输入对象,包括File
对象、InputStream
、String
或者其他Readable
对象。- 所有的输入、分词以及翻译的操作都隐藏在不同类型的
next
方法中。普通的next()
方法返回下一个String
,所有的基本类型(除char
之外)都有相应的next()
方法,如nextInt()
等。hasNext()
判断下一个输入分词是否所需的类型,如hasNextInt()
等 Scanner定界符
:在默认的情况下,Scanner
根据空白字符对输入进行分词,但自己可以通过正则表达式指定所需的定界符public static void main(String[] args) { Scanner stdin = new Scanner(System.in); stdin.useDelimiter("\\s*,\\s*"); // 修改分隔符 System.out.println(stdin.delimiter()); // 查询当前的分隔符,`\s*,\s*` while (stdin.hasNextInt()) { System.out.println(stdin.nextInt()); } }
- 用正则表达式扫描
public class ThreatAnalyzer { static String threatAnalyzer = """ 58.27.82.161@02/10/2005 204.45.234.40@02/11/2005 58.27.82.161@02/12/2005 """; public static void main(String[] args) { Scanner scanner = new Scanner(threatAnalyzer); Pattern p = Pattern.compile("(\\d+[.]\\d+[.]\\d+[.]\\d+)@(\\d{2}[/]\\d{2}/\\d{4})"); while (scanner.hasNext(p)) { // 查看下一个是否符合要求 scanner.next(p); // 获取下一个 MatchResult match = scanner.match(); // 将匹配结果返回 String ip = match.group(1); // 第一组,即ip String date = match.group(2); // 第二组,即日期 System.out.format("Threat on %s from %s\n", date, ip); } } }