能够证实,字符串操做是计算机程序设计中最多见的行为。java
String对象是不可变的。查看JDK文档你就会发现,String类中每个看起来会修改String值的方法,实际上都是建立了一个全新的String对象,以包含修改后的字符串内容,而最初的String对象则丝毫未变。正则表达式
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);
}
}
复制代码
不可变性会带来必定的效率问题。用于String的“+”与“+=”是Java中仅有的两个重载过的操做符,而Java并不容许程重载任何操做符。编程
操做符 “+” 能够用来链接String:bash
public class Concatenation {
public static void main(String[] args) {
String mango = "mango";
String s = "abc" + mango;
System.out.println(s);
}
}
复制代码
想看看以上代码究竟是如何工做的吗,能够用JDK自带的工具javap来反编译以上代码,能够获得如下的字节码:app
编译器自动引入了java.lang.StringBuilder类(由于它更高效),经过调用append方法将字符串链接起来,最后调用toString方法生成最终结果。ide
public String implicit(String[] fields) {
String result = "";
for (int i = 0; i < fields.length; i++) {
result += fields[i];
}
return result;
}
复制代码
经过反编译得以下字节码:工具
StringBuilder是在循环体内构造的,这意味着每通过循环一次,就会建立一个新的StringBuilder对象。ui
public class InfiniteRecursion {
@Override
public String toString() {
// 打印InfiniteRecursion对象的内存地址
return " InfiniteRecursion adress: " + this + "\n";
}
public static void main(String[] args) {
InfiniteRecursion infiniteRecursion = new InfiniteRecursion();
System.out.println(infiniteRecursion);
}
}
复制代码
运行以上程序出现以下结果:this
" InfiniteRecursion adress: " + this
这里发生了自动类型转换,由InfiniteRecursion类型转换成String类型。由于编译器发现String对象后面跟着一个 “+”,然后面的对象不是String,编译器调用this.toString()方法进行类型转换,所以发生了递归调用。spa
若是你真的想要打印出对象的内存地址,应该调用Object.toString()方法。因此不应使用this,而是应该调用super.toString()方法。
Java SE5推出了C语言中printf()风格的格式化输出这一功能,不须要使用重载的 “+”操做符来链接引用号内的字符串或者字符串常量,而是使用特殊的占位符来表示数据未来的位置。
public static void main(String[] args) {
int x = 5;
double y = 5.332;
// the old way
System.out.println("Row 1:[" + x + " " + y + "]");
// the new way
System.out.printf("Row 1:[%d %f]\n", x, y);
// or
System.out.format("Row 1:[%d %f]\n", x, y);
}
复制代码
运行以上程序,首先将x的值插入到%d的位置,而后将y的值插入%f的位置。这些占位符被称为格式修饰符,它们不但说明了将插入什么类型的变量,以及如何对其格式化。
在Java中,全部新的格式化功能都由java.util.Formatter类处理。能够将Formatter类看做一个翻译器,它将你的格式化字符串与数据翻译成须要的结果。
Formatter formatter = new Formatter(System.out);
formatter.format("Row 1:[%d %f]\n", x, y);
复制代码
String.format()是一个static方法,它接受与Formatter.format()方法同样的参数,但返回一个String对象。
在插入数据时,若是想要控制空格与对齐,你须要更精细复杂的格式修饰符。如下是其抽象的语法:
%[argument_index$][flags][width][.precision]conversion
复制代码
字段 | 说明 |
---|---|
argument_index | 须要将参数列表中第几个参数进行格式化 |
flags | 一些特殊的格式,好比‘-’表明向左对齐 |
width | 输出的最小的宽度,适用于全部的参数类型 |
[.precision] | 参数为String,则表示打印String输出字符的最大数量;参数为float,则表示小数点最大位数。不使用于int |
conversion | 接受的参数类型,如s表明后面接String类型的参数;d表明接int型的参数 |
正则表达式是一种强大而灵活的文本处理工具。使用正则表达式,咱们可以以编程的方式,构造复杂的文本模式,并对输入的字符串进行搜索。一旦找到了匹配这些模式的部分,你就可以为所欲为地对它们进行处理。
Java语言与其余语言相比对反斜杠
\
有不一样的处理:在其余语言中,
\\
表示“我想要在正则表达式中插入一个普通的(字面上的)反斜杠,请不要给它作任何特殊的意义。” 而在Java中,\\
的意思是“我要插入一个正则表达式的反斜杠,因此其后的字符具备特殊的意义。”例如,若是你想表示一位数字,那么正则表达式应该是
\\d
。若是你想插入一个普通的反斜杠,则应该这样\\\\
。不过换行和制表符之类的东西只需使用单斜杠线:\n\t
。
public String[] split(String regex)
public String[] split(String regex, int limit)
public String replaceFirst(String regex, String replacement) public String replaceAll(String regex, String replacement) public boolean matches(String regex) 复制代码
通常来讲,比起功能有限的String类,咱们更愿意构造功能强大的正则表达式对象。经过Pattern.complie()
方法来编译你的正则表达式便可。它会根据你的String类型的正则表达式生成一个Pattern对象。接下来,把你想要检索的字符串传入Pattern对象的matcher()方法,matcher()方法会生成一个Matcher对象,它有不少功能可用。
public class RegexExpression {
public static void main(String[] args) {
String phone = "18926119073";
Pattern pattern = Pattern.compile("1([358][0-9]|4[579]|66
|7[0135678]|9[89])[0-9]{8}");
Matcher matcher = pattern.matcher(phone);
System.out.println(matcher.matches());
}
}
复制代码
组是用括号划分的正则表达式,能够根据组的编号来引用某个组。组号为0表示整个表达式,组号1表示被第一对括号括起的组,依次类推。所以,在下面这个表达式:
A(B(C))D
复制代码
中有三个组:组0是ABCD,组1是BC,组2是C。
Mather提供了不少有用的方法具体使用查看API,使用Mather的替换方法能够实现隐藏手机号中间数字、隐藏用户名等。
String phone = "18926119073";
Pattern pattern = Pattern.compile("(\\d{3})\\d{4}(\\d{4})");
Matcher matcher = pattern.matcher(phone);
phone = matcher.replaceAll("$1****$2");
System.out.println(phone);
复制代码
Java SE5新增类Scanner类,它能够大大减轻扫描输入的工做。
public class ScannerRead {
public static void main(String[] args) {
Scanner scanner = new Scanner("Sir Robin of Camelot
\n22 1.61803"));
System.out.println("What is your name?");
String name = scanner.nextLine();
System.out.println(name);
System.out.println("(input: <age> <double>)");
System.out.println(scanner.nextInt()+" "+ scanner.nextDouble());
}
}
复制代码
Scanner的构造期能够接受任何类型的输入对象,包括File对象、InputStream、String或者Readable对象。
Scanner全部的输入、分词以及翻译的操做都隐藏在不一样类型的next方法中,普通的next()方法返回下一个String,全部的基本类型(除char以外)都有对应的next方法,包括BigDecimal和BigInteger。全部的next方法,只有在找到一个完整的分词以后才会返回,hasNext方法用以判断下一个输入分词是否所需的类型。
在默认的状况下,Scanner根据空白符对输入进行分词,可是你能够用正则表达式指定本身所需的定界符:
public class ScannerDelimiter {
public static void main(String[] args) {
Scanner scanner = new Scanner("12,42,78,99");
scanner.useDelimiter(",");
while (scanner.hasNextInt()){
System.out.println(scanner.nextInt());
}
}
}
复制代码
除了可以扫描基本类型以外,你还可使用自定义的正则表达式进行扫描。以下所示:
public class ThreatAnalyzer {
static String threatData =
"58.27.82.161@02/10/2005\n" +
"124.45.82.161@02/10/2005\n" +
"58.27.82.161@02/10/2005\n" +
"72.27.82.161@02/10/2005\n" +
"[Next log section with different data format]";
public static void main(String[] args) {
Scanner scanner = new Scanner(threatData);
String pattern = "(\\d+[.]\\d+[.]\\d+[.]\\d+)@(\\d{2}/\\d{2}/\\d{4})";
while (scanner.hasNext(pattern)) {
scanner.next(pattern);
MatchResult match = scanner.match();
String ip = match.group(1);
String date = match.group(2);
System.out.format("Threat on %s from %s\n", date, ip);
}
}
}
复制代码