最近学习 java 的一些知识, 通过学习编译后的字节码可以更好的了解 java 的编译.
1. 安装查看字节码的插件
在 eclipse 上直接通过 Eclipse Marketplace 安装, 直接搜索 bytecode 就可以看到插件
最后通过 Windows-> Show View -> Other -> Java -> Bytecode 打开查看字节码
在 IntelliJ IDEA 上也通过安装插件方式
IntelliJ IDEAL 安装好插件后, 通过下面方法查看选中文件的字节码.
2. 通过字节码学习 java 字符串的操作
下面写了一段简单的字符操作, 最后判断两个字符串是否相同. 最后输出的答案是 false,true,true
下面是上面代码生成的字节码文件 (红色字体是我添加的注释):
- // access flags 0x9
- public static main([Ljava/lang/String;)V
- L0
- LINENUMBER 5 L0
- LDC "abc" // 第一个字符串常量
- ASTORE 1
- L1
- LINENUMBER 6 L1
- NEW java/lang/StringBuilder
- DUP
- ALOAD 1
- INVOKESTATIC java/lang/String.valueOf(Ljava/lang/Object;)Ljava/lang/String;
- INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
- LDC "def" // 在连接字符串 def 的时候, 相当于操作 String test2 = new StringBuilder().append(test1).append("def").toString();
- INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
- INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;
- ASTORE 2
- L2
- LINENUMBER 7 L2
- LDC "abcdef" // 第三个字符串常量
- ASTORE 3
- L3
- LINENUMBER 8 L3
- GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
- ALOAD 2
- ALOAD 3
- IF_ACMPNE L4
- ICONST_1
- GOTO L5
- L4
- FRAME FULL [[Ljava/lang/String; java/lang/String java/lang/String java/lang/String] [java/io/PrintStream]
- ICONST_0
- L5
- FRAME FULL [[Ljava/lang/String; java/lang/String java/lang/String java/lang/String] [java/io/PrintStream I]
- INVOKEVIRTUAL java/io/PrintStream.println(Z)V
- L6
- LINENUMBER 9 L6
- GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
- ALOAD 2
- ALOAD 3
- INVOKEVIRTUAL java/lang/String.equals(Ljava/lang/Object;)Z
- INVOKEVIRTUAL java/io/PrintStream.println(Z)V
- L7
- LINENUMBER 10 L7
- GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
- ALOAD 2
- INVOKEVIRTUAL java/lang/String.intern()Ljava/lang/String;
- ALOAD 3
- IF_ACMPNE L8
- ICONST_1
- GOTO L9
- L8
- FRAME SAME1 java/io/PrintStream
- ICONST_0
- L9
- FRAME FULL [[Ljava/lang/String; java/lang/String java/lang/String java/lang/String] [java/io/PrintStream I]
- INVOKEVIRTUAL java/io/PrintStream.println(Z)V
- L10
- LINENUMBER 11 L10
- RETURN
- L11
- LOCALVARIABLE args [Ljava/lang/String; L0 L11 0
- LOCALVARIABLE test1 Ljava/lang/String; L1 L11 1
- LOCALVARIABLE test2 Ljava/lang/String; L2 L11 2
- LOCALVARIABLE test3 Ljava/lang/String; L3 L11 3
- MAXSTACK = 3
- MAXLOCALS = 4
通过以上的分析可以知道, 在创建 test2 字符串的时候, 是通过下面方式获取的.
String test2 = new StringBuilder().append(test1).append("def").toString();
这样的话字符串 test2 是放在堆里的, 而 test1 和 test3 是放在字符串的常量池当中的.
(这里涉及到 JVM 虚拟机几个大部分: 方法区, Java 虚拟机栈, 本地方法栈, 堆, 程序寄数器, 字符串常量池)
这样的话,
test2 == test3 是不相等, 返回 false;
test2.equals(test3) 时比较值 是否相等; 这里是相等, 返回 true;
test2.intern() 是把 test2 放入到字符串常量池中, 则 test2.intern() == test3 是相同的, 返回 true;
个人学习 java 知识总结:
在学习 java 的时候, 有时候只知道这样写是正确的, 但不知道 jvm 是怎么做到的.
知道是这么回事, 但是不知道为什么会是这么回事.
所以在以后学习 java 知识的时候, 希望自己多深入了解一下, 不能只知道表面现象, 要看到本质.
来源: http://www.bubuko.com/infodetail-3386894.html