String

Java字符串是Unicode字符序列。如串"Java\u2122"有5个Unicode字符组成。
Java中字符串一般被认为是不可变的。当对字符串赋值时,实际是将原本的字符串引用指向了另一个字符串。这样做是否会降低运行效率呢?毕竟这样会产生大量的无用对象,况且修改一个代码单元要比创建一个新的字符串更加简洁。确实,通过拼接来创建字符串效率确实不高,但是不可变字符串却有一个优点:编译器可以让字符串共享。可以想象将各种字符串存放在公共的存储池中。字符串变量指向存储池中响应的位置。如果复制一个字符串变量,原始字符串与复制的字符串共享相同的字符。
总而言之,Java的设计者认为共享带来的高效率远远胜于提取、拼接字符串所带来的低效率(who knows?)。

s.equals(t)###

在这里将调用字符串的equals方法与目标字符串进行比较,比较的对象为字符串的值。<strong>注意一定不能使用==运算符来检测字符串是否相等。</strong>这个运算符只能确定两个字符串是否放在同一个位置上。当然,如果字符串放置在同一个位置上,它们必然相等。但是完全有可能将内容相同的多个字符串的拷贝放置在不同的位置上。
如果虚拟机始终将相同的字符串共享,就可以使用==运算符来检测是否相等。但实际上只有字符串常量是共享的,而+substring等操作产生的结果并不是共享的。
C++中像Java中重载字符串相加的+符号那样重载了==符号用于字符串间的比较,但Java中并没有这样做。

空串和Null串###

有时要检查一个字符串既不是null也不为空串,可以使用以下条件
if (str != null && str.length() != 0)
切记要先检查不为null,因为对空对象调用length方法会引发异常。

代码点和代码单元###

Java字符串由char序列组成。char数据类型是一个采用UTF-16编码表示Unicode代码点的代码单元。代码点是指字符在Unicode编码映射表上对应的数字。大多数的常用Unicode字符使用一个代码单元就可以表示,而辅助字符需要一对代码单元表示。
length方法将返回采用UTF-16编码表示的给定字符串所需要的代码单元数量,例如:

String greeting = "hello";
int n = greeting.length();  // n = 5;

要想得到实际的长度,即代码点的数量,可以调用:

String greeting = "hello";
int cpCount = greeting.codePointCount(0, greeting.length());  // n = 5;

调用s.charAt(n)将返回位置n的代码单元,n结余0~s.length() - 1之间,例如

char first = greeting.charAt(0);  // 'H'

要想获得第i个代码点,应该使用下列语句:

int index = greeting.offsetByCodePoints(0, i);
int cp = greeting.codePointAt(index);

遍历代码点

for(int i = ; i < str.length; i++) {
    int cp = str.codePointAt(i);
    if (Character.isSupplementaryPoint(cp)) i++;
    System.out.println(new String(Character.toChars(cp)));
}

构建字符串###

//:
当使用在字符串之间重载的+运算符时,为了生成最终的String,产生了大量的需要GC回收的对象。

package com.cnsumi.l.string;

public class StringConcatTest {
    public static void main(String[] args) {
        String mango = "mango";
        String s = "abc" + mango + "def" + 47;
        System.out.println(s);
    }
}

javac StringConcatTest.java
java -c StringConcatTest

public class com.cnsumi.l.string.StringConcatTest {
public com.cnsumi.l.string.StringConcatTest();
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 mango
2: astore_1
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: ldc #5 // String abc
12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: aload_1
16: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: ldc #7 // String def
21: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: bipush 47
26: invokevirtual #8 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
29: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
32: astore_2
33: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
36: aload_2
37: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
40: return
}

在编译后的字节码中可以看到JVM自动引入了java.lang.StringBuilder类(#3),虽然在源码中我们并没有使用StringBuilder类,但是编译器却自作主张的使用了它,因为它更高效。
在这个例子中,编译器创建了一个StringBuilder对象,不断的调用其append方法,最终调用toString生成最终的结果。
虽然编译器在这个例子中优化我们的代码,但这 不代表着我们就可以随意使用+来拼接字符串,因为编译器并没有优化到那一层次。

package com.cnsumi.l.string;

public class WhitherStringBuilder {
public String implicit(String[] fields) {
String ret = "";
for (String string : fields) {
ret += string;
}
return ret;
}

public String explicit(String[] fields) {
    StringBuilder ret = new StringBuilder();
    for (String string : fields) {
        ret.append(string);
    }
    return ret.toString();
}

}

`javac WhitherStringBuilder.java`
`javap -c WhitherStringBuilder`

public class com.cnsumi.l.string.WhitherStringBuilder {
public com.cnsumi.l.string.WhitherStringBuilder();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return

public java.lang.String implicit(java.lang.String[]);
Code:
0: ldc #2 // String
2: astore_2
3: aload_1
4: astore_3
5: aload_3
6: arraylength
7: istore 4
9: iconst_0
10: istore 5
12: iload 5
14: iload 4
16: if_icmpge 51
19: aload_3
20: iload 5
22: aaload
23: astore 6
25: new #3 // class java/lang/StringBuilder
28: dup
29: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
32: aload_2
33: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: aload 6
38: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
41: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
44: astore_2
45: iinc 5, 1
48: goto 12
51: aload_2
52: areturn

public java.lang.String explicit(java.lang.String[]);
Code:
0: new #3 // class java/lang/StringBuilder
3: dup
4: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
7: astore_2
8: aload_1
9: astore_3
10: aload_3
11: arraylength
12: istore 4
14: iconst_0
15: istore 5
17: iload 5
19: iload 4
21: if_icmpge 43
24: aload_3
25: iload 5
27: aaload
28: astore 6
30: aload_2
31: aload 6
33: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: pop
37: iinc 5, 1
40: goto 17
43: aload_2
44: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
47: areturn
}

可以看到第一个方法的字节码中创建StringBuilder对象是在循环之内,而第二个方法是在循环之外。可见,编译器或许只能优化同一句中的`+`操作。

###浅谈Java String###
原文地址<a>http://www.importnew.com/21711.html</a>

####常量池####
Java代码被编译成class文件时,会生成一个常量池(Constant pool)的数据结构,用以保存字面常量和符号引用(类名、方法名、接口名和字段名等)。

package com.ctrip.ttd.whywhy;
public class Test {
public static void main(String[] args) {
String test = "test";
}
}

很简单的一段代码,通过命令 javap -verbose 查看class文件中 Constant pool 实现:

Constant pool:

1 = Methodref #4.#13 // java/lang/Object."<init>":()V

2 = String #14 // test

3 = Class #15 // com/ctrip/ttd/whywhy/test

4 = Class #16 // java/lang/Object

5 = Utf8 <init>

6 = Utf8 ()V

7 = Utf8 Code

8 = Utf8 LineNumberTable

9 = Utf8 main

10 = Utf8 ([Ljava/lang/String;)V

11 = Utf8 SourceFile

12 = Utf8 test.java

13 = NameAndType #5:#6 // "<init>":()V

14 = Utf8 test

15 = Utf8 com/ctrip/ttd/whywhy/test

16 = Utf8 java/lang/Object

通过反编译出来的字节码可以看出字符串 "test" 在常量池中的定义方式:

2 = String #14 // test

14 = Utf8 test

在main方法字节码指令中,0 ~ 2行对应代码 String test = "test"; 由两部分组成:ldc #2 和 astore_1。

// main方法字节码指令
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String test
2: astore_1
3: return

1、Test类加载到虚拟机时,”test”字符串在Constant pool中使用符号引用symbol表示,当调用 ldc #2 指令时,如果Constant pool中索引 #2 的symbol还未解析,则调用C++底层的 StringTable::intern 方法生成char数组,并将引用保存在StringTable和常量池中,当下次调用 ldc #2 时,可以直接从Constant pool根据索引 #2获取 “test” 字符串的引用,避免再次到StringTable中查找。

2、astore_1指令将”test”字符串的引用保存在局部变量表中。

常量池的内存分配 在 JDK6、7、8中有不同的实现:
1、JDK6及之前版本中,常量池的内存在永久代PermGen进行分配,所以常量池会受到PermGen内存大小的限制。
2、JDK7中,常量池的内存在Java堆上进行分配,意味着常量池不受固定大小的限制了。
3、JDK8中,虚拟机团队移除了永久代PermGen。

####字符串初始化####
字符串可以通过两种方式进行初始化:字面常量和String对象。
#####字面常量#####

public class StringTest {
public static void main(String[] args) {
String a = "java";
String b = "java";
String c = "ja" + "va";
}
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容