关于php:JAVA-String-a-abc-中发生了什么

40次阅读

共计 4620 个字符,预计需要花费 12 分钟才能阅读完成。

和上次 int a = 1 后续

测试代码

javac TestCode.java

public class TestCode {public static void main(String[] args) {
          // 此处写 aaaa 是便于看字节码
        String aaaa = "bbbb";
        aaaa = "cccc";
    }
}

反编译

javap -v -p -l TestCode(l 是小写的 L)

注解:javap 是 jdk 自带的反解析工具。作用是依据 class 字节码文件,反解析出以后类对应的 code 区 (汇编指令)、本地变量表、异样表和代码行偏移量映射表、常量池等等信息。

Classfile xxxxx/com/code/baseCode/TestCode.class
  Last modified 2021-11-22; size 475 bytes
  MD5 checksum 0a108c035194f620555926cf7d113c8b
  Compiled from "TestCode.java"
public class com.code.baseCode.TestCode
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #5.#21         // java/lang/Object."<init>":()V
   #2 = String             #22            // bbbb
   #3 = String             #23            // cccc
   #4 = Class              #24            // com/code/baseCode/TestCode
   #5 = Class              #25            // java/lang/Object
   #6 = Utf8               <init>
   #7 = Utf8               ()V
   #8 = Utf8               Code
   #9 = Utf8               LineNumberTable
  #10 = Utf8               LocalVariableTable
  #11 = Utf8               this
  #12 = Utf8               Lcom/code/baseCode/TestCode;
  #13 = Utf8               main
  #14 = Utf8               ([Ljava/lang/String;)V
  #15 = Utf8               args
  #16 = Utf8               [Ljava/lang/String;
  #17 = Utf8               aaaa
  #18 = Utf8               Ljava/lang/String;
  #19 = Utf8               SourceFile
  #20 = Utf8               TestCode.java
  #21 = NameAndType        #6:#7          // "<init>":()V
  #22 = Utf8               bbbb
  #23 = Utf8               cccc
  #24 = Utf8               com/code/baseCode/TestCode
  #25 = Utf8               java/lang/Object
{public com.code.baseCode.TestCode();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 8: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/code/baseCode/TestCode;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=2, args_size=1
         0: ldc           #2                  // String bbbb
         2: astore_1
         3: ldc           #3                  // String cccc
         5: astore_1
         6: return
      LineNumberTable:
        line 11: 0
        line 12: 3
        line 55: 6
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       7     0  args   [Ljava/lang/String;
            3       4     1  aaaa   Ljava/lang/String;
}
SourceFile: "TestCode.java"

反编译后,会发现 aaaa 存在于 Constant pool(常量池)和 LocalVariableTable(本地变量表)中

同时发现和 int a = 1 的区别在于字符串“bbbb”和“cccc”也会在常量池中

JVM 中常量池的划分,请看上篇文章

图片解释

java1.8 的内存布局

在编译后内存状况

运行时产生状况

如何查看 Java bytecode 指令?

https://en.wikipedia.org/wiki…

http://gityuan.com/2015/10/24…

其中 java byte code 为

0: ldc           #2                  // String bbbb
2: astore_1
3: ldc           #3                  // String cccc
5: astore_1

ldc:push a constant #index from a constant pool (String, int, float, Class, java.lang.invoke.MethodType, java.lang.invoke.MethodHandle, or a dynamically-computed constant) onto the stack

意思就是:int、float 或 String 型等常量从常量池推送至栈顶

astore_1:store a reference into local variable 1

意思就是将:一个援用放入到局部变量 1 中 (也就是 aaaa 中)

ldc #2

astore_1

ldc #3

astore_1

图片解释:用 new String() 创立

将代码改成如下:

public class TestCode {public static void main(String[] args) {String aaaa = new String("bbbb");
        aaaa = new String("cccc");
    }
}

则会发现常量池多了一些货色,并且执行指令多了。

其中执行指令如下

0: new           #2                  // class java/lang/String
3: dup
4: ldc           #3                  // String bbbb
6: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
9: astore_1
10: new           #2                  // class java/lang/String
13: dup
14: ldc           #5                  // String cccc
16: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
19: astore_1

new:创立类实例

dup:复制数值,并压入栈顶

ldc:从常量池推送至栈顶

invokespecial:调用非凡实例办法(包含实例初始化办法、父类办法)

astore_1:一个援用放入到局部变量 1 中 (也就是 aaaa 中)

常量池如下

Constant pool:
   #1 = Methodref          #7.#23         // java/lang/Object."<init>":()V
   #2 = Class              #24            // java/lang/String
   #3 = String             #25            // bbbb
   #4 = Methodref          #2.#26         // java/lang/String."<init>":(Ljava/lang/String;)V
   #5 = String             #27            // cccc
   #6 = Class              #28            // com/code/baseCode/TestCode
   #7 = Class              #29            // java/lang/Object
   #8 = Utf8               <init>
   #9 = Utf8               ()V
  #10 = Utf8               Code
  #11 = Utf8               LineNumberTable
  #12 = Utf8               LocalVariableTable
  #13 = Utf8               this
  #14 = Utf8               Lcom/code/baseCode/TestCode;
  #15 = Utf8               main
  #16 = Utf8               ([Ljava/lang/String;)V
  #17 = Utf8               args
  #18 = Utf8               [Ljava/lang/String;
  #19 = Utf8               aaaa
  #20 = Utf8               Ljava/lang/String;
  #21 = Utf8               SourceFile
  #22 = Utf8               TestCode.java
  #23 = NameAndType        #8:#9          // "<init>":()V
  #24 = Utf8               java/lang/String
  #25 = Utf8               bbbb
  #26 = NameAndType        #8:#30         // "<init>":(Ljava/lang/String;)V
  #27 = Utf8               cccc
  #28 = Utf8               com/code/baseCode/TestCode
  #29 = Utf8               java/lang/Object
  #30 = Utf8               (Ljava/lang/String;)V

new #2

dup

ldc #3

invokespecial #4

astore_1

后续的操作一样,因而会呈现

String a = new String("bbbb");
String b = new String("bbbb");
System.out.println(a == b); // false

面试题

做上面的题是,须要晓得几个知识点

1. 编辑时,编译器会对确切的值进行优化:

比方 String a = “a” + “b”; -> 比方 String a = “ab”;

final String a = “a”; String b = “a” + a; -> String b = “aa”;

2. 字符串变量相加会创立 StringBuilder 调用 append 办法,而后 StringBuilder 转换成 new String

3.intern:Java 查找常量池中是否有雷同 Unicode 的字符串常量,如果有,则返回其的援用,如果没有,则在常量池中减少一个 Unicode 等于 str 的字符串并返回它的援用。

String s11 = "a";
String s22 = "bc";

String s1 = "abc";
String s2 = "a" + "bc";
String s3 = new String("a") + "bc";
String s4 = new String("a") + new String("bc");
String s5 = new String("abc");
String s6 = s11 + s22;

System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s1 == s4);
System.out.println(s1 == s5);
System.out.println(s1 == s6);
System.out.println(s3 == s4);
System.out.println(s4 == s5);

System.out.println(s1 == s5.intern());
System.out.println(s1 == s6.intern());

System.out.println(s1.equals(s5));
System.out.println(s1.equals(s6));

答案解释:https://zhuanlan.zhihu.com/p/…

结尾

之前出了 int 类型的状况,上述是 String 类型的状况,后续出包装类型的状况

正文完
 0