public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];
public String() {
this.value = new char[0];
}
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
public String(char value[], int offset, int count) {
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
// Note: offset or count might be near -1>>>1.
if (offset > value.length - count) {
throw new StringIndexOutOfBoundsException(offset + count);
}
this.value = Arrays.copyOfRange(value, offset, offset+count);
}
public String replace(CharSequence target, CharSequence replacement) {
return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
}
public String toLowerCase(Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
int firstUpper;
final int len = value.length;
/* Now check if there are any characters that need to be changed. */
scan: {
for (firstUpper = 0 ; firstUpper < len; ) {
char c = value[firstUpper];
if ((c >= Character.MIN_HIGH_SURROGATE)
&& (c <= Character.MAX_HIGH_SURROGATE)) {
int supplChar = codePointAt(firstUpper);
if (supplChar != Character.toLowerCase(supplChar)) {
break scan;
}
firstUpper += Character.charCount(supplChar);
} else {
if (c != Character.toLowerCase(c)) {
break scan;
}
firstUpper++;
}
}
return this;
}
char[] result = new char[len];
int resultOffset = 0; /* result may grow, so i+resultOffset
* is the write location in result */
/* Just copy the first few lowerCase characters. */
System.arraycopy(value, 0, result, 0, firstUpper);
String lang = locale.getLanguage();
boolean localeDependent =
(lang == "tr" || lang == "az" || lang == "lt");
char[] lowerCharArray;
int lowerChar;
int srcChar;
int srcCount;
for (int i = firstUpper; i < len; i += srcCount) {
srcChar = (int)value[i];
if ((char)srcChar >= Character.MIN_HIGH_SURROGATE
&& (char)srcChar <= Character.MAX_HIGH_SURROGATE) {
srcChar = codePointAt(i);
srcCount = Character.charCount(srcChar);
} else {
srcCount = 1;
}
if (localeDependent || srcChar == '\u03A3') { // GREEK CAPITAL LETTER SIGMA
lowerChar = ConditionalSpecialCasing.toLowerCaseEx(this, i, locale);
} else {
lowerChar = Character.toLowerCase(srcChar);
}
if ((lowerChar == Character.ERROR)
|| (lowerChar >= Character.MIN_SUPPLEMENTARY_CODE_POINT)) {
if (lowerChar == Character.ERROR) {
lowerCharArray =
ConditionalSpecialCasing.toLowerCaseCharArray(this, i, locale);
} else if (srcCount == 2) {
resultOffset += Character.toChars(lowerChar, result, i + resultOffset) - srcCount;
continue;
} else {
lowerCharArray = Character.toChars(lowerChar);
}
/* Grow result if needed */
int mapLen = lowerCharArray.length;
if (mapLen > srcCount) {
char[] result2 = new char[result.length + mapLen - srcCount];
System.arraycopy(result, 0, result2, 0, i + resultOffset);
result = result2;
}
for (int x = 0; x < mapLen; ++x) {
result[i + resultOffset + x] = lowerCharArray[x];
}
resultOffset += (mapLen - srcCount);
} else {
result[i + resultOffset] = (char)lowerChar;
}
}
return new String(result, 0, len + resultOffset);
}
public class Immutable {
//每当把String对象作为方法的参数时,都会复制一份引用
//而该引用所指的对象其实一直待在单一的物理位置上,从未动过。
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);
//String can't change
System.out.println(q);
}
}
2、String Pool
String Pool,字符串池。(位于堆中)
- 常量池在java用于保存在编译期已确定的,已编译的class文件中的一份数据。
- 它包括了关于类,方法,接口等中的常量,也包括字符串常量,如String s = "java"这种申明方式;
- 当然也可扩充,执行器产生的常量也会放入常量池,故认为常量池是JVM的一块特殊的内存空间。
java中的常量池技术,是为了方便快捷地创建某些对象而出现的,当需要一个对象时,就可以从池中取一个出来(如果池中没有则创建一个),则在需要重复创建相等变量时节省了很多时间。常量池其实也就是一个内存空间,常量池存在于方法区中。
常量表类型
|
标志值(占1 byte)
|
描述
|
CONSTANT_Utf8
|
1
|
UTF-8编码的
Unicode字符串
|
CONSTANT_Integer
|
3
|
int类型的字面值
|
CONSTANT_Float
|
4
|
float类型的字面值
|
CONSTANT_Long
|
5
|
long类型的字面值
|
CONSTANT_Double
|
6
|
double类型的字面值
|
CONSTANT_Class
|
7
|
对一个类或接口的符号引用
|
CONSTANT_String
|
8
|
String类型字面值的引用
|
CONSTANT_Fieldref
|
9
|
对一个字段的符号引用
|
CONSTANT_Methodref
|
10
|
对一个类中方法的符号引用
|
CONSTANT_InterfaceMethodref
|
11
|
对一个接口中方法的符号引用
|
CONSTANT_NameAndType
|
12
|
对一个字段或方法的部分符号引用
|
2.1、采用字面值赋值方式创建对象
String str1 = "aaa";
String str2 = "aaa";
当以字符串直接创建String类的对象时,会在字符串池中查找是否已经存在该常量。
如上例中,会在String Pool中查找是否存在"aaa"这个对象,如果不存在,则在String Pool中创建一个"aaa"对象,然后将其地址返回,赋给变量str1,这样str1会指向String Pool中的这个"aaa"对象。
如果在String Pool中查找时已经存在该对象,则不创建任何对象,直接将String Pool中的这个对象地址返回,如上面str2直接得到"aaa"对象的地址,它和str1实际上指向同一个对象。
2.2采用new的形式创建对象
采用new的形式创建对象:
String str = new String("aaa");
实际上创建了两个String对象,一个是”aaa”对象,存储在常量空间中,一个是使用new关键字为对象申请的堆空间。
这种形式创建字符串对象时仍然会在String Pool中查找。
如果没有,则首先在String Pool中创建相应的对象,然后再在堆中创建一个对象;
若相同对象已经存在,则在堆中创建对象。
无论哪种情况,最后返回的地址都是堆中的地址。
如上例,首先在String Pool中查找有没有"aaa"这个字符串对象,如果有,则不在String Pool中再去创建"aaa"这个对象了,直接在堆(heap)中创建一个"aaa"字符串对象,然后将堆中的这个"aaa"对象的地址返回来,赋给str引用。
如果没有,则首先在String Pool中创建一个"aaa"对象,然后再在堆中创建一个"aaa"对象,然后将堆中的这个"aaa"对象的地址返回来,赋给str引用。
- 这样创建 1 个对象。
- JVM先到字符串池中查找,看是否已经存在值为"abc"的对象,如果存在,则不再创建新的对象,直接返回已存在对象的引用;
- 如果不存在,则先创建这个对象,然后把它加入到字符串池中,再将它的引用返回。
- 这样创建 2 个对象。
- 一个 是放在常量池中的”abc“对象。
- 另一个是由 new String()构造器在堆中创建一个“abc”对象。
- a本身只是一个引用,放在栈里,用来指向堆中创建出来的对象。
String b = "abc";
- 这样也只创建 1 个对象。
- 只在常量池中创建一个对象 "abc",然后将引用分别返回给a 和b。
- 这样创建 1 个对象。
- 由于常量字符串在编译时候也就确定的,又因为“abc”和“def”都是字符串常量,因此变量 a 的值在编译时就可以确定了。与 String a = "abcdef"; 一样。
结论:如果“+”连接的两字符串中只要有一个不是字面常量串(即定义过的),是会产生新的字符串对象。public class Concatenation2 {
public static void main(String[] args) {
String a = "abc";
String b = "def";
String c = "abcdef";
String d = "abc" + "def"; //不会产生新的字符串
System.out.println(c == d); //true
String e = a + "def";//会产生新的字符串对象
System.out.println(e == c); //false
String f = a + b; //会产生新的字符串
System.out.println(f == c); //false
}
}
public class Concatenation2 {
public static void main(String[] args) {
final String a = "abc";
final String b = "def";
String c = "abcdef";
/*
* 因为 a 和 b 都被定义成了常量,所以编译时就确定。
* 编译时会将常量替换,等同于 String d = "abc" + "def";
*/
String d = a + b;
System.out.println(d == c); //true
}
}
public class Concatenation2 {
public static void main(String[] args) {
final String a = "abc";
String b = "def";
String c = "abcdef";
/*
* 因为 a 为常量 编译时就确定,而b为不是常量不能编译时确定
* 因此 等同于 String d = "abc" + b"
*/
String d = a + b;
System.out.println(d == c); //false
}
}
public class Concatenation2 {
final static String a;
final static String b;
//此时 a 和 b 类似于变量,在类首次加载时才进行初始化
static{
a = "abc";
b = "def";
}
public static void main(String[] args) {
String c = "abcdef";
String d = a + b;
System.out.println(c == d); //false
}
}
public class Concatenation {
public static void main(String[] args) {
String a = "abc";
String b = "def";
String c = a + b;
String d = "abc" + "def";
String e = new String("abc");
System.out.println(a == e);
System.out.println(a.equals(e));
System.out.println(a == "abc");
System.out.println(a == e.intern());
System.out.println(c == "abcdef");
System.out.println(d == "abcdef");
}
}
/*
* 1、String a = "abc",会在堆中的常量池中创建"abc"这个对象然后把地址赋值给a
* String e = new String("abc"),首先在常量池里查找"abc"对象,
* 如果没有找到,就在常量池中 新创建"abc"对象,然后再将"abc"作为参数值传给String的构造函数在堆中 new 一个对象
* 如果找到该对象,则直接在堆中创建new一个"abc"对象,然后将引用返回。
* 因此 a 和 e 是两个不同的地址 ,所以 false。
* 2、a.equals(e) 比较的是字符串中的内容,因此 true。
* 3、a == "abc" , == 比较的是地址。 a 和 "abc"都是指向常量池对应对象的首地址 。 因此 true。
* 4、e.intern()从官方文档的说明可以看出:
* 首先在常量池中查找和e这个字符串等值的字符串(比较的方法是equals())
* 如果没有找到该字符串,则在常量池中创建一个字符串,然后返回该字符串的引用。
* 因为"abc"已经在常量池中,因此true。
* 5、6 后面讨论
*/
用 javap -c Concatenation2 或 javap -verbose Concatenation2反编译工具查看:public class Concatenation2 {
public static void main(String[] args) {
String a = "abc";
String b = "def";
String c = a + b ;
}
}
public class Concatenation2 {
public Concatenation2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String abc
2: astore_1
3: ldc #3 // String def
5: astore_2
6: new #4 // class java/lang/StringBuilder
9: dup
10: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
13: aload_1
14: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
17: aload_2
18: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
24: astore_3
25: return
}
- 首先看到是使用一个指针指向一个常量池的的对象内容为"abc",而另一个指针指向"def"
- 此时,通过new一个StringBuilder,然后对其进行初始化,接着调用两次append方法添加“abc”和“def”
- 然后调用toString()操作返回String类型的引用。
public class Concatenation2 {
public static void main(String[] args) {
String c = "abc" + "def";
}
}
public class Concatenation2 {
public Concatenation2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String abcdef
2: astore_1
3: return
}
String c = "abcdef";
public class Concatenation2 {
public static void main(String[] args) {
final String a = "abc";
final String b = "def";
String c = a + b;
}
}
public class Concatenation2 {
public Concatenation2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String abcdef
2: astore_3
3: return
}
在编译的时候,c 部分会被编译为:String c = "abc" + "def";
public class Concatenation2 {
public static void main(String[] args) {
String a = "abc";
final String b = "def";
String c = a + b;
}
}
反编译结果:
public class Concatenation2 {
public Concatenation2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String abc
2: astore_1
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: aload_1
11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: ldc #6 // String def
16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: astore_3
23: return
}
其次,如果a和b,是某个方法返回回来的,不论方法中是final类型还是常量什么的,都不会被编译时
将数据编译到常量池。因此编译器不会跟踪到方法里面看你做了什么。
String result = "";
for (int i = 0; i < 10000; i++) {
result += "hello ";
}
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: sipush 10000 //循环次数
9: if_icmpge 38
12: new #3 // class java/lang/StringBuilder
15: dup
16: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
19: aload_1
20: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
23: ldc #6 // String hello
25: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
31: astore_1
32: iinc 2, 1
35: goto 5
38: return
}
- if_icmpge 对堆栈中的操作数进行“大于或等于的整数比较运算” 。 循环结束跳转到第38行。
- 9-38行为一个循环体,在循环体中每循环一次就new一个StringBuilder对象,然后进行初始化,接着进行两次append,最后调用oString()方法。因此循环10000次就new了10000个对象。
- result += "hello "; 实际会被JVM优化成:
StringBuilder stringBuilder =stringBuilder.append(stringBuilder.append(stringBuilder.toString();
public String stringBuilder(){
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 10000; i++) {
stringBuilder.append("hello");
}
return stringBuilder().toString();
}
public java.lang.String stringBuilder();
Code:
0: new #2 // class java/lang/StringBuilder
3: dup
4: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V
7: astore_1
8: iconst_0
9: istore_2
10: iload_2
11: sipush 10000 //循环次数
14: if_icmpge 30
17: aload_1
18: ldc #4 // String hello
20: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
23: pop
24: iinc 2, 1
27: goto 10
30: aload_0
31: invokevirtual #6 // Method stringBuilder:()Ljava/lang/String;
34: invokevirtual #7 // Method java/lang/String.toString:()Ljava/lang/String;
37: areturn
- if_icmpge 对堆栈中的操作数进行“大于或等于的整数比较运算” 。 循环结束跳转到第30行。
- 14 - 30 行为一个循环体。StringBuilder 只在循环体外new一次和初始化一次。在循环体中每循环一次就append一次。
- 最后循环结束,将调用toString()方法返回。
- 因此,StringBuilder在如上这样的循环中销量比String += 高出很多。
那么有人会问既然有了StringBuilder类,为什么还需要StringBuffer类?查看源代码便一目了然,事实上,StringBuilder和StringBuffer类拥有的成员属性以及成员方法基本相同,区别是StringBuffer类的成员方法前面多了一个关键字:synchronized,不用多说,这个关键字是在多线程访问时起到安全保护作用的,也就是说StringBuffer是线程安全的。
@Override
public synchronized StringBuffer insert(int index, char[] str, int offset,
int len)
{
toStringCache = null;
super.insert(index, str, offset, len);
return this;
}
@Override
public StringBuilder insert(int index, char[] str, int offset,
int len)
{
super.insert(index, str, offset, len);
return this;
}
public class Concatenation2 {
private final static int time = 50000;
public static void stringBuilder()
{
//开始时间
long begin = System.currentTimeMillis();
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < time; i++)
{
stringBuilder.append("java");
}
//结束时间
long end = System.currentTimeMillis();
System.out.println(stringBuilder.getClass().getSimpleName()
+ " 所耗时间 " + (end-begin) + " 毫秒");
}
public static void stringBuffer()
{
//开始时间
long begin = System.currentTimeMillis();
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < time; i++)
{
stringBuffer.append("java");
}
//结束时间
long end = System.currentTimeMillis();
System.out.println(stringBuffer.getClass().getSimpleName()
+ " 所耗时间 " + (end-begin) + " 毫秒");
}
public static void string()
{
//开始时间
long begin = System.currentTimeMillis();
String s = "";
for (int i = 0; i < time; i++)
{
s += "java";
}
//结束时间
long end = System.currentTimeMillis();
System.out.println(s.getClass().getSimpleName()
+ " 所耗时间 " + (end-begin) + " 毫秒");
}
public static void main(String[] args) {
string();
stringBuilder();
stringBuffer();
}
}
public class StringAdd {
private final static int time = 50000;
public static void stringAddDirect()
{
//开始时间
long begin = System.currentTimeMillis();
String s = "";
for (int i = 0; i < time; i++)
{
s = "a" + "b" + "c" + "d";
}
//结束时间
long end = System.currentTimeMillis();
System.out.println("String直接相加测试时间为 " + (end - begin) + " 毫秒");
}
public static void stringAddIndirect()
{
//开始时间
long begin = System.currentTimeMillis();
String s = "";
String a = "a" , b = "b" , c = "c" , d = "d";
for (int i = 0; i < time; i++)
{
s = a + b + c + d;
}
//结束时间
long end = System.currentTimeMillis();
System.out.println("String间接相加测试时间为 " + (end - begin) + " 毫秒");
}
public static void main(String[] args) {
stringAddDirect();
stringAddIndirect();
}
}
public class AddOptimize {
private final static int time = 50000;
public static void testString()
{
//开始时间
long begin = System.currentTimeMillis();
String s = "";
for (int i = 0; i < time; i++)
{
s += "java";
}
long end = System.currentTimeMillis();
System.out.println(s.getClass().getSimpleName()
+ " 类型的 += 操作所使用的时间:" + (end - begin) + " 毫秒");
}
public static void testOptimizeString()
{
//开始时间
long begin = System.currentTimeMillis();
String s = "";
for (int i = 0; i < time; i++)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(s);
stringBuilder.append("java");
stringBuilder.toString();
}
long end = System.currentTimeMillis();
System.out.println("模拟JVM优化操作的时间:" + (end - begin) + " 毫秒");
}
public static void main(String[] args) {
testString();
testOptimizeString();
}
public class AddandAddEqual {
private final static int time = 50000;
public static void testAddString()
{
long begin = System.currentTimeMillis();
String s = "a";
for (int i = 0; i < time; i++)
{
s = s + "abc" + "def";
}
long end = System.currentTimeMillis();
System.out.println("s =" + " s +" + " abc " + "+" + " def 所使用时间:"
+ (end - begin) + " 毫秒");
}
public static void testAddEqualString()
{
long begin = System.currentTimeMillis();
String s = "a";
for (int i = 0; i < time; i++)
{
s += "abc" + "def";
}
long end = System.currentTimeMillis();
System.out.println("s +=" + " abc " + "+" + " def 所使用时间: "
+ (end - begin) + " 毫秒");
}
public static void main(String[] args) {
testAddString();
testAddEqualString();
}
}
public static void testAddString();
Code:
0: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
3: lstore_0
4: ldc #3 // String a
6: astore_2
7: iconst_0
8: istore_3
9: iload_3
10: ldc #5 // int 50000
12: if_icmpge 46
15: new #6 // class java/lang/StringBuilder
18: dup
19: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V
22: aload_2
23: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
26: ldc #9 // String abc
28: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
31: ldc #10 // String def
33: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: invokevirtual #11 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
39: astore_2
40: iinc 3, 1
43: goto 9
46: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
49: lstore_3
50: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream;
53: new #6 // class java/lang/StringBuilder
56: dup
57: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V
60: ldc #13 // String s = s + abc + def 所使用时间:
62: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
65: lload_3
66: lload_0
67: lsub
68: invokevirtual #14 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
71: ldc #15 // String 毫秒
73: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
76: invokevirtual #11 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
79: invokevirtual #16 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
82: return
public static void testAddEqualString();
Code:
0: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
3: lstore_0
4: ldc #3 // String a
6: astore_2
7: iconst_0
8: istore_3
9: iload_3
10: ldc #5 // int 50000
12: if_icmpge 41
15: new #6 // class java/lang/StringBuilder
18: dup
19: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V
22: aload_2
23: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
26: ldc #17 // String abcdef
28: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
31: invokevirtual #11 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
34: astore_2
35: iinc 3, 1
38: goto 9
41: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
44: lstore_3
45: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream;
48: new #6 // class java/lang/StringBuilder
51: dup
52: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V
55: ldc #18 // String s += abc + def 所使用时间:
57: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
60: lload_3
61: lload_0
62: lsub
63: invokevirtual #14 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
66: ldc #15 // String 毫秒
68: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
71: invokevirtual #11 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
74: invokevirtual #16 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
77: return
下面对上面的执行结果进行一般性的解释:
1)对于直接相加字符串,效率很高,因为在编译器便确定了它的值,也就是说形如"I"+"love"+"java"; 的字符串相加,在编译期间便被优化成了"Ilovejava"。这个可以用javap -c命令反编译生成的class文件进行验证。
对于间接相加(即包含字符串引用),形如s1+s2+s3; 效率要比直接相加低,因为在编译器不会对引用变量进行优化。
2)String、StringBuilder、StringBuffer三者的执行效率:
StringBuilder > StringBuffer > String
当然这个是相对的,不一定在所有情况下都是这样。
比如String str = "hello"+ "world"的效率就比 StringBuilder st = new StringBuilder().append("hello").append("world")要高。
因此,这三个类是各有利弊,应当根据不同的情况来进行选择使用:
当字符串相加操作或者改动较少的情况下,建议使用 String str="hello"这种形式;
当字符串相加操作较多的情况下,建议使用StringBuilder,如果采用了多线程,则使用StringBuffer。
public class Splitting {
public static String knights =
"Then, when you hava found the shrubbery,you must " +
"cut dowm the migjties tree in the forest..." +
"with...a herring!";
public static void split(String regex)
{
System.out.println(
Arrays.toString(knights.split(regex)));
}
public static void main(String[] args) {
//将字符串从正则表达式匹配的地方切开
split(" "); //Does't hava to contain regex chars
split("\\W+"); //Non-word characters
split("n\\W+"); // 'n' followed by non-word characters
//替换
System.out.println(knights.replaceFirst("f\\w+", "located"));
System.out.println(knights.replaceAll("shrubbery|tree|herring", "banana"));
// Pattern.matches(String regex,CharSequence input)
for(String pattern : new String[]{
"Rudolph","[rR]udolph","[rR][aeiou][a-z]ol.*","R.*"})
{
System.out.println("Rudolph".matches(pattern));
}
}
}
Predefined character classes | |
---|---|
. | Any character (may or may not match line terminators) |
\d | A digit: [0-9] |
\D | A non-digit: [^0-9] |
\h | A horizontal whitespace character: [ \t\xA0\u1680\u180e\u2000-\u200a\u202f\u205f\u3000] |
\H | A non-horizontal whitespace character: [^\h] |
\s | A whitespace character: [ \t\n\x0B\f\r] |
\S | A non-whitespace character: [^\s] |
\v | A vertical whitespace character: [\n\x0B\f\r\x85\u2028\u2029] |
\V | A non-vertical whitespace character: [^\v] |
\w | A word character: [a-zA-Z_0-9] |
\W | A non-word character: [^\w] |
Character classes | |
---|---|
[abc] | a , b , or c (simple class) |
[^abc] | Any character except a , b , or c (negation) |
[a-zA-Z] | a through z or A through Z , inclusive (range) |
[a-d[m-p]] | a through d , or m through p : [a-dm-p] (union) |
[a-z&&[def]] | d , e , or f (intersection) |
[a-z&&[^bc]] | a through z , except for b and c : [ad-z] (subtraction) |
[a-z&&[^m-p]] | a through z , and not m through p : [a-lq-z] (subtraction) |
Logical operators | |
---|---|
XY | X followed by Y |
X| Y | Either X or Y |
( X) | X, as a capturing group |
Boundary matchers | |
---|---|
^ | The beginning of a line |
$ | The end of a line |
\b | A word boundary |
\B | A non-word boundary |
\A | The beginning of the input |
\G | The end of the previous match |
\Z | The end of the input but for the final terminator, if any |
\z | The end of the input |
Greedy quantifiers
|
Reluctant quantifiers
|
Possessive quantifiers
|
|
X? | X?? |
X?+
|
X,once or not at all
|
X* |
X*?
|
X*+
|
X,zero or more times
|
X+ |
X+?
|
X++
|
X,one or more times
|
X{n}
| X{n}? |
X{n}+
|
X,exactly n times
|
X{n, } | X{n, }? |
X{n, }+
|
X,at least n times
|
X{n,m}
| X{n,m}? | X{n,m}+ | X,at least n but not more than m times |
- 贪婪型:量词总是贪婪的,除非有其他选项被设置。贪婪表达式会为所有可能的模式发现尽可能多的匹配。导致此问题的一个典型理由就是假定我们的模式仅能匹配第一个可能的字符组,如果它是贪婪的,那么它就会继续往下匹配。
- 勉强型:用问号来指定,这个量词匹配满足模式所需要的最少字符数。一次也称作:懒惰的、最少匹配的、非贪婪的或不贪婪的。
- 占有型:目前,这种类型的量词只有在Java语言中才可用(在其他语言中不可用),并且也更高级。当正则表达式被应用于字符串时,它会产生相当多的状态,以便在匹配失败时可以回溯。而”占有的“量词并不保存这些中间状态,因此它们可以防止回溯。它们常常用于防止正则表达式的失控,因此可使正则表达式执行起来更有效。
public interface CharSequence {
int length();
char charAt(int index);
CharSequence subSequence(int start, int end);
public String toString();
}
public class TestRegularExpression {
public static void main(String[] args) {
if(args.length < 2)
{
System.out.println("Usage:\njava TestRegularExpression" +
"characterSequence regularExpression");
System.exit(0);
}
System.out.println("Input:\" " + args[0] + " \"");
for(String arg : args)
{
System.out.println("Regular expression:\" " + arg + " \"");
// static Pattern.compile(String regex) -->>编译正则表达式,生成Pattern对象
// Pattern.split(CharSequence input) -->> 用于进行字符串分割
/*
* Pattern pattern = Pattern.compile(arg); 参数 arg 表示正则表达式的字符串
Matcher m = pattern.matcher(args[0]); 参数 args[0] 表示将要进行匹配的字符串
*/
Pattern pattern = Pattern.compile(arg);
Matcher m = pattern.matcher(args[0]);
while(m.find())
{
System.out.println("Match \" " + m.group() + " \" at positions "
+ m.start() + " - " + m.end());
}
}
}
}
public class Finding {
public static void main(String[] args) {
Matcher m = Pattern.compile("\\w+").matcher("" +
"Evening is full of the full 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++;
}
}
}
public class Groups {
static public final String POEM =
"Twas brillig,and the slithy toves\n" +
"Did gyre and gimble in the wabe.\n" +
"All mimsy were the borogoves,\n" +
" And the mome raths outgrabe.\n" +
" 'Beware the Jabberwock,my son,\n" +
" The jaws that bite,the claws that catch.\n" +
" Beware the Jubjub bird,and shun\n" +
" The frumious Bandersnatch.";
public static void main(String[] args) {
Matcher m = Pattern.compile("(?m)(\\S+)\\s+((\\S+)\\s+(\\S+))$").matcher(POEM);
System.out.println(m.groupCount());
while (m.find())
{
for (int i = 0; i < m.groupCount(); i++)
{
System.out.print("[ " + m.group(i) + " ]");
}
System.out.println();
}
}
}
public static Pattern compile(String regex,int flags)
Modifier and Type | Field and Description |
---|---|
static int | CANON_EQ
Enables canonical equivalence.
|
static int | CASE_INSENSITIVE (?i)
Enables case-insensitive matching.
|
static int | COMMENTS (?x)
Permits whitespace and comments in pattern.
|
static int | DOTALL (?s)
Enables dotall mode.
|
static int | LITERAL
Enables literal parsing of the pattern.
|
static int | MULTILINE (?m)
Enables multiline mode.
|
static int | UNICODE_CASE (?u)
Enables Unicode-aware case folding.
|
static int | UNICODE_CHARACTER_CLASS
Enables the Unicode version of
Predefined character classes and
POSIX character classes.
|
static int | UNIX_LINES (?d)
Enables Unix lines mode.
|
public class TheReplacements {
public static void main(String[] args) {
String s =
"/*!Here's a block of text to use as input to " +
" the ragular expression matcher. Note that we'll " +
" first special delimiters,then process the " +
" extrated block.!*/";
System.out.println(s);
Matcher mInput = Pattern.compile("/\\*!(.*)!\\*/",Pattern.DOTALL).matcher(s);
if(mInput.find())
{
s = mInput.group(1);
}
System.out.println(s);
// 替换所有匹配成功的
s = s.replaceAll(" {2,}", " ");
s.replaceAll("(?m)^ +", "");
System.out.println(s);
// 替换第一次匹配成功的
s = s.replaceFirst("[aeiou]", "(VOWEL1)");
System.out.println(s);
StringBuffer stringBuffer = new StringBuffer();
Pattern pattern = Pattern.compile("[aeiou]");
Matcher matcher = pattern.matcher(s);
// 从位置0开始匹配
while(matcher.find())
{
// Matcher.appendReplacement() 允许在执行替换的过程中,操作用来替换的字符串
// 将匹配后的字符串(即匹配并操作到的最后位置的结果)放入stringBuffer中
matcher.appendReplacement(stringBuffer, matcher.group().toUpperCase());
}
System.out.println(stringBuffer);
// 将匹配截止位置后的字符串添加到stringBuffer中
matcher.appendTail(stringBuffer);
System.out.println(stringBuffer);
}
}
public class Resetting {
public static void main(String[] args) {
Matcher matcher = Pattern.compile("[frb][aiu][gx]")
.matcher("fix the rug with bags");
while (matcher.find())
{
System.out.print(matcher.group() + " ");
}
System.out.println();
// 将位置重新移到开始位置
matcher.reset();
while (matcher.find())
{
System.out.print(matcher.group() + " ");
}
System.out.println();
// 将现有Matcher对象应用于一个新的字符序列
matcher.reset("fix the rig with rags");
while (matcher.find())
{
System.out.print(matcher.group() + " ");
}
}
}
public class SimpleRead {
public static BufferedReader input = new BufferedReader(
new StringReader("Sir Robin of Camelot\n22 1.61038"));
public static void main(String[] args) {
try{
System.out.println("What is your name ?");
String name = input.readLine();
System.out.println(name);
System.out.println("How old are you ? What is your favorite double ?");
System.out.println("input : <age> <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 = " + age + " , " + "favorite = " + favorite);
}catch(IOException e){
System.out.println(e);
}
}
}
public class BetterRead {
public static void main(String[] args) {
Scanner stdin = new Scanner(SimpleRead.input);
System.out.println("What is your name ?");
// Scanner.nextLine()
//-->Advances this scanner past the current line and returns the input that was skipped.
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.println("age = " + age + " , " + "favorite = " + favorite);
}
}
public class ThreatAnalyzer {
static String threatData =
"58.27.82.161@02/10/2005\n" +
"204.45.234.40@02/11/2005\n" +
"58.27.82.161@02/11/2005\n" +
"58.27.82.161@02/12/2005\n" +
"58.27.82.161@02/12/2005\n";
public static void main(String[] args) {
Scanner scanner = new Scanner(threatData);
String pattern = "((\\d+[.]){3}\\d+)@" +
"(\\d{2}/\\d{2}/\\d{4})";
while (scanner.hasNext(pattern))
{
// 找到下一个匹配该模式的输入部分
scanner.next(pattern);
// 将匹配结果赋值给 MatchResult 对象
MatchResult matchResult = scanner.match();
String ip = matchResult.group(1);
String date = matchResult.group(3);
System.out.println("Threat on " + date + " form " + ip);
}
}
}