java object怎么拿字段_「Java面试秘籍」String不可变,如何理解

Java中最常见的就是String类,那么很多人都说String是不可变类型,那么如何解释下面的代码?

public class StringTest {    public static void main(String[] args) {        String a = "A";        a = "B";        System.out.println(a);    }}

看到这个问题,突然有点想骂面试官,这是偷换概念好么?

作为一个有经验的码农,立即看出程序结果输出肯定是“B”,此处a确实由“A”变成了“B”,怎么理解呢?

首先,我们默默的打开String的源码(JDK8为例子)

以下为关键部分源码

/** * Strings are constant; their values cannot be changed after they * are created. String buffers support mutable strings. * Because String objects are immutable they can be shared. **/ public final class String    implements java.io.Serializable, Comparable, CharSequence {    /** The value is used for character storage. */    private final char value[];    /** Cache the hash code for the string */    private int hash; // Default to 0 }

注释中:明确写着String是常量,创建后不可变。

其实面试官,偷换了一个概念,a其实是String的引用,不可变的其实是“A”这个字符串,a作为引用当然可以重新指向到新的String,如“B”。

c9f18719e0fb498ba8f608173c404b30.png

我们如何证明?

利用javap进行反编译

javap -v StringTest.clas

反编译结果如下:

Last modified 2020-8-25; size 640 bytes  MD5 checksum 3ac58ea867e2f73eb49be1e505170898  Compiled from "StringTest.java"public class com.ganhuo.test.StringTest  minor version: 0  major version: 52  flags: ACC_PUBLIC, ACC_SUPERConstant pool:   #1 = Methodref          #7.#24         // java/lang/Object."":()V   #2 = String             #25            // A   #3 = String             #26            // B   #4 = Fieldref           #27.#28        // java/lang/System.out:Ljava/io/PrintStream;   #5 = Methodref          #29.#30        // java/io/PrintStream.println:(Ljava/lang/String;)V   #6 = Class              #31            // com/ganhuo/test/StringTest   #7 = Class              #32            // java/lang/Object   #8 = Utf8                  #9 = Utf8               ()V  #10 = Utf8               Code  #11 = Utf8               LineNumberTable  #12 = Utf8               LocalVariableTable  #13 = Utf8               this  #14 = Utf8               Lcom/ganhuo/test/StringTest;  #15 = Utf8               main  #16 = Utf8               ([Ljava/lang/String;)V  #17 = Utf8               args  #18 = Utf8               [Ljava/lang/String;  #19 = Utf8               a  #20 = Utf8               Ljava/lang/String;  #21 = Utf8               MethodParameters  #22 = Utf8               SourceFile  #23 = Utf8               StringTest.java  #24 = NameAndType        #8:#9          // "":()V  #25 = Utf8               A  #26 = Utf8               B  #27 = Class              #33            // java/lang/System  #28 = NameAndType        #34:#35        // out:Ljava/io/PrintStream;  #29 = Class              #36            // java/io/PrintStream  #30 = NameAndType        #37:#38        // println:(Ljava/lang/String;)V  #31 = Utf8               com/ganhuo/test/StringTest  #32 = Utf8               java/lang/Object  #33 = Utf8               java/lang/System  #34 = Utf8               out  #35 = Utf8               Ljava/io/PrintStream;  #36 = Utf8               java/io/PrintStream  #37 = Utf8               println  #38 = Utf8               (Ljava/lang/String;)V{  public com.ganhuo.test.StringTest();    descriptor: ()V    flags: ACC_PUBLIC    Code:      stack=1, locals=1, args_size=1         0: aload_0         1: invokespecial #1                  // Method java/lang/Object."":()V         4: return      LineNumberTable:        line 3: 0      LocalVariableTable:        Start  Length  Slot  Name   Signature            0       5     0  this   Lcom/ganhuo/test/StringTest;  public static void main(java.lang.String[]);    descriptor: ([Ljava/lang/String;)V    flags: ACC_PUBLIC, ACC_STATIC    Code:      stack=2, locals=2, args_size=1         0: ldc           #2                  // String A         2: astore_1         3: ldc           #3                  // String B         5: astore_1         6: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;         9: aload_1        10: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V        13: return      LineNumberTable:        line 5: 0        line 6: 3        line 7: 6        line 8: 13      LocalVariableTable:        Start  Length  Slot  Name   Signature            0      14     0  args   [Ljava/lang/String;            3      11     1     a   Ljava/lang/String;    MethodParameters:      Name                           Flags      args}

我们可以看到常量次中有#25和#26两个常量,分别为“A”、“B”

再看具体的main方法

0: ldc           #2                  // String A2: astore_13: ldc           #3                  // String B5: astore_1

此处仅仅是将变量重新赋值,并没有修改“A”,因此String确实是一个不可变的。

如果你细心发现,其实包装类都是final进行修饰,都是不可变类,原理同String。

字节码中指令的含义可以自行查询

1af6d859c33b07825d6e01459a3918b7.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值