Posted on 2010-12-05 16:27
alex_zheng 阅读(1154)
评论(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 3: 0
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 6: 0
line 7: 3
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 9: 0
line 10: 3
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 13: 0
line 14: 3
line 15: 8
line 16: 11
line 17: 17
line 18: 24
line 19: 31
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 22: 0
line 23: 8
line 24: 12
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的值也随之改变