Java基础--字符串(格式化输出、正则表达式)
⼀字符串
1、不可变String
String对象是不可变的,查看JDK⽂档你就会发现,String类中每⼀个看起来会修改String值的⽅法,实际上都是创建⼀个全新的String对象,以包含修改后的字符串内容。⽽最初的String对象则没有改变。
看看下⾯的代码:
public class Immutable {
public static String upcase(String s) {
UpperCase();
}
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);
}
} 
输出如下:
howdy
HOWDY
howdy 
当把q传递给upcase()⽅法时,实际传递的是引⽤的⼀个拷贝。其实,每当把String对象作为⽅法的参数时,都会复制⼀份引⽤,⽽该引⽤所指的对象其实⼀直待在单⼀的物理位置上,从未改变。
回到upcase()的定义,传⼊其中的引⽤有了名字s,只有upcase()运⾏的时候,局部引⽤s才存在。⼀旦upcase()运⾏结束,s就消失了。当然了,upcase()的返回值,其实只是最终结果的引⽤。这⾜以说明,upcase()返回的引⽤已经指向了⼀个新的对象,⽽原本的q则还在原地。
String的这种⾏为其实正是我们想要的。例如:
String s = "asdf";
String x = Immutable.upcase(s); 
难道我们真的希望upcase()改变其参数吗?对于⼀个⽅法⽽⾔,参数是为该⽅法提供信息的,⽽不是想让该⽅法改变⾃⼰的。
2、StringBuilder
String的不可变性会带来⼀定的效率问题。以下⾯⼀段代码为例:
String str="abc";
System.out.println(str);
str=str+"de";
System.out.println(str);
如果运⾏这段代码会发现先输出“abc”,然后⼜输出“abcde”,好像是str这个对象被更改了,其实,这只是⼀种假象罢了,JVM对于这⼏⾏代码是这样处理的,⾸先创建⼀个String对象str,并把“abc”赋值给str,然后在第三⾏中,其实JVM⼜创建了⼀个新的对象也名为str,然后再把原来的str的值和“de”加起来再赋值给新的str,⽽原来的str就会被JVM的垃圾回收机制(GC)给回收掉了,所以,str实际上并没有被更改,也就是前⾯说的String对象⼀旦创建之后就不可更改了。所以,Java中对String对象进⾏的操作实际上是⼀个不断创建新的对象并且将旧的对象回收的⼀个过程,所以执⾏速度很慢。
为此Java引⼊了StringBuilder,StringBuilder对象是变量,对变量进⾏操作就是直接对该对象进⾏更改,⽽不进⾏创建和回收的操作,所以速度要⽐String快很多。
另外,有时候我们会这样对字符串进⾏赋值:
String str="abc"+"de";
StringBuilder stringBuilder=new StringBuilder().append("abc").append("de");
System.out.println(str);
System.out.String());
这样输出结果也是“abcde”和“abcde”,但是String的速度却⽐StringBuilder的反应速度要快很多,这是因为第1⾏中的操作和String
str="abcde";是完全⼀样的,所以会很快,⽽如果写成下⾯这种形式:
String str1="abc";
String str2="de";
String str=str1+str2;
那么JVM就会像上⾯说的那样,不断的创建、回收对象来进⾏这个操作了。速度就会很慢。
总结⼀下:
String:适⽤于少量的字符串操作的情况;
StringBuilder:适⽤于单线程下在字符缓冲区进⾏⼤量操作的情况;
StringBuffer:适⽤多线程下在字符缓冲区进⾏⼤量操作的情况;(后⾯会讲)
3、String对象的基本⽅法
构造器:默认版本、String、StringBuilder、StringBuffer、char数组、byte数组;
length():字符的个数;
charAt():获取String中指定索引位置上的char;
toCharArray():⽣成⼀个char[],包含String中的所有字符;
....
⼆格式化输出
Java SE5推出了C语⾔中printf()风格的格式化输出这⼀功能。
1、System.out.format()
JAVA SE5引⼊的format()⽅法可⽤于PrintStream或PrintWriter对象,其中也包括System.out对象。format()⽅法模仿⾃C的printf()。如果你⽐较习惯使⽤printf()的话,你也可以使⽤System.out.printf()⽅法。
下⾯是⼀个简单的⽰例:
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);
}
}
输出如下:
Row 1: [5 5.332542]
Row 1: [5 5.332542]
Row 1: [5 5.332542]
可以看到,format()和printf()是等价的,他们只需要⼀个简单的格式化字符串,加上⼀串参数即可,每个参数对应⼀个格式修饰符。
2、Formatter类
在Java中,所有的字符串格式化功能都由java.util.Formatter类处理的。可以将Formatter看做⼀个翻译器,它将你的格式化字符串与数据翻译成需要的结果。当你创建⼀个Formatter对象的时候,需要向其构造器传递⼀些信息,告诉它最终的结果将向哪⾥输出:
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 The Turtle is at (%d,%d)\n",name,x,y);
}
public static void main(String[] args) {
PrintStream outAlias = System.out;
Turtle tommy = new Turtle("Tommy",new Formatter(System.out));
Turtle terry = new Turtle("Terry",new Formatter(outAlias));
}
}
输出如下:
Tommy The Turtle is at (0,0)
Terry The Turtle is at (4,8)
Tommy The Turtle is at (3,4)
Terry The Turtle is at (2,5)
Tommy The Turtle is at (3,3)
Terry The Turtle is at (3,3)
所有的tommy都将输出到System.out,⽽所有的terry则输出到System.out的⼀个别名上,Formatter构造器经过重载可以接受多种输出⽬的地,不过最常⽤的还是PrintStream()、OutputStream和File。
3、格式化说明符
在插⼊数据时,如果想要控制空格与对齐,则需要使⽤更精细复杂的格式修饰符。以下是其抽象的语法:
%[argument_index$][flags][width][.precision]conversion
argument_index$:argument_index 是⼀个⼗进制整数,⽤于表明参数在参数列表中的位置。第⼀个参数由 “1$” 引⽤,第⼆个参数由“2$” 引⽤,依此类推;
flags:可选 flags 是修改输出格式的字符集。有效标志集取决于转换类型;
width:控制⼀个域的最⼩值,默认情况下下是右对齐的,不过可以通过使⽤“-”标志来改变对其⽅向;
precision:精度,⽤于String时,表⽰输出字符的最⼤数量,⽤于浮点数时,表⽰⼩数部分要显⽰出来的位数(默认是6位),多则舍⼊,少则补0,⽤于整数会触发异常;
conversion:转换格式,可选的格式有:
d整数型(⼗进制)
c Unicode字符
b Boolean值
s String
f浮点数(⼗进制)
e浮点数(科学计数)
X整数(⼗六进制)
h散列码(⼗六进制)
%字符串“%”
注意当使⽤b作为转换格式时,即Boolean,对于boolean基本类型或者Boolean对象,其转换结果是对应的true或false。但是对于其他类型的参数,只要该参数不为null,那么该转换的结果就永远都是true。0也会转换为true的,跟其他语⾔有所区别。所以将b运⽤于⾮布尔类型要注意。
下⾯的程序应⽤格式修饰符来打印⼀个购物收据:
import java.util.*;
public class Receipt {
private double total = 0;
private Formatter f = new Formatter(System.out);printf怎么格式化输出
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) {
//%-15.15s:字符串左对齐,最⼩长度为15,最⼤长度也为15
//%10.2f:最⼩长度为10,其中⼩数占两位
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 Peas",3, 5.1);
receipt.print("Three Bears Porridge",1,14.29);
receipt.printTotal();
}
}
输出如下:
Item              Qty      Price
---              ---      -----
Jack's Magic Be    4      4.25
Princess Peas      3      5.10
Three Bears Por    1      14.29
Tax                        1.42
-----
Total                      25.06
4、String.format()
Java SE5参考了C语⾔中的sprintf()⽅法,以⽣成格式化的String对象。String.format()是⼀个静态⽅法,它接受与Formatter.format()⽅法⼀样的参数,但是返回⼀个String对象。
下⾯演⽰⼀个String.format()的例⼦:
public class DatabaseException extends Exception{
public DatabaseException(int transactionID,int queryID,String message) {
//调⽤基类构造函数
super(String.format("(t%d,q%d) %s", transactionID,queryID,message));
}
public static void main(String[] args) {
try {
throw new DatabaseException(3,7,"Write filed!");
}catch(Exception e) {
System.out.Message());
}
}
}
输出如下:
(t3,q7) Write filed!
其实在String.format()的内部,也是创建了⼀个Formatter对象,然后将传⼊的参数转给该Formatter。不过,与其⾃⼰做这些事情,不如使⽤快捷的String.format()⽅法,何况这样的代码更清晰易懂。
三正则表达式
正则表达式是⼀种强⼤⽽灵活的⽂本处理⼯具。
正则表达式定义了字符串的模式;
正则表达式可以⽤来搜索、编辑或处理⽂本;
正则表达式并不仅限于某⼀种语⾔,但是在每种语⾔中有细微的差别;
1、正则表达式实例
⼀个字符串其实就是⼀个简单的正则表达式,例如 Hello World 正则表达式匹配 "Hello World" 字符串。
.(点号)也是⼀个正则表达式,它匹配任何⼀个字符如:"a" 或 "1"。
下表列出了⼀些正则表达式的实例及描述:
正则表达式描述
this is text匹配字符串 "this is text"
this\s+is\s+text 注意字符串中的 \s+。
匹配单词 "this" 后⾯的 \s+ 可以匹配多个空格,之后匹配 is 字符串,再之后 \s+ 匹配多个空格然后再跟上 text 字符串。
可以匹配这个实例:this is text
^ 定义了以什么开始
\d+ 匹配⼀个或多个数字
? 设置括号内的选项是可选的
正则表达式描述
^\d+(\.\d+)?
\. 匹配 "."
可以匹配的实例:"5", "1.5" 和 "2.21"。
注意:
在其它语⾔中,\\ 表⽰:我想要在正则表达式中插⼊⼀个普通的(字⾯上的)反斜杠,请不要给它任何特殊的意义。
在 Java 中,\\ 表⽰:我要插⼊⼀个正则表达式的反斜线,所以其后的字符具有特殊的意义。
所以,在其它的语⾔中(如Perl),⼀个反斜杠 \ 就⾜以具有转义的作⽤,⽽在 Java 中正则表达式中则需要有两个反斜杠才能被解析为其它语⾔中的转义作⽤。也可以简单的理解在 Java 的正则表达式中,两个 \\ 代表其他语⾔中的⼀个 \,因此,在Java中表⽰⼀位数字的正则表达式为\\d,⽽表⽰⼀个普通的反斜杠为 \\\\。
Java 正则表达式和 Perl 的是最为相似的。
包主要包括以下三个类:
Pattern 类:
pattern 对象是⼀个正则表达式的编译表⽰。Pattern 类没有公共构造⽅法。要创建⼀个 Pattern 对象,你必须⾸先调⽤其公共静态编译⽅法,它返回⼀个 Pattern 对象。该⽅法接受⼀个正则表达式作为它的第⼀个参数。⽐如:Pattern r = Patternpile("(\\D*)(\\d+)(.*)");
Matcher 类:
Matcher 对象是对输⼊字符串进⾏解释和匹配操作的引擎。与Pattern 类⼀样,Matcher 也没有公共构造⽅法。你需要调⽤ Pattern 对象的 matcher ⽅法来获得⼀个 Matcher 对象。⽐如:Matcher m = r.matcher("This order was placed for QT3000! OK?");
PatternSyntaxException:
PatternSyntaxException 是⼀个⾮强制异常类,它表⽰⼀个正则表达式模式中的语法错误。
以下实例中使⽤了正则表达式 .*runoob.* ⽤于查字符串中是否包了 runoob ⼦串:
import *;
class RegexExample1{
public static void main(String args[]){
String content = "I am noob " +
"from runoob.";
String pattern = ".*runoob.*";
boolean isMatch = Pattern.matches(pattern, content);
System.out.println("字符串中是否包含了 'runoob' ⼦字符串? " + isMatch);
}
}
输出结果:
字符串中是否包含了 'runoob' ⼦字符串? true
2、捕获组
捕获组是把多个字符当⼀个单独单元进⾏处理的⽅法,它通过对括号内的字符分组来创建。
例如,正则表达式 (dog) 创建了单⼀分组,组⾥包含"d","o",和"g"。
捕获组是通过从左⾄右计算其开括号来编号。例如,在表达式((A)(B(C))),有四个这样的组:
((A)(B(C)))
(A)
(B(C))
(C)
可以通过调⽤ matcher 对象的 groupCount ()⽅法来查看表达式有多少个分组。groupCount() ⽅法返回⼀个 int 值,表⽰matcher对象当前有多个捕获组。
还有⼀个特殊的组(group(0)),它总是代表前⼀次匹配操作(例如find())的结果。该组不包括在 groupCount 的返回值中。
下⾯的例⼦说明如何从⼀个给定的字符串中到数字串:
import Matcher;
import Pattern;

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。