日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java is Pass-by-Value, Dammit! 我靠!Java就是值传递!

發布時間:2023/12/20 java 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java is Pass-by-Value, Dammit! 我靠!Java就是值传递! 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Java is Pass-by-Value, Dammit! 我靠!Java就是值傳遞!

原文地址:http://javadude.com/articles/passbyvalue.htm

關鍵點

  • 形如Object o這樣的定義,我們實際上定義一個指針
  • 當我們把此指針傳入方法形參,實則是把此指針所指向的地址傳遞給了方法形參,而不是對象本體的傳入,也就是沒有進行真正意義上的引用傳遞了。

全文翻譯

Introduction 引題

I finally decided to write up a little something about Java’s parameter passing. I’m really tired of hearing folks (incorrectly) state “primitives are passed by value, objects are passed by reference”.

我終于決定要寫一點關于Java參數傳遞的東西了。我真的已經厭倦人云亦云的觀點了——原始數據類型是值傳遞,對象是引用傳遞。

I’m a compiler guy at heart. The terms “pass-by-value” semantics and “pass-by-reference” semantics have very precise definitions, and they’re often horribly abused when folks talk about Java. I want to correct that… The following is how I’d describe these

我有一顆像編譯器樣的心。術語“值傳遞”和“引用傳遞”的語義是有嚴格定義的。然而在大眾談論Java時,這兩個術語總是遭到濫用。我想改變這種情況… 下面我就是我對此的解釋:

Pass-by-value 值傳遞

The actual parameter (or argument expression) is fully evaluated and the resulting value is copied into a location being used to hold the formal parameter’s value during method/function execution. That location is typically a chunk of memory on the runtime stack for the application (which is how Java handles it), but other languages could choose parameter storage differently.

實參(或者變量表達式)已被完整計算,并且結果值被復制到了方法(或函數)執行時負責持有形參值的地方。這個地方通常是應用運行時棧的一塊內存區域(Java就是這么處理的),但是其他語言可以選擇別的參數存儲方式。

Pass-by-reference 引用傳遞

The formal parameter merely acts as an alias for the actual parameter. Anytime the method/function uses the formal parameter (for reading or writing), it is actually using the actual parameter.

形參僅僅為實參扮演了一個別名的角色。任何時候,當方法(或函數)使用這些形參(為了讀寫),它實際上就是在使用實參。

Java is strictly pass-by-value, exactly as in C. Read the Java Language Specification (JLS). It’s spelled out, and it’s correct. Inhttp://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.4.1:

Java是嚴格意義上的值傳遞,如C一樣。閱讀下《Java語言規范》。其中對此已經講的很清楚了這是正確的。原文如下:

When the method or constructor is invoked (�15.12), the?values?of the actual argument expressions initialize newly created parameter variables, each of the declared Type, before execution of the body of the method or constructor. The Identifier that appears in the DeclaratorId may be used as a simple name in the body of the method or constructor to refer to the formal parameter.

當一個方法或構造器被調用時,實參表達式的值會在執行方法或構造器的代碼塊前,初始化剛剛創建的參數變量,以及每個聲明的類型。在方法或構造器的代碼塊中,DeclaratorID中出現的Identifier或許會作為一個簡單的名字,從而去引用一個形參。

In the above,?values?is my emphasis, not theirs

上文中對的加粗是我自己加的,不是原文的

In short: Java has pointers and is strictly pass-by-value. There’s no funky rules. It’s simple, clean, and clear. (Well, as clear as the evil C++-like syntax will allow ;)

簡言之:Java具有指針,并且是嚴格意義上的值傳遞。沒有其他雜七雜八的規定,就是這么簡單、清晰、明了。(這就同類似邪惡C++的語法所允許的那樣一樣清晰;)

Note: See the note at the end of this article for the semantics of remote method invocation (RMI). What is typically called “pass by reference” for remote objects is actually incredibly bad semantics.

注意:請關注文章結尾處關于“遠程方法調用”的文章。遠程對象中所謂的“引用傳遞”的概念實際上是完全錯誤的。

The Litmus Test 立見分曉的測試

There’s a simple “litmus test” for whether a language supports pass-by-reference semantics:

這里有個簡單的“石蕊測試(譯者注:一個比喻,指此測試可以像石蕊試紙那樣立刻看到結果)”,它可應用于任何支持引用傳遞的語言:

Can you write a traditional swap(a,b) method/function in the language?

你可以在此語言環境下寫出一個標準的swap(a,b)方法(函數)嘛?

A traditional swap method or function takes two arguments and swaps them such that variables passed into the function are changed outside the function. Its basic structure looks like

一個標準的swap方法(函數)有兩個參數,它會將這兩者互換。這樣,傳入方法里面的變量就可以改變方法外變量的值。它的基本結構如下:

// Figure 1: (Non-Java) Basic swap function structure
swap(Type arg1, Type arg2) {
Type temp = arg1;
arg1 = arg2;
arg2 = temp;
}

If you can write such a method/function in your language such that calling

如果你可以在你語言環境下寫出這樣的方法(函數),那么這樣的調用

// Figure 2: (Non-Java) Calling the swap function
Type var1 = ...;
Type var2 = ...;
swap(var1,var2);

actually switches the values of the variables var1 and var2, the language supports pass-by-reference semantics.

就會從實際上交換變量var1和var2的值。前提是需要此語言支持引用傳遞

For example, in Pascal, you can write

例如,在Pascal中,你可以這樣寫

//Figure 3: (Pascal) Swap function
procedure swap(var arg1, arg2: SomeType);

var
temp : SomeType;
begin
temp := arg1;
arg1 := arg2;
arg2 := temp;
end;

...

{ in some other procedure/function/program }

var
var1, var2 :
SomeType;

begin
var1 := ...; { value "A" }
var2 := ...; { value "B" }
swap(var1, var2);
{ now var1 has value "B" and var2 has value "A" }
end;

or in C++ you could write

或者在C++中你可以這樣寫

//Figure 4: (C++) Swap function
void swap(SomeType& arg1, Sometype& arg2) {
SomeType temp = arg1;
arg1 = arg2;
arg2 = temp;
}

...

SomeType var1 = ...; // value "A"
SomeType var2 = ...; // value "B"
swap(var1, var2); // swaps their values!
// now var1 has value "B" and var2 has value "A"

(Please let me know if my Pascal or C++ has lapsed and I’ve messed up the syntax…)

(請讓我知道我的Pascal和C++程序對不對,我已經搞混兩者的語法了>_<)

But you cannot do this in Java!

但是你在Java中做不到!

Now the details… 現在看細節

The problem we’re facing here is statements like

我們面對的問題就是類似下面這樣的陳述:

In Java, Objects are passed by reference, and primitives are passed by value.

在Java中,對象是通過引用傳遞的,基本數據類型是值傳遞的

This is half incorrect. Everyone can easily agree that primitives are passed by value; there’s no such thing in Java as a pointer/reference to a primitive.

這句話是半對的。人們都愿意承認非原始數據類型是值傳遞的;Java中沒有這樣一種指針(或引用)指向一個原始數據類型。

However, Objects are not passed by reference. A correct statement would be Object references are passed by value.

然而,對象也并非是引用傳遞的。正確的說法是對象引用是值傳遞的。

This may seem like splitting hairs, but it is far from it. There is a world of difference in meaning. The following examples should help make the distinction.

看起來有些斤斤計較,但是并非如此。這一個充滿不同解釋的世界。下面的例子將有助于區分上面的概念。

In Java, take the case of

在Java中,我們以此為例:

//Figure 5: (Java) Pass-by-value example

public void foo(Dog d) {
d = new Dog("Fifi"); // creating the "Fifi" dog
}

Dog aDog = new Dog("Max"); // creating the "Max" dog
// at this point, aDog points to the "Max" dog
foo(aDog);
// aDog still points to the "Max" dog

the variable passed in (aDog) is not modified! After calling foo, aDog still points to the “Max” Dog!

傳入的變量(即 aDog)并沒有被修改!,在調用foo方法后,aDog依舊指向“Max”Dog對象!

Many people mistakenly think/state that something like

很多人像這樣錯誤的認為(或陳述)這樣的東西:

//Figure 6: (Java) Still pass-by-value...

public void foo(Dog d) {
d.setName("Fifi");
}

shows that Java does in fact pass objects by reference.

表明了Java事實上是通過引用傳遞對象的。

The mistake they make is in the definition of

他們犯的錯誤在于對下面定義代碼的理解

// Figure 7: (Java) Defining a Dog pointer

Dog d;

itself. When you write that definition, you are defining a pointer to a Dog object, not a Dog object itself.

當你寫下這樣的定義時,你就會創建了一個指向Dog對象的指針,而非一個Dog對象本身。

On Pointers versus References… 指針和引用的對比

The problem here is that the folks at Sun made a naming mistake.

問題在于Sun公司的家伙們制造了一個命名錯誤。

In programming language design, a “pointer” is a variable that indirectly tracks the location of some piece of data. The value of a pointer is often the memory address of the data you’re interested in. Some languages allow you to manipulate that address; others do not.

在編程語言的設計中,一個“指針”是一種變量。它間接追蹤某些數據的位置。一個指針的值通常是你所感興趣的數據的內存地址。一些語言允許你操縱這個地址,其他則非也。

A “reference” is an alias to another variable. Any manipulation done to the reference variable directly changes the original variable.

一個“引用”則是另一個變量的別名。任何對此引用的操作都將直接改變原始變量。

Check out the second sentence ofhttp://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.3.1.

看下這個網址的第二句話

“The reference values (often just references) are pointers to these objects, and a special null reference, which refers to no object”

引用的值(或者說就是引用)是這些對象的指針。而一個特別的空引用,它不指向任何對象

They emphasize “pointers” in their description… Interesting…

他們在他們的陳述中強調了“指針”一詞,挺有意思~

When they originally were creating Java, they had “pointer” in mind (you can see some remnants of this in things like?
NullPointerException).

當他們在創作Java之初時,他們的腦海中是有“指針”這個概念的(你可以看到一些殘存的跡象,如空指針異常)。

Sun wanted to push Java as a secure language, and one of Java’s advantages was that it does not allow pointer arithmetic as C++ does.

Sun公司希望把Java做成一個安全語音,并且Java優勢之一就是它不允許像C++那樣的指針運算。

They went so far as to try a different name for the concept, formally calling them “references”. A big mistake and it’s caused even more confusion in the process.

他們努力為此概念嘗試使用一個不同名稱,并在最終正式稱其為“引用”。這是一個大失誤,并且這在后來制造了更多的困惑。

There’s a good explanation of reference variables athttp://www.cprogramming.com/tutorial/references.html. (C++ specific, but it says the right thing about the concept of a reference variable.)

這個網址中有對引用變量的不錯的解釋。(雖然針對C++,但是它正確解釋了引用變量的相關概念)

The word “reference” in programming language design originally comes from how you pass data to subroutines/functions/procedures/methods. A reference parameter is an alias to a variable passed as a parameter.

“引用”一詞在編程語言設計中,最初來源于如何向子程序、函數、過程或方法傳數據。一個引用參數是一個作為參數傳遞的變量的別名。

In the end, Sun made a naming mistake that’s caused confusion. Java has pointers, and if you accept that, it makes the way Java behaves make much more sense.

最后,Sun也就犯了一個命名錯誤,并且這個錯誤引起了不少困惑。Java具有指針,并且如果接受這個現實,這就會讓Java變得更有意義了。

Calling Methods 方法調用

Calling

調用

//Figure 8: (Java) Passing a pointer by value


foo(d);

passes the?value of d?to foo; it does not pass the object that d points to!

這個方法時,會將d的值傳入到foo中;它并不會傳入d所指向的對象

The value of the pointer being passed is similar to a memory address. Under the covers it may be a tad different, but you can think of it in exactly the same way. The value uniquely identifies some object on the heap.

指針所傳入的值類似內存地址。在底層中,兩者有微小差別,但是你可認為兩者完全相同。指針的值唯一標識堆中的一些對象。

However, it makes no difference how pointers are?implemented?under the covers. You program with them?exactly?the same way in Java as you would in C or C++. The syntax is just slightly different (another poor choice in Java’s design; they should have used the same -> syntax for de-referencing as C++).

然而,這不耽誤指針在底層的實現。在Java編程中,你可以完全像C或C++那樣使用指針。就是語法上有點滴的不同(這又是Java設計中的一個軟肋;設計者們本應像C++那樣使用“->”作為解引用語法)

In Java,

在Java中,

//Figure 9: (Java) A pointer

Dog d;

is?exactly?like C++’s

完全像C++中的

//Figure 10: (C++) A pointer

Dog *d;

And using

指針的使用

//Figure 11: (Java) Following a pointer and calling a method

d.setName("Fifi");

is exactly like C++’s

完全像C++的

//Figure 12: (C++) Following a pointer and calling a method

d->setName("Fifi");

To sum up: Java?has?pointers, and the?value?of the pointer is passed in. There’s no way to actually pass an object itself as a parameter. You can only pass a?pointer?to an object.

總結下:Java指針,并且傳入的是指針的。無法傳入一個對象本身作為參數。你僅可以向對象傳入一個指針。

Keep in mind, when you call

記住,當你調用

//Figure 13: (Java) Even more still passing a pointer by value

foo(d);

you’re not passing an object; you’re passing a pointer to the object.

時,你不是在傳一個對象實體,而是在傳入對象的一個指針。

For a slightly different (but still correct) take on this issue, please seehttp://www-106.ibm.com/developerworks/library/j-praxis/pr1.html. It’s from Peter Haggar’s excellent book, Practical Java.)

對于此問題的一個輕微的不同之處(但是依舊是正確的),請參閱網址內容。內陸來自Peter hagger的一本經典之作《Practical Java》

A Note on Remote Method Invocation (RMI) 一條關于遠程方法調用的筆記

When passing parameters to remote methods, things get a bit more complex. First, we’re (usually) dealing with passing data between two independent virtual machines, which might be on separate physical machines as well. Passing the value of a pointer wouldn’t do any good, as the target virtual machine doesn’t have access to the caller’s heap.

當向遠程方法傳參數時,會稍微有點麻煩。首先,我們(經常)在兩個獨立的虛擬機間傳送數據,或許也同時在不同物理機間。傳送一個指針的值不會是個好的選項,因為目標虛擬機無法訪問調用者的堆。

You’ll often hear “pass by value” and “pass by reference” used with respect to RMI. These terms have more of a “logical” meaning, and really aren’t correct for the intended use.

你會在遠程方法調用中常常聽到值傳遞和引用傳遞。這些術語具有比邏輯上更加廣泛的意義,并且對于將要使用的場景而言,這些術語實際上并不準確。

Here’s what is usually meant by these phrases with regard to RMI. Note that this is not proper usage of “pass by value” and “pass by reference” semantics:

這里給出與遠程方法調用RMI相關術語的常用意義。注意,這并非嚴格意義上的“值傳遞”和“引用傳遞”語義:

RMI Pass-by-value 遠程方法調用之值傳遞

The actual parameter is serialized and passed using a network protocol to the target remote object. Serialization essentially “squeezes” the data out of an object/primitive. On the receiving end, that data is used to build a “clone” of the original object or primitive. Note that this process can be rather expensive if the actual parameters point to large objects (or large graphs of objects).

實參被序列化,并利用網絡協議傳送到遠程目標對象。序列化本質上從一個對象(或原始數據類型)中“壓榨”出數據。在接收完畢后,數據會被用于創建原有對象(或原始數據類型)的“克隆”。注意如果實參指向了一個大對象(或者大對象圖),這項操作的代價將會非常巨大。

This isn’t quite the right use of “pass-by-value”; I think it should really be called something like “pass-by-memento”. (See “Design Patterns” by Gamma et al for a description of the Memento pattern).

這并非完全正確地使用了值傳遞;我個人認為這應該被稱為一種類似“記憶傳遞”的東西。(更多內容,請參閱Gamma等人編著的《設計模式》中關于記憶模式的表述)

RMI Pass-by-reference 遠程方法調用之引用傳遞

The actual parameter, which is itself a remote object, is represented by a proxy. The proxy keeps track of where the actual parameter lives, and anytime the target method uses the formal parameter, another remote method invocation occurs to “call back” to the actual parameter. This can be useful if the actual parameter points to a large object (or graph of objects) and there are few call backs.

本身是一個遠程對象的實參是通過代理來表示的。代理會追蹤實參存活的地點,以及目標方法使用形參,或另一個遠程方法回調時,觸發對這個實參的回調的任何時刻。用代理這種方法在一個實參指向了大對象(或大對象圖)時會非常有用,并且回調也會很少。

This isn’t quite the right use of “pass-by-reference” (again, you cannot change the actual parameter itself). I think it should be called something like “pass-by-proxy”. (Again, see “Design Patterns” for descriptions of the Proxy pattern).

這并非是對“引用傳遞”完全正確的使用(再一次強調,你無法改變實參本身)。我認為這樣叫做“代理傳遞”樣的東西(再次推薦閱讀《設計模式》中對代理模式的講解)

Follow up from stackoverflow.com 在stackoverflow.com上跟進問題

I posted the following as some clarification when a discussion on this article arose on?http://stackoverflow.com.

當在stackoverflow上討論此文時,我貼出了如下內容以作說明:

The Java Spec says that everything in java is pass-by-value. There is no such thing as “pass-by-reference” in java.

《Java規范》說Java中的任何傳遞都是值傳遞。在Java中沒有“引用傳遞”一說。

The key to understanding this is that something like

理解此的關鍵,就是要理解下面這樣的代碼:

//Figure 14: (Java) Not a Dog; a pointer to a Dog

Dog myDog;

is not a Dog; it’s actually a pointer to a Dog.

并不代表一個Dog對象;它實際上是執行Dog對象的的一個指針。

What that means, is when you have

它表達的意思是,當你有

// Figure 15: (Java) Passing the Dog's location

Dog myDog = new Dog("Rover");
foo(myDog);

you’re essentially passing the address of the created Dog object to the foo method. (I say essentially b/c java pointers aren’t direct addresses, but it’s easiest to think of them that way)

實際上你正在向foo方法中傳遞創建的Dog對象的地址。(我認為本質上Java指針并非是直接地址,但是這么想會讓理解更容易一些)

Suppose the Dog object resides at memory address 42. This means we pass 42 to the method.

假設Dog對象駐扎在內存地址42中。這意味著我們向方法傳入42。

If the Method were defined as

如果這個方法沒定義成這樣

//Figure 16: (Java) Looking at the called method in detail

public void foo(Dog someDog) {
someDog.setName("Max"); // AAA
someDog = new Dog("Fifi"); // BBB
someDog.setName("Rowlf"); // CCC
}

Let’s look at what’s happening.

讓我們看看這會發生什么。

the parameter someDog is set to the value 42

參數someDog被設置為42。

at line “AAA” 在“AAA”行

someDog is followed to the Dog it points to (the Dog object at address 42) that Dog (the one at address 42) is asked to change his name to Max

someDog指向這個Dog對象(也就是在地址42中存儲的Dog對象),并且這個對象要求將名字改為Max。

at line “BBB” 在“BBB”行

a new Dog is created. Let’s say he’s at address 74 we assign the parameter someDog to 74

一個新的Dog對象被創建。讓我們假設它在內存地址74當中。我們將74傳入someDog對象中。

at line “CCC” 在“CCC”行

someDog is followed to the Dog it points to (the Dog object at address 74) that Dog (the one at address 74) is asked to change his name to Rowlf then, we return

someDog現在指向了地址為74的Dog對象,現在這個位于地址74的Dog對象要求將名字改為Rowlf,然后我們的方法返回了。

Now let’s think about what happens outside the method:

現在讓我們想想在方法外發生了啥:

Did myDog change? myDog發生改變了嘛?

There’s the key.

這是關鍵。

Keeping in mind that myDog is a pointer, and not an actual Dog, the answer is NO. myDog still has the value 42; it’s still pointing to the original Dog.

記住myDog 是一個指針,并非一個真實的Dog對象。答案是“沒有”。myDog依舊是42;依然指向了原來的Dog對象。

It’s perfectly valid to follow an address and change what’s at the end of it; that does not change the variable, however.

追蹤一個地址并在最后改變其內容是完全可以的;然而變量本身并沒有被改變。

Java works exactly like C. You can assign a pointer, pass the pointer to a method, follow the pointer in the method and change the data that was pointed to. However, you cannot change where that pointer points.

Java 非常像C。你可以為一個指針賦值,也可把指針傳入到一個方法中去,還可以使用方法中的指針改變它所指向的內容。然而,你無法改變指針的指向。

In C++, Ada, Pascal and other languages that support pass-by-reference, you can actually change the variable that was passed.

在C++,Ada,Pascal以及其他支持引用傳遞的語言中,你可以對傳入的變量進行實際上的改變。

If Java had pass-by-reference semantics, the foo method we defined above would have changed where myDog was pointing when it assigned someDog on line BBB.

如果Java支持“引用傳遞”的概念,那么我們在上面定義的這個foo方法將會在BBB行,當我們把它賦值給someDog時,改變myDog的指向。

Think of reference parameters as being aliases for the variable passed in. When that alias is assigned, so is the variable that was passed in.

想想作為傳入變量別名的引用參數。當我們分配這個別名時,那么這個別名實際上就是指我們傳入的變量。

總結

以上是生活随笔為你收集整理的Java is Pass-by-Value, Dammit! 我靠!Java就是值传递!的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。