7355

Java はpass-by-reference を使用しているといつも思っていました。

ただし、 Java がpass-by-value を使用していると主張するブログ投稿を見たことがあります。

私は彼らが行っている違いを理解していないと思います。

説明は何ですか?

4

92 に答える 92

6612

Javaは常に値渡しです。残念ながら、オブジェクトを処理するときは、実際には、値で渡される参照と呼ばれるオブジェクトハンドルも処理しています。この用語とセマンティクスは、多くの初心者を簡単に混乱させます。

こんなふうになります:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    Dog oldDog = aDog;

    // we pass the object to foo
    foo(aDog);
    // aDog variable is still pointing to the "Max" dog when foo(...) returns
    aDog.getName().equals("Max"); // true
    aDog.getName().equals("Fifi"); // false
    aDog == oldDog; // true
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // change d inside of foo() to point to a new Dog instance "Fifi"
    d = new Dog("Fifi");
    d.getName().equals("Fifi"); // true
}

上記の例でaDog.getName()は、引き続き。を返し"Max"ます。オブジェクト参照が値によって渡されるため、内の値は関数aDogmainで変更されません。参照によって渡された場合、inはへの呼び出しの後に戻ります。fooDog "Fifi"aDog.getName()main"Fifi"foo

同じく:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    Dog oldDog = aDog;

    foo(aDog);
    // when foo(...) returns, the name of the dog has been changed to "Fifi"
    aDog.getName().equals("Fifi"); // true
    // but it is still the same dog:
    aDog == oldDog; // true
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // this changes the name of d to be "Fifi"
    d.setName("Fifi");
}

上記の例では、オブジェクトの名前がの中に設定されFifiているため、は呼び出し後の犬の名前です。で実行される操作は、すべての実用的な目的で、で実行されるようなものですが、変数自体の値を変更することはできません。foo(aDog)foo(...)foodaDogaDog

参照渡しと値渡しの詳細については、次のSO回答を参照してください:https ://stackoverflow.com/a/430958/6005228 。これは、2つの背後にあるセマンティクスと履歴をより完全に説明し、Javaと他の多くの現代言語が特定の場合に両方を実行するように見える理由も説明します。

于 2008-09-02T20:25:37.433 に答える
3421

あなたが私の記事を参照していることに今気づきました。

Java Spec は、Java のすべてが値渡しであると述べています。Javaには「参照渡し」というものはありません。

これを理解するための鍵は、次のようなものです

Dog myDog;

犬ではありません。実際には、Dog へのポインタです。Java での「参照」という用語の使用は非常に誤解を招くものであり、ここでの混乱のほとんどの原因となっています。彼らが「参照」と呼ぶものは、他のほとんどの言語で「ポインタ」と呼ばれるもののように機能/感じます。

それが意味するのは、あなたが持っているときです

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

基本的に、作成されたオブジェクトのアドレスをメソッドに渡しています。Dogfoo

(本質的には、Java ポインター/参照は直接のアドレスではないためですが、そのように考えるのが最も簡単です。)

オブジェクトがメモリ アドレス 42 にあるとしDogます。これは、メソッドに 42 を渡すことを意味します。

メソッドが次のように定義されている場合

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

何が起こっているのか見てみましょう。

  • パラメータsomeDogは値 42 に設定されます
  • 行「AAA」で
    • someDogDogit が指す (Dogアドレス 42 のオブジェクト)に続く
    • そのDog人(住所42の人)は名前をマックスに変えるように頼まれています
  • 行「BBB」で
    • 新しいDogものが作成されます。彼が住所74にいるとしましょう
    • someDogパラメータを74に割り当てます
  • 行「CCC」
    • someDog はDogit が指す (Dogアドレス 74 のオブジェクト)に続きます。
    • あのDog(住所74の人)は名前をRowlfに変えるように頼まれています
  • その後、戻ります

次に、メソッドの外側で何が起こるかを考えてみましょう。

myDog変わった?

鍵があります。

myDogこれはポインタであり、実際の ではないことに注意してDogください。答えは NO です。myDog値は 42 のままです。それはまだ元のものを指していますDog(ただし、「AAA」という行があるため、その名前は「Max」になっていることに注意してください。同じ Dog です。myDog値は変更されていません。)

アドレスをたどって、その末尾にあるものを変更することは完全に有効です。ただし、変数は変更されません。

Java は C とまったく同じように動作します。ポインターを割り当て、ポインターをメソッドに渡し、メソッド内でポインターをたどり、ポイントされたデータを変更できます。ただし、呼び出し元には、そのポインターが指している場所に加えられた変更は表示されません。(参照渡しセマンティクスを持つ言語では、メソッド関数はポインターを変更でき、呼び出し元はその変更を認識します。)

参照渡しをサポートする C++、Ada、Pascal などの言語では、渡された変数を実際に変更できます。

Java に参照渡しセマンティクスがあれば、foo上で定義したメソッドは、行 BBBmyDogに割り当てたときに指している場所が変わっていたでしょう。someDog

参照パラメーターは、渡された変数のエイリアスであると考えてください。そのエイリアスが割り当てられると、渡された変数も割り当てられます。

于 2008-09-16T14:37:00.803 に答える
2022

Java は常に引数を参照ではなく値で渡します。


を通してこれを説明しましょう:

public class Main {

     public static void main(String[] args) {
          Foo f = new Foo("f");
          changeReference(f); // It won't change the reference!
          modifyReference(f); // It will modify the object that the reference variable "f" refers to!
     }

     public static void changeReference(Foo a) {
          Foo b = new Foo("b");
          a = b;
     }

     public static void modifyReference(Foo c) {
          c.setAttribute("c");
     }

}

これを段階的に説明します:

  1. ftypeという名前の参照を宣言し、属性を持つFootype の新しいオブジェクトを割り当てます。Foo"f"

    Foo f = new Foo("f");
    

    ここに画像の説明を入力

  2. メソッド側からはFoo、名前付きの型の参照aが宣言され、最初に割り当てられnullます。

    public static void changeReference(Foo a)
    

    ここに画像の説明を入力

  3. メソッドを呼び出すと、引数として渡されたオブジェクトchangeReferenceに参照aが割り当てられます。

    changeReference(f);
    

    ここに画像の説明を入力

  4. btypeという名前の参照を宣言し、属性を持つFootype の新しいオブジェクトを割り当てます。Foo"b"

    Foo b = new Foo("b");
    

    ここに画像の説明を入力

  5. a = bは、属性が であるオブジェクトのaではなく 、参照に新しい代入を行います。f"b"

    ここに画像の説明を入力

  6. メソッドを呼び出すmodifyReference(Foo c)と、参照cが作成され、属性を持つオブジェクトが割り当てられます"f"

    ここに画像の説明を入力

  7. c.setAttribute("c");参照が指しているオブジェクトの属性を変更し、参照がc指しているのと同じオブジェクトですf

    ここに画像の説明を入力

オブジェクトを引数として渡す方法がJavaでどのように機能するかを理解していただければ幸いです:)

于 2012-09-14T18:22:55.293 に答える
803

Java は常に値渡しであり、例外はありませ

では、なぜ誰もがこれに混乱し、Java が参照渡しであると信じたり、Java が参照渡しとして機能する例を持っていると考えたりするのでしょうか? 重要な点は、どのような状況においても、Java はオブジェクト自体の値への直接アクセスを決して提供しないということです。オブジェクトへの唯一のアクセスは、そのオブジェクトへの参照によるものです。Java オブジェクトは常に直接ではなく参照を介してアクセスされるため、フィールド、変数、およびメソッドの引数オブジェクトとして説明するのが一般的です混乱は、この (厳密に言えば、正しくない) 命名法の変更に起因します。

そのため、メソッドを呼び出すときに

  • プリミティブ引数 ( intlongなど) の場合、値渡しはプリミティブの実際の値(たとえば、3) です。
  • オブジェクトの場合、値渡しは object への参照の値です。

したがってdoSomething(foo)、 2 つの Foo が同じオブジェクトを指す参照public void doSomething(Foo foo) { .. }をコピーしたとします。

当然のことながら、オブジェクトへの参照を値渡しすることは、オブジェクトを参照渡しすることと非常によく似ています (実際には見分けがつきません)。

于 2008-09-02T20:19:21.847 に答える
775

これにより、Java が実際にどのように機能するかについての洞察が得られ、Java の参照渡しまたは値渡しについての次の議論で笑顔になるでしょう :-)

ステップ 1 では、「p」で始まる単語「_ _ _ _ _ _」を頭から消去してください。特に、他のプログラミング言語を使用している場合は注意してください。Java と 'p' を同じ本、フォーラム、さらには txt に書くことはできません。

ステップ 2 では、オブジェクトをメソッドに渡すときは、オブジェクト自体ではなく、オブジェクト参照を渡すことに注意してください。

  • 生徒: 先生、これは Java が参照渡しということですか?
  • マスター:バッタ、No.

オブジェクトの参照/変数が何をするか/何であるかを考えてみましょう:

  1. 変数は、メモリ (ヒープ) 内の参照されたオブジェクトに到達する方法を JVM に指示するビットを保持します。
  2. メソッドに引数を渡すとき、参照変数を渡すのではなく、参照変数のビットのコピーを渡します。このようなもの: 3bad086a. 3bad086a は、渡されたオブジェクトに到達する方法を表します。
  3. したがって、参照の値である 3bad086a を渡すだけです。
  4. 参照自体ではなく、参照の値を渡しています(オブジェクトではありません)。
  5. この値は実際にコピーされ、メソッドに渡されます。

以下では(これをコンパイル/実行しようとしないでください...):

1. Person person;
2. person = new Person("Tom");
3. changeName(person);
4.
5. //I didn't use Person person below as an argument to be nice
6. static void changeName(Person anotherReferenceToTheSamePersonObject) {
7.     anotherReferenceToTheSamePersonObject.setName("Jerry");
8. }

何が起こるのですか?

  • 変数personは 1 行目で作成され、最初は null です。
  • 2 行目で新しい Person オブジェクトが作成され、メモリに格納され、変数personに Person オブジェクトへの参照が与えられます。つまり、そのアドレスです。3bad086a としましょう。
  • オブジェクトのアドレスを保持する変数personは、3 行目で関数に渡されます。
  • 4 行目では、沈黙の音を聞くことができます
  • 5行目のコメントを確認してください
  • メソッド ローカル変数 - anotherReferenceToTheSamePersonObject - が作成され、6 行目に魔法が入ります。
    • 変数/参照はビット単位でコピーされ、関数内のanotherReferenceToTheSamePersonObjectに渡されます。
    • Person の新しいインスタンスは作成されません。
    • " person " と " anotherReferenceToTheSamePersonObject " の両方が 3bad086a という同じ値を保持します。
    • これを試してはいけませんが、 person==anotherReferenceToTheSamePersonObject は true になります。
    • 両方の変数には参照の同一のコピーがあり、両方とも同じ人物オブジェクト、ヒープ上の同じオブジェクトを参照し、コピーではありません。

百聞は一見に如かず。

値渡し

anotherReferenceToTheSamePersonObject の矢印は、変数 person ではなくオブジェクトに向けられていることに注意してください。

わからない場合は、私を信じてください。Java は値渡しであると言ったほうがよいことを覚えておいてください。さて、参照値で渡します。まあ、さらに良いのは、変数値のコピーによる受け渡しです! ;)

私を嫌いになっても構いませんが、これを考えると、メソッドの引数について話すときに、プリミティブ データ型を渡すこととオブジェクトを渡すことの間に違いはないことに注意してください。

常に参照の値のビットのコピーを渡します!

  • プリミティブ データ型の場合、これらのビットにはプリミティブ データ型自体の値が含まれます。
  • それがオブジェクトの場合、ビットにはオブジェクトへのアクセス方法を JVM に伝えるアドレスの値が含まれます。

メソッド内で参照されるオブジェクトを好きなだけ変更できるため、Java は値渡しです。 _ _ _ _) 何があっても同じオブジェクト!


上記の changeName 関数は、渡された参照の実際の内容 (ビット値) を変更することはできません。つまり、changeName は Person person に別の Object を参照させることはできません。


もちろん、 Java は値渡しであると簡単に言うことができ ます。

于 2011-08-12T01:22:19.130 に答える
373

Java は参照を値で渡します。

したがって、渡される参照を変更することはできません。

于 2008-09-02T20:20:08.710 に答える
212

対比を示すために、次のC++Javaのスニペットを比較してください。

C++ の場合:注: 悪いコード - メモリ リーク! しかし、それは要点を示しています。

void cppMethod(int val, int &ref, Dog obj, Dog &objRef, Dog *objPtr, Dog *&objPtrRef)
{
    val = 7; // Modifies the copy
    ref = 7; // Modifies the original variable
    obj.SetName("obj"); // Modifies the copy of Dog passed
    objRef.SetName("objRef"); // Modifies the original Dog passed
    objPtr->SetName("objPtr"); // Modifies the original Dog pointed to 
                               // by the copy of the pointer passed.
    objPtr = new Dog("newObjPtr");  // Modifies the copy of the pointer, 
                                   // leaving the original object alone.
    objPtrRef->SetName("objRefPtr"); // Modifies the original Dog pointed to 
                                    // by the original pointer passed. 
    objPtrRef = new Dog("newObjPtrRef"); // Modifies the original pointer passed
}

int main()
{
    int a = 0;
    int b = 0;
    Dog d0 = Dog("d0");
    Dog d1 = Dog("d1");
    Dog *d2 = new Dog("d2");
    Dog *d3 = new Dog("d3");
    cppMethod(a, b, d0, d1, d2, d3);
    // a is still set to 0
    // b is now set to 7
    // d0 still have name "d0"
    // d1 now has name "objRef"
    // d2 now has name "objPtr"
    // d3 now has name "newObjPtrRef"
}

ジャワでは、

public static void javaMethod(int val, Dog objPtr)
{
   val = 7; // Modifies the copy
   objPtr.SetName("objPtr") // Modifies the original Dog pointed to 
                            // by the copy of the pointer passed.
   objPtr = new Dog("newObjPtr");  // Modifies the copy of the pointer, 
                                  // leaving the original object alone.
}

public static void main()
{
    int a = 0;
    Dog d0 = new Dog("d0");
    javaMethod(a, d0);
    // a is still set to 0
    // d0 now has name "objPtr"
}

Java には、組み込み型の値渡しと、オブジェクト型のポインターの値渡しの 2 種類の受け渡ししかありません。

于 2008-09-17T17:38:43.447 に答える
209

基本的に、オブジェクト パラメーターを再割り当てしても、引数には影響しません。

private void foo(Object bar) {
    bar = null;
}

public static void main(String[] args) {
    String baz = "Hah!";
    foo(baz);
    System.out.println(baz);
}

"Hah!"の代わりに出力されnullます。これが機能する理由barは、 が の値のコピーであり、bazへの単なる参照であるため"Hah!"です。それが実際の参照自体である場合は、fooに再定義bazされnullます。

于 2008-09-02T20:21:04.683 に答える
204

Java はオブジェクトへの参照を値で渡します。

于 2008-09-02T20:23:30.340 に答える
165

バーバラ・リスコフのことをまだ誰も言及していないなんて信じられない。彼女が 1974 年にCLUを設計したとき、彼女はこれと同じ用語の問題に遭遇し、「値が参照」。

于 2010-09-07T22:07:02.323 に答える
133

The crux of the matter is that the word reference in the expression "pass by reference" means something completely different from the usual meaning of the word reference in Java.

Usually in Java reference means a a reference to an object. But the technical terms pass by reference/value from programming language theory is talking about a reference to the memory cell holding the variable, which is something completely different.

于 2009-01-12T20:50:25.880 に答える
94

Javaではすべてが参照であるため、次のようなものがある場合: Point pnt1 = new Point(0,0);Javaは次のことを行います:

  1. 新しいポイント オブジェクトを作成します
  2. 新しい Point 参照を作成し、その参照を以前に作成された Point オブジェクトのポイント (参照)に初期化します。
  3. ここから Point オブジェクトの life を介して、pnt1 参照を介してそのオブジェクトにアクセスします。したがって、Java では、参照を通じてオブジェクトを操作すると言えます。

ここに画像の説明を入力

Java は参照によってメソッド引数を渡しません。それらを値で渡します。このサイトの例を使用します:

public static void tricky(Point arg1, Point arg2) {
  arg1.x = 100;
  arg1.y = 100;
  Point temp = arg1;
  arg1 = arg2;
  arg2 = temp;
}
public static void main(String [] args) {
  Point pnt1 = new Point(0,0);
  Point pnt2 = new Point(0,0);
  System.out.println("X1: " + pnt1.x + " Y1: " +pnt1.y); 
  System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);
  System.out.println(" ");
  tricky(pnt1,pnt2);
  System.out.println("X1: " + pnt1.x + " Y1:" + pnt1.y); 
  System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);  
}

プログラムの流れ:

Point pnt1 = new Point(0,0);
Point pnt2 = new Point(0,0);

2 つの異なる参照が関連付けられた 2 つの異なる Point オブジェクトを作成します。 ここに画像の説明を入力

System.out.println("X1: " + pnt1.x + " Y1: " +pnt1.y); 
System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);
System.out.println(" ");

予想どおり、出力は次のようになります。

X1: 0     Y1: 0
X2: 0     Y2: 0

この行で「値渡し」が機能します...

tricky(pnt1,pnt2);           public void tricky(Point arg1, Point arg2);

参照pnt1pnt2はトリッキーなメソッドに値で渡されます。つまり、あなたの参照が名前付きと.Sopnt1を持ち、同じオブジェクトを指すことになります。(とも同じ) pnt2copiesarg1arg2pnt1arg1 pnt2arg2ここに画像の説明を入力

trickyメソッドでは:

 arg1.x = 100;
 arg1.y = 100;

ここに画像の説明を入力

次のtrickyメソッド

Point temp = arg1;
arg1 = arg2;
arg2 = temp;

ここでは、最初に参照のように同じ場所を指す新しいtempポイント参照を作成します。次に、参照のように同じ場所を指すように参照を移動します。最後にのように同じ場所を指します。arg1arg1arg2arg2temp

ここに画像の説明を入力

ここからtrickyメソッドのスコープがなくなり、参照にアクセスできなくなります: arg1arg2temp. しかし、重要な注意点は、これらの参照が「生きている」ときにこれらの参照で行うすべてのことは、それらが指しているオブジェクトに永続的に影響するということです。

メソッド を実行した後tricky、 に戻るとmain、次のような状況になります。 ここに画像の説明を入力

したがって、プログラムの完全な実行は次のようになります。

X1: 0         Y1: 0
X2: 0         Y2: 0
X1: 100       Y1: 100
X2: 0         Y2: 0
于 2013-11-21T16:04:12.383 に答える
86

Javaは値渡しです(スタックメモリ)

使い方

  • まず、Java がプリミティブ データ型とオブジェクト データ型を格納する場所を理解しましょう。

  • プリミティブ データ型自体とオブジェクト参照はスタックに格納されます。オブジェクト自体はヒープに格納されます。

  • つまり、スタック メモリにはプリミティブ データ型とオブジェクトのアドレスが格納されます。

  • そして、参照の値のビットのコピーを常に渡します。

  • プリミティブ データ型の場合、これらのコピーされたビットにはプリミティブ データ型自体の値が含まれます。そのため、メソッド内で引数の値を変更すると、外部の変更が反映されません。

  • Foo foo=new Foo()のようなオブジェクト データ型の場合、この場合、オブジェクトのアドレスのコピーがファイル Shortcut のように渡されます。C:\desktopにテキスト ファイルabc.txtがあり、そのショートカットを作成するとします。同じファイルをC:\desktop\abc-shortcut内に配置して、 C:\desktop\abc.txtからファイルにアクセスし、「Stack Overflow」と記述してファイルを閉じ、再度ショートカットからファイルを開くと、 write ' はプログラマーが学ぶ最大のオンライン コミュニティです' とすると、ファイルの合計変更量は'Stack Overflow はプログラマーが学ぶ最大のオンライン コミュニティです' になります。つまり、同じファイルにアクセスするたびに、どこからファイルを開くかは問題ではありません。ここでは、Fooをファイルと見なし、foo が123hd7h ( C:\desktop\abc.txtのような元のアドレス)に格納されていると想定できます。アドレスと234jdid ( C:\desktop\abc-shortcut のようなコピーされたアドレスで、実際には内部にファイルの元のアドレスが含まれています) .. 理解を深めるために、ショートカット ファイルを作成して感じます。

于 2017-04-08T05:51:29.657 に答える
61

私の知る限り、Java は値渡しのみを認識します。つまり、プリミティブ データ型の場合はコピーを使用し、オブジェクトの場合はオブジェクトへの参照のコピーを使用します。ただし、いくつかの落とし穴があると思います。たとえば、これは機能しません。

public static void swap(StringBuffer s1, StringBuffer s2) {
    StringBuffer temp = s1;
    s1 = s2;
    s2 = temp;
}


public static void main(String[] args) {
    StringBuffer s1 = new StringBuffer("Hello");
    StringBuffer s2 = new StringBuffer("World");
    swap(s1, s2);
    System.out.println(s1);
    System.out.println(s2);
}

これは、World Hello ではなく Hello World にデータを入力します。これは、swap 関数でメインの参照に影響を与えないコピーを使用するためです。ただし、オブジェクトが不変でない場合は、次のように変更できます。

public static void appendWorld(StringBuffer s1) {
    s1.append(" World");
}

public static void main(String[] args) {
    StringBuffer s = new StringBuffer("Hello");
    appendWorld(s);
    System.out.println(s);
}

これにより、コマンド ラインに Hello World が入力されます。StringBuffer を String に変更すると、String は不変であるため、Hello のみが生成されます。例えば:

public static void appendWorld(String s){
    s = s+" World";
}

public static void main(String[] args) {
    String s = new String("Hello");
    appendWorld(s);
    System.out.println(s);
}

ただし、次のような String のラッパーを作成して、String で使用できるようにすることもできます。

class StringWrapper {
    public String value;

    public StringWrapper(String value) {
        this.value = value;
    }
}

public static void appendWorld(StringWrapper s){
    s.value = s.value +" World";
}

public static void main(String[] args) {
    StringWrapper s = new StringWrapper("Hello");
    appendWorld(s);
    System.out.println(s.value);
}

編集: String のような不変オブジェクトではできない元のオブジェクトを変更できるため、これが2つの文字列を「追加」するときに StringBuffer を使用する理由でもあると思います。

于 2009-04-01T21:33:20.723 に答える
58

いいえ、参照渡しではありません。

Javaは、Java言語仕様に従って値渡しされます。

メソッドまたはコンストラクターが呼び出されると(§15.12)、実際の引数式の値は、メソッドまたはコンストラクターの本体を実行する前に、宣言された型のそれぞれで、新しく作成されたパラメーター変数を初期化します。DeclaratorIdに表示される識別子は、仮パラメーターを参照するために、メソッドまたはコンストラクターの本体で単純な名前として使用できます。

于 2012-10-12T03:00:04.803 に答える
55

4 つの例を使って、私の理解を説明してみましょう。Java は値渡しであり、参照渡しではありません

/**

値渡し

Java では、すべてのパラメーターが値で渡されます。つまり、メソッド引数の割り当ては、呼び出し元には表示されません。

*/

例 1:

public class PassByValueString {
    public static void main(String[] args) {
        new PassByValueString().caller();
    }

    public void caller() {
        String value = "Nikhil";
        boolean valueflag = false;
        String output = method(value, valueflag);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'value' and 'valueflag'
         */
        System.out.println("output : " + output);
        System.out.println("value : " + value);
        System.out.println("valueflag : " + valueflag);

    }

    public String method(String value, boolean valueflag) {
        value = "Anand";
        valueflag = true;
        return "output";
    }
}

結果

output : output
value : Nikhil
valueflag : false

例 2:

/** * * 値渡し * */

public class PassByValueNewString {
    public static void main(String[] args) {
        new PassByValueNewString().caller();
    }

    public void caller() {
        String value = new String("Nikhil");
        boolean valueflag = false;
        String output = method(value, valueflag);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'value' and 'valueflag'
         */
        System.out.println("output : " + output);
        System.out.println("value : " + value);
        System.out.println("valueflag : " + valueflag);

    }

    public String method(String value, boolean valueflag) {
        value = "Anand";
        valueflag = true;
        return "output";
    }
}

結果

output : output
value : Nikhil
valueflag : false

例 3:

/** この「値渡し」は「参照渡し」の感覚があります

プリミティブ型と「文字列」は「値渡し」であり、オブジェクトは「参照渡し」であると言う人もいます。

しかし、この例から、実際には値渡しのみであることがわかります。ここでは参照を値として渡していることに注意してください。つまり、参照は値によって渡されます。そのため、変更することができますが、ローカルスコープの後でもそれは当てはまります。ただし、元のスコープ外で実際の参照を変更することはできません。それが何を意味するかは、次の PassByValueObjectCase2 の例で示されています。

*/

public class PassByValueObjectCase1 {

    private class Student {
        int id;
        String name;
        public Student() {
        }
        public Student(int id, String name) {
            super();
            this.id = id;
            this.name = name;
        }
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        @Override
        public String toString() {
            return "Student [id=" + id + ", name=" + name + "]";
        }
    }

    public static void main(String[] args) {
        new PassByValueObjectCase1().caller();
    }

    public void caller() {
        Student student = new Student(10, "Nikhil");
        String output = method(student);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'student'
         */
        System.out.println("output : " + output);
        System.out.println("student : " + student);
    }

    public String method(Student student) {
        student.setName("Anand");
        return "output";
    }
}

結果

output : output
student : Student [id=10, name=Anand]

例 4:

/**

例 3 (PassByValueObjectCase1.java) で述べたことに加えて、元のスコープ外の実際の参照を変更することはできません。

注: のコードは貼り付けていませんprivate class Student。のクラス定義Studentは Example3 と同じです。

*/

public class PassByValueObjectCase2 {

    public static void main(String[] args) {
        new PassByValueObjectCase2().caller();
    }

    public void caller() {
        // student has the actual reference to a Student object created
        // can we change this actual reference outside the local scope? Let's see
        Student student = new Student(10, "Nikhil");
        String output = method(student);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'student'
         */
        System.out.println("output : " + output);
        System.out.println("student : " + student); // Will it print Nikhil or Anand?
    }

    public String method(Student student) {
        student = new Student(20, "Anand");
        return "output";
    }

}

結果

output : output
student : Student [id=10, name=Nikhil]
于 2015-05-12T21:24:07.773 に答える
52

Javaでは参照を渡すことはできません。明らかな方法の1つは、メソッド呼び出しから複数の値を返したい場合です。C++の次のコードについて考えてみます。

void getValues(int& arg1, int& arg2) {
    arg1 = 1;
    arg2 = 2;
}
void caller() {
    int x;
    int y;
    getValues(x, y);
    cout << "Result: " << x << " " << y << endl;
}

Javaで同じパターンを使用したい場合がありますが、使用できません。少なくとも直接ではありません。代わりに、次のようなことを行うことができます。

void getValues(int[] arg1, int[] arg2) {
    arg1[0] = 1;
    arg2[0] = 2;
}
void caller() {
    int[] x = new int[1];
    int[] y = new int[1];
    getValues(x, y);
    System.out.println("Result: " + x[0] + " " + y[0]);
}

以前の回答で説明したように、Javaでは、配列へのポインタを値としてに渡しますgetValues。このメソッドは配列要素を変更するため、これで十分です。慣例により、要素0に戻り値が含まれることを期待しています。もちろん、これは他の方法でも実行できます。たとえば、コードを構造化してこれが不要になるようにしたり、戻り値を含めるか設定できるようにするクラスを構築したりできます。ただし、上記のC++で使用できる単純なパターンはJavaでは使用できません。

于 2009-03-08T06:28:52.767 に答える
51

仕様から詳細を追加するために、この回答に貢献すると思いました。

まず、参照渡しと値渡しの違いは何ですか?

参照渡しとは、呼び出された関数のパラメーターが、呼び出し元に渡された引数と同じになることを意味します (値ではなく ID

  • 変数自体)。

値渡しとは、呼び出された関数のパラメーターが、呼び出し元に渡された引数のコピーになることを意味します。

またはウィキペディアから、参照渡しについて

参照渡し評価 (参照渡しとも呼ばれます) では、関数は、値のコピーではなく、引数として使用される変数への暗黙的な参照を受け取ります。これは通常、関数が引数として使用される変数を変更 (つまり、代入) できることを意味します。これは呼び出し元から見えるものです。

そして、値渡しについて

値渡しでは、引数式が評価され、結果の値が関数内の対応する変数にバインドされます [...]。関数またはプロシージャがそのパラメーターに値を割り当てることができる場合、そのローカル コピーのみが割り当てられます [...]。

次に、Java がメソッド呼び出しで何を使用するかを知る必要があります。Java 言語仕様の状態

メソッドまたはコンストラクターが呼び出されると (§15.12)、実引数式の値は、メソッドまたはコンストラクターの本体の実行前に、新しく作成されたパラメーター変数(宣言された型のそれぞれ) を初期化します。

したがって、引数の値を対応するパラメーター変数に割り当てます (またはバインドします)。

引数の値は何ですか?

参照型を考えてみましょう。Java 仮想マシン仕様は次のように述べています。

参照型には、クラス型、配列型、インターフェイス型の 3 種類があります。それらの値は、それぞれ、動的に作成されたクラス インスタンス、配列、またはインターフェイスを実装するクラス インスタンスまたは配列への参照です。

Java言語仕様にも記載されています

参照値 (多くの場合、単なる参照) は、これらのオブジェクトへのポインターであり、オブジェクトを参照しない特別な null 参照です。

(何らかの参照型の) 引数の値は、オブジェクトへのポインターです。変数、参照型の戻り値を持つメソッドの呼び出し、およびインスタンス作成式 ( new ...) はすべて、参照型の値に解決されることに注意してください。

そう

public void method (String param) {}
...
String variable = new String("ref");
method(variable);
method(variable.toString());
method(new String("ref"));

Stringすべてインスタンスへの参照の値を、メソッドの新しく作成されたパラメーター にバインドしますparam。これはまさに、値渡しの定義で説明されていることです。そのため、Java は値渡しです

参照に従ってメソッドを呼び出したり、参照されたオブジェクトのフィールドにアクセスしたりできるという事実は、会話とはまったく関係ありません。参照渡しの定義は

これは通常、関数が引数として使用される変数を変更 (つまり、代入) できることを意味します。これは呼び出し元から見えるものです。

Java では、変数の変更は再割り当てを意味します。Java では、メソッド内で変数を再割り当てした場合、呼び出し元には気付かれませんでした。変数によって参照されるオブジェクトを変更することは、まったく別の概念です。


プリミティブ値は、Java 仮想マシン仕様 (こちら) でも定義されています。型の値は、対応する整数値または浮動小数点値であり、適切にエンコードされます (8、16、32、64 ビットなど)。

于 2014-07-03T06:48:11.940 に答える
44

違い、またはおそらく元のポスターと同じ印象を受けていたときに覚えている方法は次のとおりです。Javaは常に価値によって渡されます。Javaのすべてのオブジェクト(Javaでは、プリミティブを除くすべて)は参照です。これらの参照は値によって渡されます。

于 2008-09-03T19:42:03.677 に答える
42

多くの人が前に述べたように、Javaは常に値渡しです

違いを理解するのに役立つ別の例を次に示します(従来のスワップの例)。

public class Test {
  public static void main(String[] args) {
    Integer a = new Integer(2);
    Integer b = new Integer(3);
    System.out.println("Before: a = " + a + ", b = " + b);
    swap(a,b);
    System.out.println("After: a = " + a + ", b = " + b);
  }

  public static swap(Integer iA, Integer iB) {
    Integer tmp = iA;
    iA = iB;
    iB = tmp;
  }
}

プリント:

前:a = 2、b = 3
後:a = 2、b = 3

これは、iAとiBが、渡された参照と同じ値を持つ新しいローカル参照変数であるために発生します(それぞれaとbを指します)。したがって、iAまたはiBの参照を変更しようとすると、ローカルスコープ内でのみ変更され、このメソッドの外部では変更されません。

于 2008-09-03T20:01:59.667 に答える
40

私はいつもそれを「コピーで渡す」と考えています。これは、プリミティブまたは参照である値のコピーです。プリミティブの場合は値であるビットのコピーであり、オブジェクトの場合は参照のコピーです。

public class PassByCopy{
    public static void changeName(Dog d){
        d.name = "Fido";
    }
    public static void main(String[] args){
        Dog d = new Dog("Maxx");
        System.out.println("name= "+ d.name);
        changeName(d);
        System.out.println("name= "+ d.name);
    }
}
class Dog{
    public String name;
    public Dog(String s){
        this.name = s;
    }
}

Java PassByCopy の出力:

名前=マックス
名前=ファイド

プリミティブ ラッパー クラスと文字列は不変であるため、これらの型を使用する例は、他の型/オブジェクトと同じようには機能しません。

于 2008-09-08T14:48:41.293 に答える
36

Javaには値渡ししかありません。これを検証するための非常に簡単な例。

public void test() {
    MyClass obj = null;
    init(obj);
    //After calling init method, obj still points to null
    //this is because obj is passed as value and not as reference.
}
private void init(MyClass objVar) {
    objVar = new MyClass();
}
于 2013-07-10T06:31:27.303 に答える
33

簡単に言うと、Javaオブジェクトにはいくつかの非常に独特な特性があります。

一般に、Javaには、値によって直接渡されるプリミティブ型(、、、、intなど)があります。次に、Javaにはオブジェクト(から派生するすべてのもの)があります。オブジェクトは実際には常に参照(参照はタッチできないポインタ)を介して処理されます。つまり、参照は通常は面白くないため、実際には、オブジェクトは参照によって渡されます。ただし、参照自体が値によって渡されるため、どのオブジェクトがポイントされるかを変更できないことを意味します。boolchardoublejava.lang.Object

これは奇妙で紛らわしいように聞こえますか?Cが参照渡しと値渡しをどのように実装するかを考えてみましょう。Cでは、デフォルトの規則は値による受け渡しです。void foo(int x)intを値で渡します。void foo(int *x)は必要ありませんint aが、intへのポインタを必要とする関数ですfoo(&a)。これを&演算子とともに使用して、変数アドレスを渡します。

これをC++に移すと、参照があります。参照は基本的に(このコンテキストでは)方程式のポインター部分を隠す構文糖衣です:void foo(int &x)によって呼び出されfoo(a)ます。コンパイラー自体はそれが参照であり、非参照のアドレスをa渡す必要があることを認識しています。Javaでは、オブジェクトを参照するすべての変数は実際には参照型であり、事実上、C ++などによって提供されるきめ細かい制御(および複雑さ)なしで、ほとんどの意図と目的のために参照による呼び出しを強制します。

于 2008-09-02T20:53:59.393 に答える
30

私は、プログラミング言語に関するこの種の質問専用のスレッドをここに作成しました。

Java についても言及されています。短い要約は次のとおりです。

  • Javaはパラメータを値で渡します
  • 「値渡し」は、Java でパラメータをメソッドに渡す唯一の方法です。
  • パラメーターとして指定されたオブジェクトのメソッドを使用すると、参照が元のオブジェクトを指しているため、オブジェクトが変更されます。(そのメソッド自体がいくつかの値を変更する場合)
于 2008-09-08T14:54:48.563 に答える
30

いくつかの投稿にいくつかの修正。

C は参照渡しをサポートしていません。常に値渡しです。C++ は参照渡しをサポートしていますが、デフォルトではなく、非常に危険です。

Java の値が何であるかは問題ではありません。オブジェクトのプリミティブまたはアドレス (大まかに) であり、常に値によって渡されます。

Java オブジェクトが参照によって渡されるように「動作」する場合、それは可変性のプロパティであり、メカニズムの受け渡しとはまったく関係ありません。

なぜこれがそれほど混乱しているのかはわかりません。おそらく、多くの Java "プログラマー" が正式な訓練を受けておらず、メモリ内で実際に何が起こっているのかを理解していないためでしょうか?

于 2009-12-26T20:19:22.790 に答える
26

Java プログラミング言語における最大の混乱の 1 つは、Java が値渡し参照渡しかということです。

まず、値渡しまたは参照渡しの意味を理解する必要があります。

値渡し:メソッドのパラメーター値が別の変数にコピーされ、コピーされたオブジェクトが渡されるため、値渡しと呼ばれます。

参照渡し:実際のパラメーターへのエイリアスまたは参照がメソッドに渡されるため、参照渡しと呼ばれます。

以下のようなクラス Balloon があるとします。

public class Balloon {

    private String color;

    public Balloon(){}

    public Balloon(String c){
        this.color=c;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}

そして、2 つのオブジェクトを交換するための一般的なメソッドを持つ単純なプログラムがあります。クラスは以下のようになります。

public class Test {

    public static void main(String[] args) {

        Balloon red = new Balloon("Red"); //memory reference 50
        Balloon blue = new Balloon("Blue"); //memory reference 100

        swap(red, blue);
        System.out.println("red color="+red.getColor());
        System.out.println("blue color="+blue.getColor());

        foo(blue);
        System.out.println("blue color="+blue.getColor());

    }

    private static void foo(Balloon balloon) { //baloon=100
        balloon.setColor("Red"); //baloon=100
        balloon = new Balloon("Green"); //baloon=200
        balloon.setColor("Blue"); //baloon = 200
    }

    //Generic swap method
    public static void swap(Object o1, Object o2){
        Object temp = o1;
        o1=o2;
        o2=temp;
    }
}

上記のプログラムを実行すると、次の出力が得られます。

red color=Red
blue color=Blue
blue color=Red

出力の最初の 2 行を見ると、swap メソッドが機能していないことは明らかです。これは、Java が値渡しされるためです。この swap() メソッド テストは、任意のプログラミング言語で使用して、値渡しか参照渡しかを確認できます。

プログラムの実行を段階的に分析してみましょう。

Balloon red = new Balloon("Red");
Balloon blue = new Balloon("Blue");

new 演算子を使用してクラスのインスタンスを作成すると、インスタンスが作成され、変数にはオブジェクトが保存されるメモリの参照場所が含まれます。この例では、「赤」が 50 を指し、「青」が 100 を指しており、これらが両方の Balloon オブジェクトのメモリ位置であると仮定します。

swap() メソッドを呼び出すと、それぞれ 50 と 100 を指す 2 つの新しい変数 o1 と o2 が作成されます。

したがって、以下のコード スニペットは、swap() メソッドの実行で何が起こったかを説明しています。

public static void swap(Object o1, Object o2){ //o1=50, o2=100
    Object temp = o1; //temp=50, o1=50, o2=100
    o1=o2; //temp=50, o1=100, o2=100
    o2=temp; //temp=50, o1=100, o2=50
} //method terminated

o1 と o2 の値を変更していることに注意してください。ただし、これらは「赤」と「青」の参照位置のコピーであるため、実際には「赤」と「青」の値、つまり出力に変化はありません。

ここまで理解できれば、混乱の原因は簡単に理解できます。変数はオブジェクトへの単なる参照であるため、参照を渡していると混乱し、Java は参照によって渡されます。ただし、参照のコピーを渡しているため、値渡しです。これですべての疑問が解消されることを願っています。

それでは、 foo()メソッドの実行を分析してみましょう。

private static void foo(Balloon balloon) { //baloon=100
    balloon.setColor("Red"); //baloon=100
    balloon = new Balloon("Green"); //baloon=200
    balloon.setColor("Blue"); //baloon = 200
}

最初の行は、参照位置のオブジェクトでメソッドが呼び出されるメソッドを呼び出すときに重要な行です。この時点で、バルーンは 100 を指しているため、色が赤に変わります。

次の行では、バルーン参照が 200 に変更され、実行されるその他のメソッドはメモリ位置 200 のオブジェクトで発生し、メモリ位置 100 のオブジェクトには影響しません。これは、青色を印刷するプログラム出力の 3 行目を説明しています。 =レッド。

上記の説明ですべての疑問が解消されることを願っています。変数は参照またはポインターであり、そのコピーがメソッドに渡されることを覚えておいてください。したがって、Java は常に値渡しされます。ヒープ メモリとスタック メモリ、およびさまざまなオブジェクトと参照が格納されている場所について学習すると、より明確になります。

于 2017-09-06T14:13:22.680 に答える
24

Java はパラメーターを VALUE と値のみで渡します。

簡単に言えば、次のとおりです。

C# から来た場合: 「out」パラメータはありません。

PASCAL を使用している場合: 「var」パラメーターはありません。

つまり、オブジェクト自体から参照を変更することはできませんが、オブジェクトのプロパティはいつでも変更できます。

StringBuilder回避策は、代わりにパラメータを使用することStringです。そして、いつでも配列を使用できます!

于 2015-03-18T21:35:08.447 に答える
24

これが質問に答える最良の方法です...

まず、Java では、パラメーターの受け渡しの動作...

public void foo(Object param)
{
  // some code in foo...
}

public void bar()
{
  Object obj = new Object();

  foo(obj);
}

とまったく同じです...

public void bar()
{
  Object obj = new Object();

  Object param = obj;

  // some code in foo...
}

この議論には関係のないスタックの場所を考慮していません。

実際、私たちがJavaで探しているのは、変数の割り当てがどのように機能するかです。ドキュメントで見つけました:

遭遇する最も一般的な演算子の 1 つは、単純な代入演算子 "=" [...] です。これは、右側の値を左側のオペランドに割り当てます。

int ケイデンス = 0;
int 速度 = 0;
int ギア = 1;

この演算子は、オブジェクト参照を割り当てるためにオブジェクトで使用することもできます[...]

この演算子が、値の代入と参照の代入という 2 つの異なる方法でどのように機能するかは明らかです。最後はオブジェクトの場合... 最初はオブジェクトではない場合、つまりプリミティブの場合です。しかし、Java の関数パラメーターは値渡しと参照渡しであることが理解できますか?

真実はコードにあります。試してみよう:

public class AssignmentEvaluation
{
  static public class MyInteger
  {
    public int value = 0;
  }

  static public void main(String[] args)
  {
    System.out.println("Assignment operator evaluation using two MyInteger objects named height and width\n");

    MyInteger height = new MyInteger();
    MyInteger width  = new MyInteger();

    System.out.println("[1] Assign distinct integers to height and width values");

    height.value = 9;
    width.value  = 1;

    System.out.println("->  height is " + height.value + " and width is " + width.value + ", we are different things! \n");

    System.out.println("[2] Assign to height's value the width's value");

    height.value = width.value;

    System.out.println("->  height is " + height.value + " and width is " + width.value + ", are we the same thing now? \n");

    System.out.println("[3] Assign to height's value an integer other than width's value");

    height.value = 9;

    System.out.println("->  height is " + height.value + " and width is " + width.value + ", we are different things yet! \n");

    System.out.println("[4] Assign to height the width object");

    height = width;

    System.out.println("->  height is " + height.value + " and width is " + width.value + ", are we the same thing now? \n");

    System.out.println("[5] Assign to height's value an integer other than width's value");

    height.value = 9;

    System.out.println("->  height is " + height.value + " and width is " + width.value + ", we are the same thing now! \n");

    System.out.println("[6] Assign to height a new MyInteger and an integer other than width's value");

    height = new MyInteger();
    height.value = 1;

    System.out.println("->  height is " + height.value + " and width is " + width.value + ", we are different things again! \n");
  }
}

これは私の実行の出力です:

height と width という名前の 2 つの MyInteger オブジェクトを使用した代入演算子の評価

[1] 高さと幅の値に個別の整数を割り当てる
-> 高さが 9 で幅が 1 で、私たちは別物です!

[2] 高さの値に幅の値を割り当てます
-> 高さが 1 で幅が 1 ですが、同じものですか?

[3] 高さの値に幅の値以外の整数を代入する
-> 高さが 9 で幅が 1 です。

[4] width オブジェクトを height に代入
-> 高さが 1 で幅が 1 ですが、同じものですか?

[5] 高さの値に幅の値以外の整数を代入する
-> 高さが 9 で幅が 9 で、同じものになりました。

[6] 新しい MyInteger と width の値以外の整数を height に割り当てます
-> 高さが 1 で幅が 9 です。また別物です。

[2]では、個別のオブジェクトがあり、1 つの変数の値を別の変数に割り当てます。しかし、[3]で新しい値を割り当てた後、オブジェクトには異なる値がありました。つまり、[2]では、割り当てられた値は通常pass-by-valueと呼ばれるプリミティブ変数のコピーでした。それ以外の場合は、[3]に出力された値です。 ]は同じである必要があります。

[4]では、まだ個別のオブジェクトがあり、あるオブジェクトを別のオブジェクトに割り当てています。[5]で新しい値を割り当てた後、オブジェクトは同じ値を持っていました。つまり、[4]では、割り当てられたオブジェクトは他のオブジェクトのコピーではなく、これはpass-by-referenceと呼ばれる必要があります。しかし、[6]を注意深く見ると、コピーが行われていないことを確信することはできません... ?????

[6]ではオブジェクトが同じで、新しいオブジェクトをそれらの 1 つに割り当てた後、オブジェクトの値が異なるため、確信が持てません! それらが同じだった場合、それらはどのように区別できますか? ここでも同じはずです!???????

何が起こっているのかを理解するには、ドキュメントを覚えておく必要があります。

この演算子をオブジェクトで使用して、オブジェクト参照を割り当てることもできます

したがって、2 つの変数は参照を格納していました... 私たちの変数は[4]の後に同じ参照を持ち、 [6]の後に異なる参照を持ちました... そのようなことが可能である場合、これはオブジェクトの割り当てがオブジェクトのコピーによって行われることを意味しますそれ以外の場合、それが参照のコピーではない場合、[6]の変数の出力値は同じである必要があります。したがって、オブジェクト (参照) は、プリミティブと同様に、代入によって変数にコピーされます。これは、通常、値渡しと呼ばれます。これが Java での唯一のパスバイです。

于 2018-02-02T21:23:56.807 に答える
21

Java は値によって参照をコピーします。したがって、それを別のものに変更した場合 (たとえば、 を使用new)、参照はメソッドの外では変更されません。ネイティブ型の場合、常に値渡しです。

于 2009-12-26T21:05:11.310 に答える
21

それは本当に非常に単純です:

プリミティブ型の変数 (例: intbooleancharなど) の場合、その名前をメソッド引数に使用すると、それに含まれる値 ( 5true、または'c') が渡されます。この値は「コピー」され、変数はメソッドの呼び出し後もその値を保持します。

参照型の変数 (例: StringObjectなど) の場合、メソッドの引数にその名前を使用すると、それに含まれる値 (オブジェクトを「指す」参照値) が渡されます。この参照値は「コピー」され、変数はメソッド呼び出し後もその値を保持します。参照変数は、同じオブジェクトを「ポイント」し続けます。

いずれにせよ、あなたは常に値渡しをしています。


これを比較して、 を受け取るメソッドを持つことができる C++int&や、 を受け取ることができる C#と比較してくださいref int(ただし、この場合、ref変数の名前をメソッドに渡すときに修飾子も使用する必要があります)。

于 2012-08-01T17:31:26.280 に答える
20

すべての回答を通して、@ Gevorgが書いたように、Javaの値渡し、または「変数値のコピー渡し」がわかり、これは常に心に留めておくべき考えです。

アイデアを理解するのに役立つ例に焦点を当てていますが、これは以前の回答の補遺です。

[1] より Java では、常に引数をコピーで渡します。つまり、常に関数内で値の新しいインスタンスを作成しています。しかし、参照渡しをしていると思わせる特定の動作があります。

  • コピーによる受け渡し: 変数がメソッド/関数に渡されると、コピーが作成されます (プリミティブを渡すと、コピーを作成していると聞くことがあります)。

  • 参照渡し: 変数がメソッド/関数に渡されると、メソッド/関数内のコードは元の変数で動作します (まだコピー渡しですが、複雑なオブジェクト内の値への参照は両方のバージョンの一部です。関数内のオリジナルとバージョンの両方の変数. 複雑なオブジェクト自体はコピーされますが、内部参照は保持されます)

コピー渡し/値渡しの例

[ref 1] の例

void incrementValue(int inFunction){
  inFunction ++;
  System.out.println("In function: " + inFunction);
}

int original = 10;
System.out.print("Original before: " + original);
incrementValue(original);
System.out.println("Original after: " + original);

We see in the console:
 > Original before: 10
 > In Function: 11
 > Original after: 10 (NO CHANGE)

[ref 2] の例

メカニズム ウォッチ最大5分をうまく表示

(参照渡し) 変数値のコピー渡し

[ref 1] の例 (配列はオブジェクトであることを思い出してください)

void incrementValu(int[] inFuncion){
  inFunction[0]++;
  System.out.println("In Function: " + inFunction[0]);
}

int[] arOriginal = {10, 20, 30};
System.out.println("Original before: " + arOriginal[0]);
incrementValue(arOriginal[]);
System.out.println("Original before: " + arOriginal[0]);

We see in the console:
  >Original before: 10
  >In Function: 11
  >Original before: 11 (CHANGE)

複合オブジェクト自体はコピーされていますが、内部参照は保持されています。

[ref 3] の例

package com.pritesh.programs;

class Rectangle {
  int length;
  int width;

  Rectangle(int l, int b) {
    length = l;
    width = b;
  }

  void area(Rectangle r1) {
    int areaOfRectangle = r1.length * r1.width;
    System.out.println("Area of Rectangle : " 
                            + areaOfRectangle);
  }
}

class RectangleDemo {
  public static void main(String args[]) {
    Rectangle r1 = new Rectangle(10, 20);
    r1.area(r1);
  }
}

長方形の面積は 200 で、長さ = 10、幅 = 20 です。

最後に共有したいのは、講義のこの瞬間でした: メモリ割り当て は、Java の値渡し、または @Gevorg が書いた「変数値のコピー渡し」を理解するのに非常に役立ちました。 .

  1. REF 1 Lynda.com
  2. REF 2 メヘラン・サハミ教授
  3. REF 3 c4learn
于 2014-09-25T00:23:38.403 に答える
18

Javaは、参照のコピーが渡される定数参照によって渡されます。これは、基本的に値渡しであることを意味します。クラスが可変の場合、参照の内容を変更できますが、参照自体を変更することはできません。つまり、アドレスは値渡しなので変更できませんが、アドレスが指す内容は変更できます。不変クラスの場合、参照の内容も変更できません。

于 2012-12-31T19:35:46.853 に答える
18

非常に多くの長い答え。簡単なものを挙げましょう:

  • Java は常にすべてを値で渡します
  • つまり、参照も値渡しされます

つまり、渡されたパラメーターの値を変更することはできませんが、メソッドを呼び出したり、渡されたオブジェクト参照の属性を変更したりすることはできます。

于 2016-03-16T15:29:29.443 に答える
15

主要な基礎知識は、引用されたものでなければなりません。

オブジェクト参照がメソッドに渡されるとき、参照自体は値渡しを使用して渡されます。ただし、渡される値はオブジェクトを参照するため、その値のコピーは、対応する引数によって参照される同じオブジェクトを引き続き参照します。

Java: 初心者向けガイド、第 6 版、ハーバート シルト

于 2016-11-14T16:51:48.843 に答える
13

このコードを見てください。このコードはスローしませんNullPointerException...「Vinay」と出力します

public class Main {
    public static void main(String[] args) {
        String temp = "Vinay";
        print(temp);
        System.err.println(temp);
    }

    private static void print(String temp) {
        temp = null;
    }
}

NullPointerExceptionJava が参照渡しの場合、参照が Null に設定されているため、Java はスローされるはずです。

于 2010-08-09T12:21:58.100 に答える
11

言語が参照渡しをサポートしているかどうかを確認する簡単なテストは、単に従来のスワップを記述することです。従来の swap(a,b) メソッド/関数を Java で記述できますか?

従来の swap メソッドまたは関数は 2 つの引数を取り、関数に渡された変数が関数の外部で変更されるようにそれらを交換します。その基本構造は次のようになります

(Java 以外) 基本的な swap 関数の構造

swap(Type arg1, Type arg2) {
    Type temp = arg1;
    arg1 = arg2;
    arg2 = temp;
}

あなたの言語でそのようなメソッド/関数を書くことができるなら

Type var1 = ...;
Type var2 = ...;
swap(var1,var2);

実際に変数 var1 と var2 の値を切り替えると、言語は参照渡しをサポートします。しかし、Java では、ポインターや参照ではなく、値の受け渡しのみがサポートされているため、このようなことは許可されていません。

于 2016-09-29T09:53:44.237 に答える
10

この問題をめぐる混乱の多くは、Java が「値渡し」と「参照渡し」の意味を再定義しようとしたという事実から生じています。これらは業界用語であり、その文脈以外では正しく理解できないことを理解することが重要です。これらはコーディングを支援するためのものであり、理解する価値があるため、最初にその意味を確認しましょう。

両方の詳細については、こちらを参照してください。

値渡し関数が受け取る値は、呼び出し元が使用しているオブジェクトのコピーです。それは関数に完全に固有であり、そのオブジェクトに対して行うことはすべて関数内でのみ表示されます。

参照渡し関数が受け取る値は、呼び出し元が使用しているオブジェクトへの参照です。値が参照するオブジェクトに対して関数が行うことはすべて、呼び出し元に表示され、その時点からそれらの変更を処理します。

これらの定義から明らかなように、参照が値渡しであるという事実は無関係です。その定義を受け入れると、これらの用語は無意味になり、すべての言語は値渡しのみになります。

どのように参照を渡しても、値でのみ渡すことができます。それはポイントではありません。ポイントは、オブジェクトのコピーではなく、独自のオブジェクトへの参照を関数に渡したということです。受け取ったリファレンスを破棄できるという事実は関係ありません。繰り返しになりますが、その定義を受け入れると、これらの用語は無意味になり、誰もが常に値渡しを行っています。

いいえ、C++ の特別な「参照渡し」構文は、参照渡しの排他的な定義ではありません。これは、ポインターを渡した後にポインター構文を使用する必要がないようにすることを目的とした、純粋に便利な構文です。これはまだポインターを渡しているため、コンパイラーはその事実を隠しているだけです。また、そのポインターを BY VALUE で渡します。コンパイラーはそれをあなたから隠しているだけです。

したがって、この理解があれば、Java を見て、実際に両方があることがわかります。呼び出し元のオブジェクトのコピーを受け取り、それらのコピーを変更できないため、すべての Java プリミティブ型は常に値渡しです。呼び出し元のオブジェクトへの参照を受け取り、それらのオブジェクトを直接変更できるため、すべての Java 参照型は常に参照渡しです。

呼び出し元の参照を変更できないという事実は、参照渡しとは関係なく、参照渡しをサポートするすべての言語に当てはまります。

于 2016-05-12T14:36:25.857 に答える
10

少しわかりにくいですが、Java は常に値をコピーします。ポイントは、通常、値は参照です。したがって、何も考えずに同じオブジェクトになってしまいます...

于 2009-01-12T20:34:02.980 に答える
10

データがどのように作成され、渡されるかを示すこの小さな図を作成しました

データの作成と受け渡しの図

注: プリミティブ値は値として渡され、その値への最初の参照はメソッドの引数です

つまり、次のことを意味します。

  • 関数myObject の値を変更できます
  • myObjectただし、関数内で参照先を変更することはできませpointん。myObject
  • pointとはどちらmyObject参照であり、異なる参照ですが、これらの参照は同じものを指していることを覚えておいてくださいnew Point(0,0)
于 2016-04-25T14:32:28.917 に答える
9

すべて値渡しです。プリミティブとオブジェクト参照。ただし、インターフェイスで許可されている場合は、オブジェクトを変更できます。

オブジェクトをメソッドに渡すと、参照が渡され、メソッドの実装によってオブジェクトを変更できます。

void bithday(Person p) {
    p.age++;
}

オブジェクト自体の参照は、値によって渡されます。パラメーターを再割り当てできますが、変更は反映されません。

void renameToJon(Person p) { 
    p = new Person("Jon"); // this will not work
}

jack = new Person("Jack");
renameToJon(jack);
sysout(jack); // jack is unchanged

事実上、「p」は参照 (オブジェクトへのポインター) であり、変更することはできません。

プリミティブ型は値渡しです。オブジェクトの参照もプリミティブ型と見なすことができます。

要約すると、すべてが値渡しされます。

于 2012-04-19T20:25:21.140 に答える
8

簡単なプログラム

import java.io.*;
class Aclass
{
    public int a;
}
public class test
{
    public static void foo_obj(Aclass obj)
    {
        obj.a=5;
    }
    public static void foo_int(int a)
    {
        a=3;
    }
    public static void main(String args[])
    {
        //test passing an object
        Aclass ob = new Aclass();
        ob.a=0;
        foo_obj(ob);
        System.out.println(ob.a);//prints 5

        //test passing an integer
        int i=0;
        foo_int(i);
        System.out.println(i);//prints 0
    }
}

C/C++ プログラマーの観点から見ると、java は値渡しを使用するため、プリミティブ データ型 (int、char など) の場合、関数の変更は呼び出し元の関数に反映されません。ただし、オブジェクトを渡し、関数内でそのデータ メンバーを変更するか、オブジェクトの状態を変更できるメンバー関数を呼び出すと、呼び出し元の関数が変更を取得します。

于 2014-12-22T18:48:19.010 に答える
6

参照用の Java の回避策があります。この例で説明しましょう:

public class Yo {
public static void foo(int x){
    System.out.println(x); //out 2
    x = x+2;
    System.out.println(x); // out 4
}
public static void foo(int[] x){
    System.out.println(x[0]); //1
    x[0] = x[0]+2;
    System.out.println(x[0]); //3
}
public static void main(String[] args) {
    int t = 2;
    foo(t);
    System.out.println(t); // out 2 (t did not change in foo)

    int[] tab = new int[]{1};
    foo(tab);
    System.out.println(tab[0]); // out 3 (tab[0] did change in foo)
}}

これが役立つことを願っています!

于 2014-10-06T04:34:29.353 に答える
6

Java は参照によってオブジェクトを操作し、すべてのオブジェクト変数は参照です。ただし、Java は参照によってメソッド引数を渡しません。それらを値で渡します。

たとえば、badSwap() メソッドを見てみましょう。

    public void badSwap(int var1, int
 var2{ int temp = var1; var1 = var2; var2 =
 temp; }

badSwap() が戻ったとき、引数として渡された変数は元の値を保持しています。引数の型を int から Object に変更した場合も、メソッドは失敗します。これは、Java がオブジェクト参照も値で渡すためです。さて、ここがトリッキーになるところです:

public void tricky(Point arg1, Point   arg2)
{ arg1.x = 100; arg1.y = 100; Point temp = arg1; arg1 = arg2; arg2 = temp; }
public static void main(String [] args) { 

 Point pnt1 = new Point(0,0); Point pnt2
 = new Point(0,0); System.out.println("X:
 " + pnt1.x + " Y: " +pnt1.y);

     System.out.println("X: " + pnt2.x + " Y:
 " +pnt2.y); System.out.println(" ");

     tricky(pnt1,pnt2);
 System.out.println("X: " + pnt1.x + " Y:" + pnt1.y);

     System.out.println("X: " + pnt2.x + " Y: " +pnt2.y); }

この main() メソッドを実行すると、次の出力が表示されます。

X: 0 Y: 0 X: 0 Y: 0 X: 100 Y: 100 X: 0 Y: 0

このメソッドは、pnt1 の値が値渡しされているにもかかわらず、値を正常に変更します。ただし、pnt1 と pnt2 のスワップは失敗します。これが混乱の主な原因です。main() メソッドでは、pnt1 と pnt2 はオブジェクト参照にすぎません。pnt1 と pnt2 をトリッキー() メソッドに渡すと、Java は他のパラメーターと同様に参照を値で渡します。これは、メソッドに渡される参照が実際には元の参照のコピーであることを意味します。以下の図 1 は、Java がオブジェクトをメソッドに渡した後、同じオブジェクトを指す 2 つの参照を示しています。

Java は、オブジェクトではなく値によって参照をコピーして渡します。したがって、参照が元のオブジェクトを指しているため、メソッド操作によってオブジェクトが変更されます。ただし、参照はコピーであるため、スワップは失敗します。図 2 が示すように、メソッドはスワップを参照しますが、元の参照は参照しません。残念ながら、メソッドを呼び出した後は、スワップされていない元の参照のみが残ります。メソッド呼び出しの外でスワップを成功させるには、コピーではなく元の参照をスワップする必要があります。

于 2016-09-26T17:55:19.900 に答える
5

私の意見では、「値渡し」は、2つの類似しているが異なるイベントを単独で説明するためのひどい方法です。彼らが最初に私に尋ねるべきだったと思います。

プリミティブでは、プリミティブの実際の値をメソッド(またはコンストラクター)に渡します。整数「5」、文字「c」、またはあなたが持っているものです。その実際の値は、それ自体のローカルプリミティブになります。しかし、オブジェクトの場合、同じオブジェクトに追加の参照(ローカル参照)を与えるだけなので、同じオブジェクトを指す2つの参照ができます。

この簡単な説明がお役に立てば幸いです。

于 2011-06-24T00:13:17.690 に答える
5

次のプログラムで理解しようとしたため、Javaではすべてが値によって呼び出されているようです

クラスS

class S{
String name="alam";
public void setName(String n){
this.name=n; 
}}

クラスサンプル

    public class Sample{
    public static void main(String args[]){
    S s=new S();
    S t=new S();
    System.out.println(s.name);
    System.out.println(t.name);
    t.setName("taleev");
    System.out.println(t.name);
    System.out.println(s.name);
    s.setName("Harry");
    System.out.println(t.name);
    System.out.println(s.name);
    }}

出力

アラム

アラム

タレフ

アラム

タレフ

ハリー

値taleevを持つインスタンス変数名でクラス S を定義しているため、それから初期化するすべてのオブジェクトに対して、taleevの値を持つ名前変数が含まれますが、任意のオブジェクトの名前の値を変更すると、名前のみが変更されます。そのクラス(オブジェクト)のコピーはすべてのクラス用ではないため、その後もSystem.out.println(s.name)を実行すると、taleevのみが出力されます。最初に定義した名前の値を変更することはできません。変更しているのはインスタンス変数の値ではなくオブジェクトの値であるため、インスタンス変数を定義すると、それを変更することはできません

だから、Javaが参照ではなくのみを扱っていることを示していると思います

プリミティブ変数のメモリ割り当ては、これで理解できます

于 2016-11-16T11:58:41.320 に答える
5

Javaはすべてを値で渡します!!

// 名前と年齢を渡してオブジェクトを作成します。

PersonClass variable1 = new PersonClass("Mary", 32);

PersonClass variable2;

// variable2 と variable1 の両方が同じオブジェクトを参照するようになりました

variable2 = variable1; 


PersonClass variable3 = new PersonClass("Andre", 45);

// variable1 が variable3 を指すようになりました

variable1 = variable3;

//これによる出力は何ですか?

System.out.println(variable2);
System.out.println(variable1);

Mary 32
Andre 45

この例を理解できれば完了です。それ以外の場合は、この Web ページにアクセスして詳細な説明を参照してください。

ウェブページ

于 2015-02-26T18:44:32.547 に答える
5

値渡しの要点: 呼び出されたメソッドは呼び出し元の変数を変更できませんが、オブジェクト参照変数の場合、呼び出されたメソッドは変数が参照するオブジェクトを変更できます。変数の変更とオブジェクトの変更の違いは何ですか? オブジェクト参照の場合、呼び出されたメソッドが呼び出し元の元の参照変数を再割り当てして、別のオブジェクトまたは null を参照できないことを意味します。

このコードと説明は、Java 認定に関する本から引用し、いくつかのマイナーな変更を加えました。
オブジェクトの値渡しの良い例だと思います。以下のコードでは、g を再割り当てしても f! は再割り当てされません。bar() メソッドの最後で、2 つの Foo オブジェクトが作成されています。1 つはローカル変数 f によって参照され、もう 1 つはローカル (引数) 変数 g によって参照されます。

doStuff() メソッドには参照変数のコピーがあるため、たとえば setName() メソッドを呼び出すなどして、元の Foo オブジェクトにアクセスする方法があります。ただし、doStuff() メソッドには f 参照変数を取得する方法がありません。そのため、doStuff() は f が参照するオブジェクト内の値を変更できますが、doStuff() は f の実際の内容 (ビット パターン) を変更できません。つまり、doStuff() は、f が参照するオブジェクトの状態を変更できますが、f に別のオブジェクトを参照させることはできません!

package test.abc;

public class TestObject {

    /**
     * @param args
     */
    public static void main(String[] args) {
        bar();
    }

    static void bar() {
        Foo f = new Foo();
        System.out.println("Object reference for f: " + f);
        f.setName("James");
        doStuff(f);
        System.out.println(f.getName());
        //Can change the state of an object variable in f, but can't change the object reference for f.
        //You still have 2 foo objects.
        System.out.println("Object reference for f: " + f);
        }

    static void doStuff(Foo g) {
            g.setName("Boo");
            g = new Foo();
            System.out.println("Object reference for g: " + g);
        }
}


package test.abc;

public class Foo {
    public String name = "";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

以下のコンソール出力では、オブジェクト参照が変更されていないことに注意してください。

コンソール出力:

f のオブジェクト参照: test.abc.Foo@62f72617

g のオブジェクト参照: test.abc.Foo@4fe5e2c3

f の Boo オブジェクト参照: test.abc.Foo@62f72617

于 2013-09-04T20:22:41.393 に答える
5

簡単さと冗長性のために そのpass reference by value

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    Dog oldDog = aDog;

    // we pass the object to foo
    foo(aDog);
    // aDog variable is still pointing to the "Max" dog when foo(...) returns
    aDog.getName().equals("Max"); // true
    aDog.getName().equals("Fifi"); // false
    aDog == oldDog; // true
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // change d inside of foo() to point to a new Dog instance "Fifi"
    d = new Dog("Fifi");
    d.getName().equals("Fifi"); // true
}
于 2021-04-28T06:17:51.760 に答える
4

より正確な定義は次のとおりです。

  • 値による受け渡し/呼び出し:仮パラメーターは、関数のスコープ内のローカル変数のようなもので、関数呼び出しの時点で実際のパラメーターに評価されます。
  • 参照による受け渡し/呼び出し:仮パラメーターは実際の値の単なるエイリアスです。関数のスコープ内でそれを変更すると、コードの他の部分の外側に副作用が生じる可能性があります。

したがって、C/C++ では、参照を使用して渡された 2 つの値を交換する関数を作成できます。

void swap(int& a, int& b) 
{
    int tmp = a; 
    a = b; 
    b = tmp; 
}

a と b への一意の参照があることがわかります。したがって、コピーはなく、tmp は一意の参照を保持しているだけです。

Java の同じ関数には副作用がありません。パラメーターの受け渡しは、参照のない上記のコードとまったく同じです。

Java はポインター/参照で動作しますが、パラメーターは一意のポインターではなく、各属性でコピーされ、代わりに C/C++ のように割り当てられます。

于 2021-03-08T02:24:37.567 に答える