java-string类型参数传递分析

Posted on 2010-12-05 16:27 alex_zheng 阅读(1156) 评论(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