Posted on 2010-12-05 16:27
alex_zheng 閱讀(1159)
評(píng)論(0) 編輯 收藏 所屬分類(lèi):
java
上周去一家公司面試,其中一個(gè)面試題是這樣的,判斷下面程序輸出,本人很杯具的寫(xiě)了"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();
}
回來(lái)后想,在java中參數(shù)傳遞都是值傳遞,如果是對(duì)象的話,傳的是該對(duì)象的引用的值,那么從java運(yùn)行時(shí)的棧數(shù)據(jù)來(lái)看,在調(diào)用test方法的時(shí)候,test的局部變量區(qū)中h的值是hi,然后在執(zhí)行change方法前,s在change的局部變量表的值還是hi,在給s賦值后,s的值變?yōu)閏hanged,但這個(gè)作用范圍是change方法,那么這時(shí)候h的值是什么呢?答案是還是hi,在change方法執(zhí)行完后,指令返回test方法時(shí),test的局部變量區(qū)內(nèi)h的值并沒(méi)有改變。
對(duì)這個(gè)類(lèi)稍微做下改變
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();
}
}
通過(guò)命令查看class文件的字節(jié)碼javap -verbose -c -private StringArgTest >~/StringArgTest
這里假定操作棧為S,局部變量表為L(zhǎng),S0表示操作數(shù)棧頂,L0表示局部變量表的第一個(gè)變量,我們知道jvm執(zhí)行方法時(shí),每個(gè)方法的局部變量表是方法獨(dú)立的
Compiled from "StringArgTest.java"
public class com.demo.StringArgTest extends java.lang.Object
SourceFile: "StringArgTest.java"
minor version: 0
major version: 50
Constant pool://常量池內(nèi)容,是該類(lèi)的元數(shù)據(jù)
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();//系統(tǒng)默認(rèn)生成的構(gòu)造函數(shù)
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,此時(shí)s的值是changed
3: return //返回到test方法,不返回任何數(shù)據(jù)
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,此時(shí)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;這里方法返回常量區(qū)changed2的引用,壓入S4
16: astore_2 //彈出S4即changed2的引用,賦值給L2
17: getstatic #33; //Field java/lang/System.out:Ljava/io/PrintStream;
20: aload_1 //加載L1,即h的引用,對(duì)應(yīng)字符串仍為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,對(duì)應(yīng)字符串為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方法里雖然改變了參數(shù)s的值,但是s的作用范圍是change方法,在該方法退出后,test方法里局部變量h的引用并未改變,仍是常量池中hi的索引地址,在調(diào)用change2方法后,test方法局部變量h2的值也隨之改變