java-string類型參數傳遞分析

          Posted on 2010-12-05 16:27 alex_zheng 閱讀(1162) 評論(0)  編輯  收藏 所屬分類: java
          上周去一家公司面試,其中一個面試題是這樣的,判斷下面程序輸出,本人很杯具的寫了"changed"
          public class StringArgTest {

              
          private void change(String s){
                  s 
          = "changed";
              }
            

              
          private void test(){
                  String h 
          = "hi";
                  change(h);     

                  System.out.println(h);       
              }
              
              
          public static void main(String[] args) {
                  StringArgTest st 
          = new StringArgTest();
                  st.test();
              }
          回來后想,在java中參數傳遞都是值傳遞,如果是對象的話,傳的是該對象的引用的值,那么從java運行時的棧數據來看,在調用test方法的時候,test的局部變量區中h的值是hi,然后在執行change方法前,s在change的局部變量表的值還是hi,在給s賦值后,s的值變為changed,但這個作用范圍是change方法,那么這時候h的值是什么呢?答案是還是hi,在change方法執行完后,指令返回test方法時,test的局部變量區內h的值并沒有改變。
          對這個類稍微做下改變
          public class StringArgTest {

              
          private void change(String s){
                  s 
          = "changed";
              }
              
          private String change2(String s){
                  s 
          = "changed2";
                  
          return s;
              }
              
          private void test(){
                  String h 
          = "hi";
                  change(h);
                  String h2 
          = "h2";
                  h2 
          = change2(h2);
                  System.out.println(h);
                  System.out.println(h2);
              }
              
              
          public static void main(String[] args) {
                  StringArgTest st 
          = new StringArgTest();
                  st.test();
              } 
          }
          通過命令查看class文件的字節碼javap -verbose -c -private StringArgTest >~/StringArgTest
          這里假定操作棧為S,局部變量表為L,S0表示操作數棧頂,L0表示局部變量表的第一個變量,我們知道jvm執行方法時,每個方法的局部變量表是方法獨立的
          Compiled from "StringArgTest.java"
          public class com.demo.StringArgTest extends java.lang.Object
            SourceFile: 
          "StringArgTest.java"
            minor version: 
          0
            major version: 
          50
            Constant pool://常量池內容,是該類的元數據
          const #1 = class    #2;    //  com/demo/StringArgTest
          const #2 = Asciz    com/demo/StringArgTest;
          const #3 = class    #4;    //  java/lang/Object
          const #4 = Asciz    java/lang/Object;
          const #5 = Asciz    <init>;
          const #6 = Asciz    ()V;
          const #7 = Asciz    Code;
          const #8 = Method    #3.#9;    //  java/lang/Object."<init>":()V
          const #9 = NameAndType    #5:#6;//  "<init>":()V
          const #10 = Asciz    LineNumberTable;
          const #11 = Asciz    LocalVariableTable;
          const #12 = Asciz    this;
          const #13 = Asciz    Lcom/demo/StringArgTest;;
          const #14 = Asciz    change;
          const #15 = Asciz    (Ljava/lang/String;)V;
          const #16 = String    #17;    //  changed
          const #17 = Asciz    changed;
          const #18 = Asciz    s;
          const #19 = Asciz    Ljava/lang/String;;
          const #20 = Asciz    change2;
          const #21 = Asciz    (Ljava/lang/String;)Ljava/lang/String;;
          const #22 = String    #23;    //  changed2
          const #23 = Asciz    changed2;
          const #24 = Asciz    test;
          const #25 = String    #26;    //  hi
          const #26 = Asciz    hi;
          const #27 = Method    #1.#28;    //  com/demo/StringArgTest.change:(Ljava/lang/String;)V
          const #28 = NameAndType    #14:#15;//  change:(Ljava/lang/String;)V
          const #29 = String    #30;    //  h2
          const #30 = Asciz    h2;
          const #31 = Method    #1.#32;    //  com/demo/StringArgTest.change2:(Ljava/lang/String;)Ljava/lang/String;
          const #32 = NameAndType    #20:#21;//  change2:(Ljava/lang/String;)Ljava/lang/String;
          const #33 = Field    #34.#36;    //  java/lang/System.out:Ljava/io/PrintStream;
          const #34 = class    #35;    //  java/lang/System
          const #35 = Asciz    java/lang/System;
          const #36 = NameAndType    #37:#38;//  out:Ljava/io/PrintStream;
          const #37 = Asciz    out;
          const #38 = Asciz    Ljava/io/PrintStream;;
          const #39 = Method    #40.#42;    //  java/io/PrintStream.println:(Ljava/lang/String;)V
          const #40 = class    #41;    //  java/io/PrintStream
          const #41 = Asciz    java/io/PrintStream;
          const #42 = NameAndType    #43:#15;//  println:(Ljava/lang/String;)V
          const #43 = Asciz    println;
          const #44 = Asciz    h;
          const #45 = Asciz    main;
          const #46 = Asciz    ([Ljava/lang/String;)V;
          const #47 = Method    #1.#9;    //  com/demo/StringArgTest."<init>":()V
          const #48 = Method    #1.#49;    //  com/demo/StringArgTest.test:()V
          const #49 = NameAndType    #24:#6;//  test:()V
          const #50 = Asciz    args;
          const #51 = Asciz    [Ljava/lang/String;;
          const #52 = Asciz    st;
          const #53 = Asciz    SourceFile;
          const #54 = Asciz    StringArgTest.java;

          {
          public com.demo.StringArgTest();//系統默認生成的構造函數
            Code:
             Stack
          =1, Locals=1, Args_size=1
             
          0:    aload_0
             
          1:    invokespecial    #8//Method java/lang/Object."<init>":()V
             4:    return
            LineNumberTable: 
             line 
          30

            LocalVariableTable: 
             Start  Length  Slot  Name   Signature
             
          0      5      0    this       Lcom/demo/StringArgTest;


          private void change(java.lang.String);
            Code:
             Stack
          =1, Locals=2, Args_size=2
             
          0:    ldc    #16//String changed 從常量池壓入changed的引用地址16到S0
             2:    astore_1    //彈出S0,賦值給局部變量L1,此時s的值是changed
             
          3:    return        //返回到test方法,不返回任何數據
            LineNumberTable: 
             line 
          60
             line 
          73

            LocalVariableTable: 
             Start  Length  Slot  Name   Signature
             
          0      4      0    this       Lcom/demo/StringArgTest;
             
          0      4      1    s       Ljava/lang/String;


          private java.lang.String change2(java.lang.String);
            Code:
             Stack
          =1, Locals=2, Args_size=2
             
          0:    ldc    #22//String changed2
             2:    astore_1    //彈出S0,賦值給局部變量L1,即s
             
          3:    aload_1     //從L1中加載變量到S0
             
          4:    areturn     //返回S0中的引用,即變量s的引用,這里是changed2的索引22
            LineNumberTable: 
             line 
          90
             line 
          103

            LocalVariableTable: 
             Start  Length  Slot  Name   Signature
             
          0      5      0    this       Lcom/demo/StringArgTest;
             
          0      5      1    s       Ljava/lang/String;


          private void test();
            Code:
             Stack
          =2, Locals=3, Args_size=1
             
          0:    ldc    #25//String hi 從常量池中壓入hi的引用,
             2:    astore_1    //彈出S0,賦值給L1,即h
             
          3:    aload_0     //加載L0到S0,即this的引用
             
          4:    aload_1     //加載L1到S1,即h的引用
             
          5:    invokespecial    #27//Method change:(Ljava/lang/String;)V,此時test的局部變量表中,h的值仍是hi
             8:    ldc    #29//String h2  壓入h2到S2
             10:    astore_2      //彈出S2,賦值給L2,即h2
             
          11:    aload_0       //加載L0到S2
             
          12:    aload_2       //加載L2到S3
             
          13:    invokespecial    #31//Method change2:(Ljava/lang/String;)Ljava/lang/String;這里方法返回常量區changed2的引用,壓入S4
             16:    astore_2      //彈出S4即changed2的引用,賦值給L2
             
          17:    getstatic    #33//Field java/lang/System.out:Ljava/io/PrintStream;
             20:    aload_1      //加載L1,即h的引用,對應字符串仍為hi
             
          21:    invokevirtual    #39//Method java/io/PrintStream.println:(Ljava/lang/String;)V
             24:    getstatic    #33//Field java/lang/System.out:Ljava/io/PrintStream;
             27:    aload_2      //加載L2,對應字符串為changed2
             
          28:    invokevirtual    #39//Method java/io/PrintStream.println:(Ljava/lang/String;)V
             31:    return
            LineNumberTable: 
             line 
          130
             line 
          143
             line 
          158
             line 
          1611
             line 
          1717
             line 
          1824
             line 
          1931

            LocalVariableTable: 
             Start  Length  Slot  Name   Signature
             
          0      32      0    this       Lcom/demo/StringArgTest;
             
          3      29      1    h       Ljava/lang/String;
             
          11      21      2    h2       Ljava/lang/String;


          public static void main(java.lang.String[]);
            Code:
             Stack
          =2, Locals=2, Args_size=1
             
          0:    new    #1//class com/demo/StringArgTest
             3:    dup
             
          4:    invokespecial    #47//Method "<init>":()V
             7:    astore_1
             
          8:    aload_1
             
          9:    invokespecial    #48//Method test:()V
             12:    return
            LineNumberTable: 
             line 
          220
             line 
          238
             line 
          2412

            LocalVariableTable: 
             Start  Length  Slot  Name   Signature
             
          0      13      0    args       [Ljava/lang/String;
             
          8      5      1    st       Lcom/demo/StringArgTest;


          }
          從上可以看出,change方法里雖然改變了參數s的值,但是s的作用范圍是change方法,在該方法退出后,test方法里局部變量h的引用并未改變,仍是常量池中hi的索引地址,在調用change2方法后,test方法局部變量h2的值也隨之改變

          posts - 10, comments - 9, trackbacks - 0, articles - 15

          Copyright © alex_zheng

          主站蜘蛛池模板: 黄山市| 牟定县| 阿坝| 凤山市| 郧西县| 石狮市| 江陵县| 精河县| 麟游县| 鹤壁市| 富顺县| 瑞昌市| 忻城县| 信宜市| 白山市| 京山县| 西峡县| 大邑县| 大冶市| 通榆县| 桐城市| 巨野县| 集安市| 阳泉市| 红河县| 江津市| 上高县| 大庆市| 九寨沟县| 广平县| 资源县| 鹤山市| 繁峙县| 喀喇| 濉溪县| 尚义县| 曲沃县| 安丘市| 普兰县| 当雄县| 潞城市|