1, 常量池
Java 被编译成 class 文件时, 会生成一个常量池 (Constant pool) 的数据结构, 用于保存字面常量和符号引用(类名, 方法名, 接口名和字段名等). 常量池的内存在 Java 堆上进行分配, 意味着常量池不受固定大小的限制.
2, 字符串初始化
字符串初始化的方式有两种: 字面常量, String 对象
(1)如下一段代码:
- String a = "java";
- String b = "java";
- String c = "ja" + "va";
- System.out.println("一致性"+(a==b));
- System.out.println("一致性"+(a==c));
- System.out.println("一致性"+(b==c));
输出结果是:
image
我们通过 javap -c 命令分析一下字节码指令的实现:
image
通过上图可以看出, ldc 指令将 int,float,String 等类型的常量值从常量池中推送到栈顶, 所以 a,b 都指向常量池中 "java" 字符串, 同时也可以发现 c 也指向常量池中 "java" 字符串, 那是因为在编译期间, 表达式 "ja"+"va" 已经将结果值 "java" 直接赋值给 c
(2)如下一段代码:
- String a = "java";
- String c = new String("java");
- System.out.println((a==c));
输出结果为:
image
我们通过 javap -c 命令分析一下字节码指令的实现:
image
通过上图可以分析出, a 指向常量池中的 "java" 字符串, c 指向 Java 堆中新建的 String 对象, 而该对象的 char 数组则指向常量池中的 "java" 字符串, 所以得出结论, a != c
(3)如下一段代码:
- String a = "ja";
- String b = "va";
- String c = a + b;
- String d = "java";
- System.out.println(c == d);
输出结果:
image
我们通过 javap -c 命令分析一下字节码指令的实现:
image
可以看到会创建一个 StringBuilder 对象, 然后将 a,b 拼接起来, 然后通过 toString 方法产生一个 String 对象 c, 而 d 是直接指向常量池中的字符串 "java", 所以得出 c != d
(4)但如果是如下代码:
- final String a = "ja";
- final String b = "va";
- String c = a + b;
- String d = "java";
- System.out.println(c == d);
则能输出 true
我们通过 javap -c 命令分析一下字节码指令的实现:
image
通过上图可以看出, 用 final 修饰后, 在编译期间, 就已经将 a 和 b 拼接好赋值给 c 了, 然后 c 和 d 同时指向常量池中字符串 "java", 所以得出的结论是 c == d
3,String,StringBuffer 和 StringBuilder 的区别
三者本质上都是字符数组
(1)String 是不可变的对象, 因此每次改变 String 的时候, 都会产生一个新的 String 对象, 然后将指针指向新的 String 对象, 所以经常改变内容的字符串不要使用 String, 如果要操作少量的数据, 则可以使用 String
(2)单线程操作大量数据, 使用 StringBuilder, 因为不用保证线程同步, 所以速度更快
(3)多线程操作大量数据, 使用 StringBuffer, 因为需要考虑线程同步, 所以速度更慢一些
来源: http://www.jianshu.com/p/2ff23980a656