1,不可变String
String对象时不可变的,每一个看起来会修改String的方法,实际上都是创建了一个全新的String对象,而最初的String对象丝毫未动!
package com.str;
public class Immutable {
public static String upcase(String s){
return s.toUpperCase();
}
public static void main(String[] args) {
String q = "howpy";
System.out.println(q);
String qq = upcase(q);
System.out.println(qq);
System.out.println(q);
}
}
重载“+”与StringBuilder
“+”可用于String:
package com.str;
public class Concatenation {
public static void main(String[] args) {
String mango = "mango";
String s = "abc"+mango+"def"+47;
System.out.println(s);
}
}
这种进行字符串的拼接,其实在底层编译器会把String替换成StringBuilder来提高效率。
这种一次性的操作字符串可以使用String,编译器会帮助我们替换,不过在循环中操作字符串可以手动替换为StringBuilder来提高效率!
StringBuilder有佷多丰富而全面的方法!
package com.str;
import java.util.*;
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()-1,result.length());
result.append("]");
return result.toString();
}
public static void main(String[] args) {
UsingStringBuilder usb = new UsingStringBuilder();
System.out.println(usb);
}
}
3,无意识的递归
java每个类都集成Object,因此容器类都有toString()方法,并且重写了该方法,这使得他生成的String结果能够表达容器自身。
当使用ArrayList.toString()方法时,它会遍历ArrayList中包含的所有变量,并且调用每个元素的toString()方法!
package com.str;
import java.util.*;
public class ArrayListDisplay {
public String toString(){
return "toString";
}
public static void main(String[] args) {
List<ArrayListDisplay> a = new ArrayList<>();
for(int i = 0; i< 10; i++){
a.add(new ArrayListDisplay());
}
System.out.println(a);
}
}
当希望toString()方法打印对象的内存地址,也许你会烤炉使用this关键字,像下面这样:
return "toString"+this;
}
运行时,会报一大堆错。因为使用+号时,编译器会将ArrayListDisplay强制转换为String。
正确的方式是调用Object.toString()方法!
public String toString(){
return "toString"+super.toString();
}
4,String上的操作
一系列的方法
5,格式化输出
5.1,printf()
java有和c语言一样的格式化输出语句printf(),
package com.str;
public class Printf {
public static void main(String[] args) {
int i = 1;
System.out.printf("i=%d",i);
}
}
5.2,System.out.format()
format与printf()一样
5.3,Formatter类
Formatter需要向其构造器传递一些信息,以告诉它最终的结果将传向哪里
package com.str;
import java.io.*;
import java.util.*;
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 id(%d,%d)\n", name,x,y);
}
public static void main(String[] args) {
PrintStream out = System.out;
Turtle tommy = new Turtle("tommy",new Formatter(System.out));
Turtle terry = new Turtle("terry",new Formatter(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);
}
}
格式化说明符
package com.str;
import java.util.Formatter;
public class Receipt {
private double total = 10;
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",4,4.25);
f.format("%-15s %5s %10s\n", "","","-----");
f.format("%-15s %5s %10.2f\n", "Total","",total);
}
public static void main(String[] args) {
Receipt receipt = new Receipt();
receipt.printTitle();
receipt.print("abc", 1, 4.25);
receipt.print("def", 2, 4.25);
receipt.print("ghi", 3, 4.25);
receipt.printTotal();
}
}
/*
输出:
Item Qty Price
---- --- -----
abc 1 4.25
def 2 4.25
ghi 3 4.25
Tax 4 4.25
-----
Total 22.75
*/
数字代表所占位数,-号代表左对齐
Formatter转换
最常用的类型转换:
d:整数型(十进制)
c:Unicode字符
b:Boolean值
s:String
f:浮点数(十进制)
e:浮点数(科学计数)
x:整数(十六进制)
h:散列码
%: 字符“%”
String.format()
在String.format()内部,也是创建一个Formatter对象,然后调用format()。
6,正则表达式
关于正则表达式,不进行介绍了。
看java中是如何实现正则表达式的。
java正则表达式中的“\”与其他语言中的不一样!
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
*/
java中的“\”与其他语言的“\”是一样的。
字符串类型有matches()方法,返回布尔值,参数是一个正则表达式。
String类还有一个正则表达式工具——spliy(),用于将字符串从正则表达式的地方切开。
package com.str;
import java.util.Arrays;
public class Splitting {
public static String knights = "Then-when you have found the";
public static void split(String regex){
System.out.println(Arrays.toString(knights.split(regex)));
}
public static void main(String[] args) {
split(" ");
split("\\w+");
split("n\\w+");
}
}
/*
输出:
[Then-when, you, have, found, the]
[, -, , , , ]
[Then-when you have fou, the]
*/
\w代表单词字符。
另一个有用的工具是替换工具。
package com.str;
public class Replacing {
public static void main(String[] args) {
String knights = "Then-when you have found the found";
System.out.println(knights.replaceFirst("f\\w+", "located"));
System.out.println(knights.replaceAll("f\\w+", "located"));
}
}
/*
输出:
Then-when you have located the found
Then-when you have located the located
*/
创建正则表达式
正则表达式的完整构造子列表,参考JDK文档java.util.regex的Pattern类.
字符
B 指定字符B
\xhh 十六进制值为oxhh的字符
\xhhhh 十六进制表示为oxhhhh的Unicode字符
\t 制表符tab
\n 换行符
\r 回车
\f 换页
\e 转义(Escape)
字符类
[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|b|c|h|i|j作用相同
[a-z&&[hij]] 任意h,i,或j(取交集)
\s 空白符(空格,tab,换行,换页,和回车)
\S 非空白符([^0-9])
\d 数字[0-9]
\D 非数字[^0-9]
\w 词字符[a-zA-Z0-9]
\W 非词字符[^\w]
逻辑操作符
XY Y跟在X后面
X|Y X或Y
(X) 捕获组(capturing group)。可以在表达式中用\i引用第i个捕获组
边界通配符
^ 一行的开始
$ 一行的结束
\b 词的边界
\B 非词的边界
\G 前一个匹配结束
如下代码示例:
package com.str;
public class Rudolph {
public static void main(String[] args) {
for(String pattern : new String[]{"Rudolph","[rR]udolph","[rR][aeiou][a-z]ol.*","R.*"}){
System.out.println("Rudolph".matches(pattern));
}
}
}
/*
输出:
true
true
true
true
*/
量词
- 贪婪性
- 勉强性
- 占有性(java特有的)
书上没给示例,不太懂,标记一下
Pattern和matcher
package com.str;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TestRegularExpression {
public static void main(String[] args) {
Pattern p = Pattern.compile("a(\\w)+");
Matcher m = p.matcher("aaa abc aqu iop");
while(m.find()){
System.out.println(m.group()+" "+m.start()+" "+m.end());
}
System.out.println(Pattern.matches("a+", "aaa"));
String[] s = p.split("aaa,abc.adf");
for(String ss:s){
System.out.print(ss+" ");
}
}
}
/*
输出:
aaa 0 3
abc 4 7
aqu 8 11
true
, .
*/
Pattern.compile();方法生成一个正则表达式对象,然后用此对象的matcher()方法匹配想要匹配的字符串,该方法返回一个Matcher对象。
Matcher对象有佷多方法。
find()方法返回布尔值,这个方法会从头到尾一直匹配,只要匹配到就返回ture,重载的find()还能有一个整数的参数,规定匹配开始的位置,不过后一个find()方法会不断的回到搜索的其实位置,而不会往下匹配!
group()方法返回匹配到的字符串
start()与end()方法返回所匹配的子串在父串的位置。
另外,Pattern的静态方法matches()接受一个正则表达式,和一个字符串,返回布尔值,以此来判断是否完全匹配。
Pattern对象有split()方法,它接受一个字符串,然后把这个字符串根据compile()的正则表达式分割成一个字符串数组。
空
7,扫描输入
如果不用扫描输入,那么从文件和标准输入则很麻烦,看如下代码:
package com.str;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
public class SimpleRead {
public static BufferedReader input = new BufferedReader(
new StringReader("Sir Robin of Camelot\n22 1.61803"));
public static void main(String[] args) throws IOException {
System.out.println("what is your name?");
String name = input.readLine();
System.out.println(name);
System.out.println("double");
System.out.println("(double)");
String numbers = input.readLine();
System.out.println(numbers);
String[] numArray = numbers.split(" ");
int age = Integer.parseInt(numArray[0]);
double favorite = Double.parseDouble(numArray[1]);
System.out.println(age);
System.out.println(favorite);
}
}
/*
输出:
what is your name?
Sir Robin of Camelot
double
(double)
22 1.61803
22
1.61803
*/
可以看到,需要一行一行的读取,然后进行截取,再用Integer,Double的各种解析方法来解析数据。
使用扫描类Scanner则很方便:
package com.str;
import java.util.Scanner;
public class BetterRead {
public static void main(String[] args) {
Scanner stdin = new Scanner(SimpleRead.input);
System.out.println("name");
String name = stdin.nextLine();
System.out.println(name);
System.out.println("double");
int age = stdin.nextInt();
double favorite = stdin.nextDouble();
System.out.println(age);
System.out.println(favorite);
}
}
Scanner的构造器可以接受很合类型的输入对象,包括FILE对象,InputStream,String和Readable。
Scanner界定符
在默认情况下,Scanner根据空白字符对输入进行分词,但也可以使用正则表达式指定自己的界定符;
package com.str;
import java.util.Scanner;
import java.util.regex.Pattern;
public class ScannerDelimiter {
public static void main(String[] args) {
Scanner scanner = new Scanner("12,42,78,99,42");
scanner.useDelimiter("\\s*,\\s*");
Pattern p = scanner.delimiter();
while(scanner.hasNextInt()){
System.out.println(scanner.nextInt());
}
}
}
useDelimiter()方法规定要使用的界定符,delimiter()返回界定符正则表达式的Pattern对象。
用正则表达式扫描
扫描复杂数据:
package com.str;
import java.util.Scanner;
import java.util.regex.MatchResult;
public class ThreatAnalyzer {
static String threatDate =
"59.27.82.161@02/10/2016\n"+
"59.27.82.161@01/10/2016\n"+
"[abc deg asd asd qwe]";
public static void main(String[] args) {
Scanner sc = new Scanner(threatDate);
String pattern = "(\\d+[.]\\d+[.]\\d+[.]\\d+)@"+
"(\\d{2}/\\d{2}/\\d{4})";
while(sc.hasNext(pattern)){
sc.next(pattern);
MatchResult match = sc.match();
String ip = match.group(1);
String date = match.group(2);
System.out.println("Thread on "+date+" from "+ip);
}
}
}
/*
输出:
Thread on 02/10/2016 from 59.27.82.161
Thread on 01/10/2016 from 59.27.82.161
*/
先分析数据的格式,写出相应的正则表达式,在使用Scanner对象的next()方法时,把正则表达式当做参数传入,然后调用match()方法就会返回一个MatchResult对象,用该对象操作数据。
在此需要注意的是正则表达式中不要有界定符!
8,StringTokenizer
使用正则表达式和Scanner分隔字符串就可以了,这个类已经废弃不用了!,不用管了。