<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
    數據加載中……

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

       實在看不下去網上的一些面試題,很多都是錯的答案。例如像今天這個問題:java方法用的是值傳遞還是引用傳遞。你在blogjava上還能搜到不同的答案呢。最近有空就翻譯了一篇國外的文章,很多東西不能只看答案,而不知其所以然。第一次翻譯文章,博友多指教。
        重申:對于原始類型(primitive type也譯為值類型),是通過拷貝一個相同的值傳給java方法的參數的;而對于引用類型(reference type),就是對象,是通過拷貝一個相同的應用或地址傳給java方法的參數的。業界都統稱這是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://www.tkk7.com/zygcs/archive/2008/10/05/232438.html

    實際參數是如何傳遞到java方法的

    HOW ARGUMENTS ARE PASSED TO JAVA METHODS

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

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

     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,然后作為實際參數傳遞到方法f。這個方法聲明一個相同名字的形式參數arg1,被用于訪問實際參數

    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.

    當你執行這個程序會發生什么事情呢?方法f修改了arg1的值,所以會打印什么呢,5還是10?這里5是正確的答案。這就意味著在方法f中,將arg1設為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:

    為什么呢?答案在于區分使用“值傳遞”方式和“引用傳遞”方式給方法傳遞參數。Java語言使用與眾不同的“值傳遞”方式。在解釋它的意思之前,讓我們看看以下一個值傳遞的例子,這是用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.

    函數f有兩個形參。第一個是值傳遞,第二個是引用傳遞。當你運行這個程序的時候,第一個參數賦值不影響調用者(main函數)。但是第二個參數賦值卻會,事實上,修改了mainarg2的值。在int&中的&表示arg2是一個引用傳遞的形參。這個特別的例子與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:

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

        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的操作數堆棧(operand stack)。然后這個值隨后存儲第二個本地變量(arg1的值存在第一個本地變量處)。iload_1指令將第二個本地變量的值再壓入操作數堆棧,作為被調用方法的形參。

    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指令調用。形參的值出棧,被用于為被調用的方法創建一個棧框架(stack frame)。這個棧框架表示所有f中的所有本地變量,而arg1實參將作為棧框架內的第一個本地變量。

    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.

    這里的的意思是形式參數作為實際參數的拷貝,被傳入方法當中。如果你(在方法體內)修改了一個實際參數,它并不影響調用者,你只是修改了棧框架內的拷貝而已。不可能在調用方法中“取回”實參的值。因此在f中給arg1賦值不會改變main中的arg1,而且這兩個arg1毫無關聯,除了main中的arg1f中的arg1的一個拷貝之外。這兩個變量存在內存的不同區域,事實上雖然它們有同樣的名字,但是毫不相干。

    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.

    對比值傳遞,引用傳遞的參數是指傳遞調用者實參的內存地址給被調用的函數。實參地址拷貝給形參。形參指向了實參的內存區域,所以對形參的修改同樣會修改到實參。從底層的角度來說,你可以任意修改變量的值,只要你有該變量的內存地址。

    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:

    關于參數傳遞的討論會復雜化,這是因為以上引用傳遞中的術語“引用”與Java編程中使用的(術語)存在微妙的區別。在java中,術語“引用”用于對象所有引用的上下文。當你給方法傳遞一個對象引用,你并不是使用引用傳遞,更像是值傳遞。尤其是,這樣會拷貝一份實際參數的對象引用,(通過形參)對該拷貝的修改不會影響到調用者。讓我們用以下兩個例子來證明這個觀點。


     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.

    在這個例子中,A的引用傳遞給f。arg1設置為null不影響調用者,就像前一個例子一樣。程序會打印5。調用者傳遞一份對象引用(arg1)的拷貝,而不是arg1的內存地址。所以調用方法無法取回到(調用者的)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.

    這個例子會打印10。為什么呢?通過之前那個例子,你已經知道無法改變在調用方法中修改(實參)arg1。但是這里的代碼顯示arg1能夠被修改。這里調用方法和被調用方法有個共同的對象,兩個方法都能修改這個對象。在這個例子中,對象的引用(arg1)是值傳遞。他的一個拷貝被加入到f的棧框架。但是原來的和拷貝的對象都是對象引用,它們指向相同的內存區域中的對象,因此可以修改。

    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編程中,經常可以聽到“一個String對象被傳遞到方法f”或者“一個數組被傳到方法g”。從技術上來說,不是傳遞一些對象或數組,而是傳遞引用或地址。例如,如果你有一個java對象包含25個整型的屬性,每個屬性占4 字節,那這個對象大約是占100字節。但是作為形式參數傳遞的時候,不會拷貝100 字節。而是傳遞一個指針、引用或地址。這樣調用者和被調用的方法都指向同一個對象。對比而言,與C++類似的語言,它可以傳遞真實的對象,也可以傳遞該對象的指針。

    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"

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

    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:

    你可以修正以上的例子,當被調用方法修改一個對象時,讓這個類成為不可變的。一個不可變類是指其實例不可修改的類。這里教你怎樣去定義它。


     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.

    打印結果是5,現在去掉CallDemo4f方法內的注釋和重新編譯。注意它會編譯出錯。因為你已經讓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.

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

    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

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

    術語對照翻譯

    英文

    中文

    Argument

    實際參數(實參)

    Parameter

    形式參數(形參)

    Method

    方法

    Function

    函數

    pass-by-value

    值傳遞

    pass-by-reference

    引用傳遞

    operand stack

    操作數堆棧

    Stack frame

    棧框架

    Caller

    調用者



    程序員的一生其實可短暫了,這電腦一開一關,一天過去了,嚎;電腦一開不關,那就成服務器了,嚎……

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

    評論

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

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

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

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

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

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

    樓上正解,學習了!!
    2009-04-24 14:02 | 杰德。張

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

    @sky ao
    精辟!
    我個人認為很多技術問題不是只了解結論就行了。
    2009-04-24 14:22 | Heis

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

    這個是引用傳遞和值傳遞的爭論是沒有意義的,因為說的東西都是不一樣的
    2009-04-24 15:48 | 5452

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

    @5452
    在技術領域有統一的術語是非常重要的,當然術語背后的原理更為重要。
    2009-04-24 22:00 | Heis

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

    地球人都知道。
    2009-04-24 23:07 | 9527
    主站蜘蛛池模板: 亚洲熟妇无码av另类vr影视| 精品久久久久成人码免费动漫| 亚洲午夜电影在线观看| 亚洲男人在线无码视频| 91麻豆最新在线人成免费观看| 精品一区二区三区免费| 免费亚洲视频在线观看| 亚洲AV色吊丝无码| 亚洲综合无码一区二区| 亚洲熟女乱综合一区二区| 国产免费无遮挡精品视频| av无码免费一区二区三区| 日韩一级在线播放免费观看| 无码国产精品一区二区免费| 日韩成人免费aa在线看| 国产亚洲精品免费视频播放| 亚洲男人的天堂在线va拉文| 亚洲韩国—中文字幕| 亚洲国产精品一区二区久久| 亚洲乱码在线观看| 亚洲人成色4444在线观看| 99久久婷婷国产综合亚洲| 亚洲AV无码国产精品色| 黄色毛片免费观看| 免费无码AV一区二区| 特级毛片aaaa免费观看| 牛牛在线精品免费视频观看| 久9久9精品免费观看| 99国产精品免费视频观看| 99久9在线|免费| 日本免费一区二区三区最新| 久久精品国产69国产精品亚洲| 亚洲午夜福利717| 在线亚洲人成电影网站色www| 久久夜色精品国产亚洲| 成人亚洲国产va天堂| 91免费国产视频| 国产精品免费高清在线观看| 59pao成国产成视频永久免费| 国产精品爱啪在线线免费观看| 亚洲国产精品无码久久久久久曰|