<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Heis的Blog

    保持簡單,保持愚蠢
    隨筆 - 29, 文章 - 1, 評論 - 122, 引用 - 0
    數(shù)據(jù)加載中……

    Java方法中使用的是值傳遞(pass-by-value)!

       實(shí)在看不下去網(wǎng)上的一些面試題,很多都是錯(cuò)的答案。例如像今天這個(gè)問題:java方法用的是值傳遞還是引用傳遞。你在blogjava上還能搜到不同的答案呢。最近有空就翻譯了一篇國外的文章,很多東西不能只看答案,而不知其所以然。第一次翻譯文章,博友多指教。
        重申:對于原始類型(primitive type也譯為值類型),是通過拷貝一個(gè)相同的值傳給java方法的參數(shù)的;而對于引用類型(reference type),就是對象,是通過拷貝一個(gè)相同的應(yīng)用或地址傳給java方法的參數(shù)的。業(yè)界都統(tǒng)稱這是pass-by-value(值傳遞),這里是翻譯一篇 國外的文章來說明為什么java中的值傳遞比較特別。
        java中值傳遞比較特別,也比較有爭議,所以重要的是要理解它的原理。推薦Java程序員都去讀《Effective java》。


        原文地址:http://java.sun.com/developer/JDCTechTips/2001/tt1009.html
        另一篇寫的比較好的文章(英文):http://www.brpreiss.com/books/opus5/html/page590.html
        blogjava上前輩的文章:http://m.tkk7.com/zygcs/archive/2008/10/05/232438.html

    實(shí)際參數(shù)是如何傳遞到java方法的

    HOW ARGUMENTS ARE PASSED TO JAVA METHODS

    Suppose you're doing some Java programming, and you have a simple program like this:

    假如你正在編寫java程序,然后你寫了以下一個(gè)程序:

     1 public class CallDemo1 {
     2         static void f(int arg1) {
     3             arg1 = 10;
     4         }
     5     
     6         public static void main(String args[]) {
     7             int arg1;
     8     
     9             arg1 = 5;
    10     
    11             f(arg1);
    12     
    13             System.out.println("arg1 = " + arg1);
    14         }
    15     }
    16 

    In the main method, the variable arg1 is given the value 5, and then passed as an argument to the method f. This method declares a parameter of the same name, arg1, used to access the argument.

    main方法中,變量arg1賦值5,然后作為實(shí)際參數(shù)傳遞到方法f。這個(gè)方法聲明一個(gè)相同名字的形式參數(shù)arg1,被用于訪問實(shí)際參數(shù)。

    What happens when you run this simple program? The method f modifies the value of arg1, so what value gets printed, 5 or 10? It turns out that 5 is the right answer. This implies that setting arg1 to 10 in method f has no effect outside of the method.

    當(dāng)你執(zhí)行這個(gè)程序會(huì)發(fā)生什么事情呢?方法f修改了arg1的值,所以會(huì)打印什么呢,5還是10?這里5是正確的答案。這就意味著在方法f中,將arg1設(shè)為10并不影響到方法的外部。

    Why does it work this way? The answer has to do with the distinction between the pass-by-value and pass-by-reference approaches to passing arguments to a method. The Java language uses pass-by-value exclusively. Before explaining what this means, let's look at an example of pass-by-reference, using C++ code:

    為什么呢?答案在于區(qū)分使用“值傳遞”方式和“引用傳遞”方式給方法傳遞參數(shù)。Java語言使用與眾不同的“值傳遞”方式。在解釋它的意思之前,讓我們看看以下一個(gè)值傳遞的例子,這是用C++寫的。

     1 #include <iostream>
     2     
     3     using namespace std;
     4     
     5     void f(int arg1, int& arg2)
     6     {
     7         arg1 = 10;
     8         arg2 = 10;
     9     }
    10     
    11     int main()
    12     {
    13         int arg1, arg2;
    14     
    15         arg1 = 5;
    16         arg2 = 5;
    17     
    18         f(arg1, arg2);
    19     
    20         cout << "arg1 = " << arg1 << " arg2 = "<< arg2 << endl;
    22     }
    23 

    Function f has two parameters. The first parameter is a pass-by-value parameter, the second a pass-by-reference one. When you run this program, the first assignment in function f has no effect in the caller (the main function). But the second assignment does, in fact, change the value of arg2 in main. The & in int& says that arg2 is a pass-by-reference parameter. This particular example has no equivalent in Java programming.

    函數(shù)f有兩個(gè)形參。第一個(gè)是值傳遞,第二個(gè)是引用傳遞。當(dāng)你運(yùn)行這個(gè)程序的時(shí)候,第一個(gè)參數(shù)賦值不影響調(diào)用者(main函數(shù))。但是第二個(gè)參數(shù)賦值卻會(huì),事實(shí)上,修改了mainarg2的值。在int&中的&表示arg2是一個(gè)引用傳遞的形參。這個(gè)特別的例子與java有所不同。

    So what does pass-by-value actually mean? To answer this, it's instructive to look at some JVM1 bytecodes that result from the following two commands:

    那值傳遞到底是什么意思?要回答這個(gè)問題,有需要看一下運(yùn)行以下兩行命令后的JVM的字節(jié)碼

        javac CallDemo1.java

        javap -c -classpath . CallDemo1

    Here is an excerpt from the output:

    這里是輸出的片段:

    Method void f(int)

            
    0 bipush 10

            
    2 istore_0

            
    3 return

        Method 
    void main(java.lang.String[])

            
    0 iconst_5

            
    1 istore_1

            
    2 iload_1

            
    3 invokestatic #2 <Method void f(int)>


    In the main method, the instruction iconst_5 pushes the value 5 onto the operand stack of the Java virtual machine. This value is then stored in the second local variable (arg1, with args as the first local variable). iload_1 pushes the value of the second local variable onto the operand stack, where it will serve as an argument to the called method.

    main方法中,iconst_5指令將值5壓入JVM的操作數(shù)堆棧(operand stack)。然后這個(gè)值隨后存儲(chǔ)第二個(gè)本地變量(arg1的值存在第一個(gè)本地變量處)。iload_1指令將第二個(gè)本地變量的值再壓入操作數(shù)堆棧,作為被調(diào)用方法的形參。

    Then the method f is invoked using the invokestatic instruction. The argument value is popped from the stack, and is used to create a stack frame for the called method. This stack frame represents the local variables in f, with the method parameter (arg1) as the first of the local variables.

    然后方法f通過invokestatic指令調(diào)用。形參的值出棧,被用于為被調(diào)用的方法創(chuàng)建一個(gè)??蚣埽?/span>stack frame)。這個(gè)??蚣鼙硎舅?/span>f中的所有本地變量,而arg1實(shí)參將作為棧框架內(nèi)的第一個(gè)本地變量。

    What this means is that the parameters in a method are copies of the argument values passed to the method. If you modify a parameter, it has no effect on the caller. You are simply changing the copy's value in the stack frame that is used to hold local variables. There is no way to "get back" at the arguments in the calling method. So assigning to arg1 in f does not change arg1 in main. Also, the arg1 variables in f and in main are unrelated to each other, except that arg1 in f starts with a copy of the value of arg1 in main. The variables occupy different locations in memory, and the fact that they have the same name is irrelevant.

    這里的的意思是形式參數(shù)作為實(shí)際參數(shù)的拷貝,被傳入方法當(dāng)中。如果你(在方法體內(nèi))修改了一個(gè)實(shí)際參數(shù),它并不影響調(diào)用者,你只是修改了??蚣軆?nèi)的拷貝而已。不可能在調(diào)用方法中“取回”實(shí)參的值。因此在f中給arg1賦值不會(huì)改變main中的arg1,而且這兩個(gè)arg1毫無關(guān)聯(lián),除了main中的arg1f中的arg1的一個(gè)拷貝之外。這兩個(gè)變量存在內(nèi)存的不同區(qū)域,事實(shí)上雖然它們有同樣的名字,但是毫不相干。

    By contrast, a pass-by-reference parameter is implemented by passing the memory address of the caller's argument to the called function. The argument address is copied into the parameter. The parameter contains an address that references the argument's memory location so that changes to the parameter actually change the argument value in the caller. In low-level terms, if you have the memory address of a variable, you can change the variable's value at will.

    對比值傳遞,引用傳遞的參數(shù)是指傳遞調(diào)用者實(shí)參的內(nèi)存地址給被調(diào)用的函數(shù)。實(shí)參地址拷貝給形參。形參指向了實(shí)參的內(nèi)存區(qū)域,所以對形參的修改同樣會(huì)修改到實(shí)參。從底層的角度來說,你可以任意修改變量的值,只要你有該變量的內(nèi)存地址。

    The discussion of argument passing is complicated by the fact that the term "reference" in pass-by-reference means something slightly different than the typical use of the term in Java programming. In Java, the term reference is used in the context of object references. When you pass an object reference to a method, you're not using pass-by-reference, but pass-by-value. In particular, a copy is made of the object reference argument value, and changes to the copy (through the parameter) have no effect in the caller. Let's look at a couple of examples to clarify this idea:

    關(guān)于參數(shù)傳遞的討論會(huì)復(fù)雜化,這是因?yàn)橐陨弦脗鬟f中的術(shù)語“引用”與Java編程中使用的(術(shù)語)存在微妙的區(qū)別。在java中,術(shù)語“引用”用于對象所有引用的上下文。當(dāng)你給方法傳遞一個(gè)對象引用,你并不是使用引用傳遞,更像是值傳遞。尤其是,這樣會(huì)拷貝一份實(shí)際參數(shù)的對象引用,(通過形參)對該拷貝的修改不會(huì)影響到調(diào)用者。讓我們用以下兩個(gè)例子來證明這個(gè)觀點(diǎn)。


     1 class A {
     2 
     3         public int x;
     4 
     5         A(int x) {
     6             this.x = x;
     7         }
     8 
     9         public String toString() {
    10             return Integer.toString(x);
    11         }
    12 
    13     }
    14 
    15     public class CallDemo2 {
    16 
    17         static void f(A arg1) {
    18             arg1 = null;
    19         }
    20 
    21         public static void main(String args[]) {
    22             A arg1 = new A(5);
    23             f(arg1);
    24             System.out.println("arg1 = " + arg1);
    25         }
    26 
    27     }

    In this example, a reference to an A object is passed to f. Setting arg1 to null in f has no effect on the caller, just as in the previous example. The value 5 gets printed. The caller passes a copy of the object reference value (arg1), not the memory address of arg1. So the called method cannot get back at arg1 and change it.

    在這個(gè)例子中,A的引用傳遞給f。arg1設(shè)置為null不影響調(diào)用者,就像前一個(gè)例子一樣。程序會(huì)打印5。調(diào)用者傳遞一份對象引用(arg1)的拷貝,而不是arg1的內(nèi)存地址。所以調(diào)用方法無法取回到(調(diào)用者的)arg1變量并修改它。

    Here's another example:


     1 class A {
     2 
     3         public int x;
     4 
     5         public A(int x) {
     6             this.x = x;
     7         }
     8 
     9         public String toString() {
    10             return Integer.toString(x);
    11         }
    12 
    13     }
    14 
    15    
    16 
    17     public class CallDemo3 {
    18 
    19         static void f(A arg1) {
    20             arg1.x = 10;
    21         }
    22 
    23         public static void main(String args[]) {
    24             A arg1 = new A(5);
    25             f(arg1);
    26             System.out.println("arg1 = " + arg1);
    27         }
    28 
    29     }



    What gets printed here is 10. How can that be? You've already seen that there's no way to change the caller's version of arg1 in the called method. But this code shows that the object referenced by arg1 can be changed. Here, the calling method and the called method have an object in common, and both methods can change the object. In this example, the object reference (arg1) is passed by value. Then a copy of it is made into the stack frame for f. But both the original and the copy are object references, and they point to a common object in memory that can be modified.

    這個(gè)例子會(huì)打印10。為什么呢?通過之前那個(gè)例子,你已經(jīng)知道無法改變在調(diào)用方法中修改(實(shí)參)arg1。但是這里的代碼顯示arg1能夠被修改。這里調(diào)用方法和被調(diào)用方法有個(gè)共同的對象,兩個(gè)方法都能修改這個(gè)對象。在這個(gè)例子中,對象的引用(arg1)是值傳遞。他的一個(gè)拷貝被加入到f的??蚣堋5窃瓉淼暮涂截惖膶ο蠖际菍ο笠?,它們指向相同的內(nèi)存區(qū)域中的對象,因此可以修改。

    In Java programming, it's common to say things like "a String object is passed to method f" or "an array is passed to method g." Technically speaking, objects and arrays are not passed. Instead, references or addresses to them are passed. For example, if you have a Java object containing 25 integer fields, and each field is 4 bytes, then the object is approximately 100 bytes long. But when you pass this object as an argument to a method, there is no actual copy of 100 bytes. Instead, a pointer, reference, or address of the object is passed. The same object is referenced in the caller and the called method. By contrast, in a language like C++, it's possible to pass either an actual object or a pointer to the object.

    java編程中,經(jīng)??梢月牭?#8220;一個(gè)String對象被傳遞到方法f”或者“一個(gè)數(shù)組被傳到方法g”。從技術(shù)上來說,不是傳遞一些對象或數(shù)組,而是傳遞引用或地址。例如,如果你有一個(gè)java對象包含25個(gè)整型的屬性,每個(gè)屬性占4 字節(jié),那這個(gè)對象大約是占100字節(jié)。但是作為形式參數(shù)傳遞的時(shí)候,不會(huì)拷貝100 字節(jié)。而是傳遞一個(gè)指針、引用或地址。這樣調(diào)用者和被調(diào)用的方法都指向同一個(gè)對象。對比而言,與C++類似的語言,它可以傳遞真實(shí)的對象,也可以傳遞該對象的指針。

    What are the implications of pass-by-value? One is that when you pass objects or arrays, the calling method and the called method share the objects, and both can change the object. So you might want to employ defensive copying techniques, as described in the September 4, 2001 Tech Tip, "Making Defensive Copies of Objects"

    那什么是隱含的值傳遞呢?其一是當(dāng)你傳遞對象或數(shù)組,由調(diào)用方法和被調(diào)用方法共享,雙方都可修改對象。因此你可能想請入保護(hù)性拷貝技術(shù),就像200194 日技術(shù)貼士“使用保護(hù)性拷貝技術(shù)。”

    You can fix the case above, where the called method modifies an object, by making the class immutable. An immutable class is one whose instances cannot be modified. Here's how you to do this:

    你可以修正以上的例子,當(dāng)被調(diào)用方法修改一個(gè)對象時(shí),讓這個(gè)類成為不可變的。一個(gè)不可變類是指其實(shí)例不可修改的類。這里教你怎樣去定義它。


     1 final class A {
     2 
     3         private final int x;
     4 
     5         public A(int x) {
     6             this.x = x;
     7         }
     8 
     9         public String toString() {
    10             return Integer.toString(x);
    11         }
    12     }
    13 
    14    
    15 
    16     public class CallDemo4 {
    17 
    18         static void f(A arg1) {
    19             //arg1.x = 10;
    20         }
    21 
    22         public static void main(String args[]) {
    23             A arg1 = new A(5);
    24             f(arg1);
    25             System.out.println("arg1 = " + arg1);
    26 
    27         }
    28 
    29     }



    The printed result is 5. Now uncomment the modification of A in f and recompile the program. Notice that it results in a compile error. You have made A immutable, so it can't be legally modified by f.

    打印結(jié)果是5,現(xiàn)在去掉CallDemo4f方法內(nèi)的注釋和重新編譯。注意它會(huì)編譯出錯(cuò)。因?yàn)槟阋呀?jīng)讓A不可變了,所以f不能合法修改它。

    Another implication of pass-by-value is that you can't use method parameters to return multiple values from a method, unless you pass in a mutable object reference or array, and let the method modify the object. There are other ways of returning multiple values, such as returning an array from the method, or creating a specialized class and returning an instance of it.

    另一個(gè)隱含的值傳遞是你不能利用方法的形參在一個(gè)方法中返回多個(gè)值,除非你傳入一個(gè)不可變對象引用或數(shù)組,然后讓方法修改該對象。還有另外的方式返回多個(gè)值,例如從方法中返回一個(gè)數(shù)組,或者創(chuàng)建一個(gè)特別定義的類的實(shí)例。

    For more information about how arguments are passed to Java Methods, see Section 1.8.1, Invoking a Method, and section 2.6.4, Parameter Values, in "The Java Programming Language Third Edition" by Arnold, Gosling, and Holmes. Also see item 13, Favor immutability, and item 24, Make defensive copies when needed, in "Effective Java Programming Language Guide" by Joshua

    想知道更多關(guān)于Java方法中的實(shí)際參數(shù)的傳遞的信息,請看Arnold, Gosling, and Holmes的《The Java Programming Language Third Edition》中的1.8.1節(jié)“Invoking a Method”和2.6.4節(jié)“Parameter Values”。還有Joshua的《Effective Java Programming Language Guide條款13 Favor immutability”和24 Make defensive copies when needed”。

    術(shù)語對照翻譯

    英文

    中文

    Argument

    實(shí)際參數(shù)(實(shí)參)

    Parameter

    形式參數(shù)(形參)

    Method

    方法

    Function

    函數(shù)

    pass-by-value

    值傳遞

    pass-by-reference

    引用傳遞

    operand stack

    操作數(shù)堆棧

    Stack frame

    ??蚣?/span>

    Caller

    調(diào)用者



    程序員的一生其實(shí)可短暫了,這電腦一開一關(guān),一天過去了,嚎;電腦一開不關(guān),那就成服務(wù)器了,嚎……

    posted on 2009-04-23 23:31 Heis 閱讀(4999) 評論(7)  編輯  收藏 所屬分類: 雜七雜八

    評論

    # re: Java方法中使用的是值傳遞(pass-by-value)!  回復(fù)  更多評論   

    高深!
    2009-04-24 09:28 | 淘聲依舊

    # re: Java方法中使用的是值傳遞(pass-by-value)!  回復(fù)  更多評論   

    n年前就有人總結(jié)了啊,簡單點(diǎn)說就兩句話:
    1. 基本類型:按值傳遞
    2. 對象:將引用按值傳遞

    搞清楚什么是對象,什么是引用就一切簡單了。不明白的,參考一下c/c++中的指針。
    2009-04-24 13:34 | sky ao

    # re: Java方法中使用的是值傳遞(pass-by-value)!  回復(fù)  更多評論   

    樓上正解,學(xué)習(xí)了??!
    2009-04-24 14:02 | 杰德。張

    # re: Java方法中使用的是值傳遞(pass-by-value)!  回復(fù)  更多評論   

    @sky ao
    精辟!
    我個(gè)人認(rèn)為很多技術(shù)問題不是只了解結(jié)論就行了。
    2009-04-24 14:22 | Heis

    # re: Java方法中使用的是值傳遞(pass-by-value)!  回復(fù)  更多評論   

    這個(gè)是引用傳遞和值傳遞的爭論是沒有意義的,因?yàn)檎f的東西都是不一樣的
    2009-04-24 15:48 | 5452

    # re: Java方法中使用的是值傳遞(pass-by-value)!  回復(fù)  更多評論   

    @5452
    在技術(shù)領(lǐng)域有統(tǒng)一的術(shù)語是非常重要的,當(dāng)然術(shù)語背后的原理更為重要。
    2009-04-24 22:00 | Heis

    # re: Java方法中使用的是值傳遞(pass-by-value)!  回復(fù)  更多評論   

    地球人都知道。
    2009-04-24 23:07 | 9527
    主站蜘蛛池模板: 亚洲日本国产综合高清| 在线不卡免费视频| 4虎永免费最新永久免费地址| 亚洲黄色免费电影| 成熟女人牲交片免费观看视频 | www在线观看播放免费视频日本| 国产午夜不卡AV免费| 1000部羞羞禁止免费观看视频 | 亚洲av无码不卡私人影院| 伊伊人成亚洲综合人网7777| 亚洲自偷自偷精品| 亚洲免费综合色在线视频| sihu国产精品永久免费| 国产92成人精品视频免费| 国内精品免费视频自在线| 亚洲日韩在线第一页| 亚洲综合激情九月婷婷| 亚洲成a人片在线不卡一二三区 | a级精品九九九大片免费看| 在线观看成人免费视频不卡| 免费国产不卡午夜福在线 | 久久久久亚洲精品美女| 久久夜色精品国产噜噜亚洲a| 又长又大又粗又硬3p免费视频| 久久精品视频免费播放| 国产精品国产午夜免费福利看 | 国产AⅤ无码专区亚洲AV | 久久精品亚洲一区二区| 亚洲色大成网站www久久九| 中国一级全黄的免费观看| 国产99视频精品免费观看7| AV在线播放日韩亚洲欧| 亚洲乱码在线视频| 久青草视频在线观看免费| 成年女人色毛片免费看| 亚洲精品无码永久在线观看你懂的 | 亚洲五月综合缴情婷婷| 精品国产免费人成网站| 成人毛片免费在线观看| 久久精品国产精品亚洲艾| 小说专区亚洲春色校园|