224

配列はJavaのプリミティブ型ではありませんが、オブジェクトでもないので、値または参照によって渡されますか? 参照やプリミティブ型など、配列に含まれるものに依存しますか?

4

7 に答える 7

293

Java ではすべて値渡しです。配列 (オブジェクト以外の何物でもない) の場合、配列参照は値によって渡されます (オブジェクト参照が値によって渡されるのと同じように)。

配列を他のメソッドに渡すと、実際にはその配列への参照がコピーされます。

  • その参照による配列の内容の変更は、元の配列に影響します。
  • ただし、新しい配列を指すように参照を変更しても、元のメソッドの既存の参照は変更されません。

この投稿を参照してください: Java "pass-by-reference" or "pass-by-value"?

次の実際の例を参照してください。

public static void changeContent(int[] arr) {

   // If we change the content of arr.
   arr[0] = 10;  // Will change the content of array in main()
}

public static void changeRef(int[] arr) {
   // If we change the reference
   arr = new int[2];  // Will not change the array in main()
   arr[0] = 15;
}

public static void main(String[] args) {
    int [] arr = new int[2];
    arr[0] = 4;
    arr[1] = 5;

    changeContent(arr);

    System.out.println(arr[0]);  // Will print 10.. 
  
    changeRef(arr);

    System.out.println(arr[0]);  // Will still print 10.. 
                                 // Change the reference doesn't reflect change here..
}
于 2012-10-06T07:47:07.800 に答える
168

あなたの質問は誤った前提に基づいています。

配列はJavaのプリミティブ型ではありませんが、オブジェクトでもありません...」

実際、Java のすべての配列オブジェクトです1。すべての Java 配列型はそのスーパータイプとして を持ち、 APIjava.lang.Objectのすべてのメソッドの実装を継承します。Object

...では、それらは値渡しですか、それとも参照渡しですか? 参照やプリミティブ型など、配列に含まれるものに依存しますか?

簡単な答え: 1) 値渡し、2) 違いはありません。

より長い答え:

すべての Java オブジェクトと同様に、配列は値で渡されますが、値は配列への参照です。したがって、呼び出されたメソッドで配列のセルに何かを割り当てると、呼び出し元が見るのと同じ配列オブジェクトに割り当てられます。

これは参照渡しではありません。 実際の参照渡しでは、変数のアドレスを渡します。実際の参照渡しでは、呼び出されたメソッドがそのローカル変数に割り当てることができ、これにより呼び出し元の変数が更新されます 。

しかし、Java ではありません。Java では、呼び出されたメソッドは配列の内容を更新でき、配列参照のコピーを更新できますが、呼び出し元の配列参照を保持する呼び出し元の変数を更新することはできません。したがって... Javaが提供しているのは参照渡しではありません。

参照渡しと値渡しの違いを説明するリンクを次に示します。上記の説明が理解できない場合、または用語に同意できない場合は、それら読んでください。

関連する SO の質問:

歴史的背景:

「参照渡し」という語句は、もともとは「参照渡し」であり、FORTRAN (参照渡し) の引数渡しのセマンティクスと ALGOL-60 (値渡し) のセマンティクスを区別するために使用されました。および名前による呼び出し)。

  • 値渡しでは、引数式が値に評価され、その値が呼び出されたメソッドにコピーされます。

  • 参照渡しでは、引数式は、呼び出し元のメソッドに渡される「左辺値」(つまり、変数または配列要素のアドレス) に部分的に評価されます。呼び出し元のメソッドは、変数 / 要素を直接読み取って更新できます。

  • 名前による呼び出しでは、実引数の式が呼び出し元のメソッドに渡され (!!)、複数回評価できます (!!!)。これは実装が複雑で、非常に理解しにくいコードを書くために使用 (悪用) される可能性がありました。名前による呼び出しは、これまで Algol-60 でのみ使用されていました (ありがたいことに!)。

アップデート

実際、Algol-60 の名前による呼び出しは、ラムダ式をパラメーターとして渡すことに似ています。問題は、これらの正確ではないラムダ式 (実装レベルでは「サンク」と呼ばれていました) が、呼び出し元のプロシージャ/関数のスコープ内にある変数の状態を間接的に変更できることです。それが彼らを理解するのを非常に困難にした理由の一部です。(たとえば、ジェンセンのデバイスに関するウィキペディアのページを参照してください。)


1. リンクされた Q&A ( Java の配列とそれらがメモリに格納される方法) には、配列がオブジェクトではないことを述べたり暗示したりするものはありません。

于 2012-10-06T07:47:44.567 に答える
64

配列は実際にはオブジェクトであるため、参照が渡されます (参照自体は値によって渡されますが、まだ混乱していますか?)。簡単な例:

// assuming you allocated the list
public void addItem(Integer[] list, int item) {
    list[1] = item;
}

呼び出し元のコードからのリストへの変更が表示されます。ただし、値で渡されるため、参照自体を変更することはできません。

// assuming you allocated the list
public void changeArray(Integer[] list) {
    list = null;
}

null 以外のリストを渡すと、メソッドが戻るまでに null にはなりません。

于 2012-10-06T07:46:17.927 に答える
13

いいえ、それは間違っています。配列は Java の特別なオブジェクトです。したがって、参照自体ではなく、参照の値を渡す場所に他のオブジェクトを渡すようなものです。つまり、呼び出されたルーチンで配列の参照を変更しても、呼び出したルーチンには反映されません。

于 2012-10-06T07:47:38.673 に答える
4

Java ではすべて値渡しです。

配列の場合、参照は新しい参照にコピーされますが、Java ではすべてが値によって渡されることに注意してください。

詳細については、この興味深い記事をご覧ください...

于 2012-10-06T07:49:22.820 に答える
3

配列の決定的な議論はhttp://docs.oracle.com/javase/specs/jls/se5.0/html/arrays.html#27803にあります。これにより、Java 配列がオブジェクトであることが明確になります。これらのオブジェクトのクラスは 10.8 で定義されています。

言語仕様のセクション 8.4.1 ( http://docs.oracle.com/javase/specs/jls/se5.0/html/classes.html#40420 ) では、メソッドに引数を渡す方法について説明しています。Java 構文は C と C++ から派生しているため、動作は似ています。プリミティブ型は、C と同様に値で渡されます。オブジェクトが渡されると、オブジェクト参照 (ポインター) が値で渡され、値でポインターを渡す C 構文が反映されます。4.3.1、http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.3を参照してください。

実際には、メソッド内の配列の内容を変更すると、呼び出しスコープ内の配列オブジェクトに反映されますが、メソッド内の参照に新しい値を再割り当てしても、呼び出しスコープ内の参照には影響しません。これは、C の構造体または C++ のオブジェクトへのポインターに期待される動作とまったく同じです。

用語の混乱の少なくとも一部は、C が一般的に使用される前の高水準言語の歴史に起因しています。以前の一般的な高水準言語では、アドレスによってメモリを直接参照することは、可能な限り避けるべきものでした。抽象化の層を提供することが言語の仕事と考えられていました。これにより、言語はサブルーチン (必ずしも関数ではない) から値を返すメカニズムを明示的にサポートする必要がありました。このメカニズムは、「参照渡し」を参照するときに正式に意味されるものです。

C が導入されたとき、すべての引数が入力専用であり、呼び出し元に返される唯一の値が関数の結果であるという、プロシージャ呼び出しの簡略化された概念が付属していました。ただし、参照を渡す目的は、ポインタを明示的かつ広範に使用することで達成できます。同じ目的を果たすため、ポインターを値への参照として渡す方法は、口語的に参照渡しと呼ばれることがよくあります。参照によって渡されるパラメーターのルーチン呼び出しのセマンティクスの場合、 C の構文では、プログラマーがポインターを明示的に渡す必要があります。値によるポインターの受け渡しは、C で参照による受け渡しセマンティクスを実装するための設計パターンです。

C における raw ポインターの唯一の目的は、クラッシュするバグを作成することであると思われることが多いため、その後の開発、特に Java では、パラメーターを渡すためのより安全な手段に戻ろうとしました。しかし、C が優勢であるため、開発者は慣れ親しんだ C コーディング スタイルを模倣する必要がありました。その結果、ポインターと同様に渡される参照になりますが、より安全にするためにより多くの保護が実装されています。別の方法としては、Ada のような言語の豊富な構文を使用することもできましたが、これは歓迎されない学習曲線のように見え、Java の採用の可能性を減らしていました。

要するに、Java における配列を含むオブジェクトのパラメータ受け渡しの設計は、本質的に参照渡しのセマンティックな意図を果たすことですが、参照を値渡しする構文で実装されています。

于 2012-10-06T08:51:52.860 に答える
0

ある種のトリックの現実...Javaでは参照でさえ値によって渡されるため、呼び出された関数レベルでスコープが設定されている参照自体が変更されます。コンパイラや JVM は、多くの場合、値の型を参照に変換します。

于 2012-10-06T07:48:03.267 に答える