首先来看一道题。
题目描述
问下面两种赋值方式有何区别?
public class Demo { |
分析与解答
从表面其实看不出什么,我们可以通过 Class 文件反编译成的字节码(Byte Code)来分析。
如果你在使用 IDEA,请先在 IDEA 中安装 ASMified Bytecode Outline 插件,点击 安装详细教程,如果是其他集成环境,请自行 Google 安装插件教程。
ASMified Bytecode Online 插件作用:用于将 Class 文件反编译成字节码(Byte Code)形式。将上面代码在 IDEA 中运行后,生成的字节码(Byte Code)如下图所示:
... |
看不懂,没关系!你只需要知道几个指令就能理解了。根据 《深入理解 Java 虚拟机(第二版)》— 周志明著 的第六章知识可知:
- ldc 指令:将一个常量加载到操作数栈。
- _new 指令:实例化对象
- invokevirtual 指令:用于调用对象的实例方法,根据对象的实际类型进行分派(虚方法分派),这也是 Java 语言中最常见的方法分派方式。
我们根据字节码顺序来看:
// "1"+"2"+"3" 被 JVM 转换成了字符串 "123" 存储到操作数栈 |
跳过 astore、dup 等指令(不是本节重点)
/** |
当时用使用 +
操作符连接字符串时,为什么两者有无字符串对象就有区别呢?
是因为如果不出现字符串引用,字符串常量的值在编译期时就可以确定下来,所以不会使用到 StringBuilder;如果出现字符串引用,JVM 不能将字符串引用和字符串常量直接连接,所以将在运行期间动态生成 StringBuilder 对象,让它去实现连接。
说了 StringBuilder,就不能不提 StringBuffer,两者最大的区别是,前者线程不安全的,后者是线程安全的。不能一看是线程不安全就觉得不好,其实线程不安全比线程安全效率高,再者,因为我们写的一些代码不用线程安全这样多此一举。
特别注意!在循环中连接字符串时,最好不要出现字符串引用,因为每次循环都会新建 StringBuilder,即使 Java 有垃圾回收机制,这样也很浪费资源。这时候就可以用 StringBuilder 来连接字符串。
public class Demo { |
总结
如果此博文谬误,还望各位路过的朋友指正!