ホームページ >Java >&#&はじめる >Javaの値渡しと参照渡しの違いは何ですか

Javaの値渡しと参照渡しの違いは何ですか

青灯夜游
青灯夜游オリジナル
2023-01-04 11:50:005955ブラウズ

相違点: 1. 値転送ではコピーが作成されますが、参照転送ではコピーが作成されません。 2. 値転送中に関数内で元のオブジェクトを変更することはできませんが、関数内で元のオブジェクトを変更できます。リファレンス転送中。値による受け渡しとは、関数呼び出し時に実際のパラメーターのコピーが関数に渡されることを意味し、関数内でパラメーターが変更されても実際のパラメーターは影響を受けません。参照による受け渡しとは、実際のパラメーターがコピーされることを意味します。関数を呼び出すとき、アドレスは関数に直接渡されるため、関数内のパラメーターを変更すると実際のパラメーターに影響します。

Javaの値渡しと参照渡しの違いは何ですか

#このチュートリアルの動作環境: Windows7 システム、Java8 バージョン、DELL G3 コンピューター。

実際のパラメータと形式的なパラメータ

Java でメソッドを定義するときにパラメータを定義できることは誰もが知っています。たとえば、Java の main メソッド、public static void main(String[ ] args) ですが、ここでの args はパラメータです。プログラミング言語ではパラメータは仮パラメータと実パラメータに分けられます。

  • 仮パラメータ: 関数名と関数本体を定義するときに使用されるパラメータで、関数の呼び出し時に渡されるパラメータを受け取ることが目的です。

  • 実際のパラメータ: パラメータ化された関数を呼び出すとき、呼び出し元の関数と呼び出された関数の間にはデータ転送関係が存在します。呼び出し関数内で関数を呼び出す場合、関数名の後の括弧内のパラメータを「実パラメータ」と呼びます。

簡単な例:

public static void main( String[ ] args) {
ParamTest pt = new ParamTest();
pt.sout( "Hollis");//实际参数为Hollis
}
public void sout( String name) {/!形式参数为name
system.out.println(name);
}

実際のパラメータは次のとおりです。仮引数とは、引数付きのメソッドを呼び出すときに実際に渡される内容であり、実引数の内容を受け取るために使用される引数です。

値の受け渡しと参照の受け渡し

前述したように、パラメーター化された関数を呼び出すと、実際のパラメーターが仮パラメーターに渡されます。ただし、プログラミング言語では、この転送処理には、値による転送と参照による転送の 2 つのケースがあります。プログラミング言語で値渡しと参照渡しがどのように定義され、区別されるかを見てみましょう。

値の受け渡しとは、関数を呼び出すときに実際のパラメーターのコピーを関数にコピーすることを指します。これにより、関数内でパラメーターが変更されても、実際のパラメーターは影響を受けません。
参照渡しとは、関数呼び出し時に実パラメータのアドレスを直接関数に渡すことを指し、関数内でパラメータを変更すると実パラメータに影響を与えます。

上記の概念を使用して、コードを作成して練習することができます。Java での値の受け渡しなのか参照の受け渡しなのかを見てみましょう。したがって、最も単純なコードが完成しました:

public static void main( String[] args) {
     ParamTest pt = new ParamTest();
    int i = 10;
    pt.pass(i);
    System.out.println( "print in main , i is " +i);
}
public void pass(int j){
    j = 20;
    system.out.println( "print in pass , j is " + j);
}

上記のコードでは、パス メソッドのパラメーター j の値を変更し、そのパラメーターの値をパス メソッドとメイン メソッドにそれぞれ出力します。出力結果は次のとおりです。

print in pass , j is 20
print in main , i is 10

pass メソッド内で i の値を変更しても、実際のパラメータ i の値は変更されないことがわかります。したがって、上記の定義に従って、誰かが「Java のメソッド受け渡しは値受け渡しである」という結論に達しました。

しかし、すぐに疑問を呈する人も出てきました(笑、だから安易に結論を出さないでください)。次に、次のコードを移動します。

public static void main(String[ ] args) {
    ParamTest pt = new ParamTest();
    User hollis = new User();
    hollis.setName( "Hollis");
    hollis.setGender("Male");
    pt.pass(hollis);
    system.out.println( "print in main , user is " + hollis);}public void pass(User user) {
    user.setName( "hollischuang");
    System.out.println( "print in pass , user is " + user);}
もパス メソッドであり、パラメータの値もパス メソッド内で変更されます。出力結果は以下のようになります:

print in pass , user is User{name='hollischuang', gender='Male '}
print in main , user is User{name='hollischuang' , gender='Male '}

passメソッド実行後、実パラメータの値が変更されます 上記の参照渡しの定義に従い、実パラメータの値が変更されます。これは参照渡しと呼ばれるものではないでしょうか?したがって、上記の 2 つのコードに基づいて、誰かが新しい結論に達しました。Java メソッドでは、通常の型を渡す場合は値によって渡され、オブジェクト型を渡す場合は参照によって渡されます。

ただし、この記述は依然として間違っています。私の言うことが信じられない場合は、パラメーターの型がオブジェクトである次のパラメーター転送を見てください。

public static void main( string[] args) {
    ParamTest pt = new ParamTest();
    string name = "Hollis";
    pt.pass(name ) ;
    System.out.println( "print in main , name is " + name);
}
public void pass(string name) {
    name = "hollischuang";
    system.out.println( "print in pass , name is " + name);
}

上記のコードの出力結果は

print in pass , name is hollischuangprint in main , name is Hollis

です。これはどういう説明ですか?オブジェクトも渡されますが、元のパラメータは The value has not been modified. 転送されたオブジェクトが再び値転送になっている可能性はありますか?

Java での値転送

上記では、結果が異なることを示すために 3 つの例を挙げました。これが、多くの初心者だけでなく、多くの上級プログラマさえも Java の転送タイプについて混乱している理由です。実は、私が言いたいのは、上記の考え方は間違っていないのですが、コード例には問題があるということです。さあ、このコンセプトの重要なポイントを概説してから、本当に適切な例をいくつか挙げてみましょう。

値の受け渡しとは、関数を呼び出すときに実際のパラメーターのコピーを関数にコピーすることを指します。これにより、関数内でパラメーターが変更されても、実際のパラメーターは影響を受けません。
参照渡しとは、関数呼び出し時に実パラメータのアドレスを直接関数に渡すことを指し、関数内でパラメータを変更すると実パラメータに影響を与えます。

それでは、値の受け渡しと参照の受け渡しの違いの重要なポイントをまとめてみましょう。

##根本的な違いコピーを作成しますコピーは作成しませんすべて元のオブジェクトを作成することはできません関数で変更されました元のオブジェクトは関数で変更できます

我们上面看过的几个pass的例子中,都只关注了实际参数内容是否有改变。如传递的是User对象,我们试着改变他的name属性的值,然后检查是否有改变。其实,在实验方法上就错了,当然得到的结论也就有问题了。

为什么说实验方法错了呢?这里我们来举一个形象的例子。再来深入理解一下值传递和引用传递,然后你就知道为啥错了。

你有一把钥匙,当你的朋友想要去你家的时候,如果你直接把你的钥匙给他了,这就是引用传递。这种情况下,如果他对这把钥匙做了什么事情,比如他在钥匙上刻下了自己名字,那么这把钥匙还给你的时候,你自己的钥匙上也会多出他刻的名字。

你有一把钥匙,当你的朋友想要去你家的时候,你复刻了一把新钥匙给他,自己的还在自己手里,这就是值传递。这种情况下,他对这把钥匙做什么都不会影响你手里的这把钥匙。

但是,不管上面那种情况,你的朋友拿着你给他的钥匙,进到你的家里,把你家的电视砸了。那你说你会不会受到影响?而我们在pass方法中,改变user对象的name属性的值的时候,不就是在“砸电视”么。

还拿上面的一个例子来举例,我们真正的改变参数,看看会发生什么?

public static void main(String[ ] args){
    ParamTest pt = new ParamTest();
    User hollis = new User();
    hollis.setName( "Hollis");
    hollis.setGender("Male" );
    pt.pass(hollis);
    system.out.println("print in main , user is " + hollis);
    public void pass(User user) {
        user = new User();
        user.setName( "hollischuang");
        user.setGender( "Male");
        system.out.println( "print in pass , user is " + user);

上面的代码中,我们在pass方法中,改变了user对象,输出结果如下:

print in pass , user is User{name='hollischuang ' , gender='Male '}
print in main , user is User{name='Hollis', gender= 'Male '}

我们来画一张图,看一下整个过程中发生了什么,然后我再告诉你,为啥Java中只有值传递。

Javaの値渡しと参照渡しの違いは何ですか

稍微解释下这张图,当我们在main中创建一个User对象的时候,在堆中开辟一块内存,其中保存了name和gender等数据。然后hollis持有该内存的地址ex123456(图1)。当尝试调用pass方法,并且hollis作为实际参数传递给形式参数user的时候,会把这个地址ex123456交给user,这时,user也指向了这个地址(图2)。然后在pass方法内对参数进行修改的时候,即user = newUser();,会重新开辟一块 eX456789的内存,赋值给user。后面对user的任何修改都不会改变内存eX123456的内容(图3)。

上面这种传递是什么传递?肯定不是引用传递,如果是引用传递的话,在user=new User()的时候,实际参数的引用也应该改为指向eX456789,但是实际上并没有。

通过概念我们也能知道,这里是把实际参数的引用的地址复制了一份,传递给了形式参数。所以,上面的参数其实是值传递,把实参对象引用的地址当做值传递给了形式参数。

我们再来回顾下之前的那个“砸电视”的例子,看那个例子中的传递过程发生了什么。

Javaの値渡しと参照渡しの違いは何ですか

同样的,在参数传递的过程中,实际参数的地址eX1213456被拷贝给了形参,只是,在这个方法中,并没有对形参本身进行修改,而是修改的形参持有的地址中存储的内容。

所以,值传递和引用传递的区别并不是传递的内容。而是实参到底有没有被复制一份给形参。在判断实参内容有没有受影响的时候,要看传的的是什么,如果你传递的是个地址,那么就看这个地址的变化会不会有影响,而不是看地址指向的对象的变化。就像钥匙和房子的关系。

那么,既然这样,为啥上面同样是传递对象,传递的String对象和User对象的表现结果不一样呢?我们在pass方法中使用name = “hollischuang”;试着去更改name的值,阴差阳错的直接改变了name的引用的地址。因为这段代码,会new一个String,在把引用交给name,即等价于name =new String(“hollischuang”);。而原来的那个”Hollis”字符串还是由实参持有着的,所以,并没有修改到实际参数的值。

Javaの値渡しと参照渡しの違いは何ですか

所以说,Java中其实还是值传递的,只不过对于对象参数,值的内容是对象的引用。

总结

无论是值传递还是引用传递,其实都是一种求值策略(Evaluation strategy)。在求值策略中,还有一种叫做按共享传递。其实Java中的参数传递严格意义上说应该是按共享传递。

共有による受け渡しとは、関数が呼び出されたときに、実パラメータのアドレスのコピーが関数に渡されることを意味します (実パラメータがスタック上にある場合、値は直接コピーされます)。関数内のパラメータを操作する場合は、アドレスをコピーして特定の値を見つけてから操作する必要があります。値がスタック上にある場合、それは値の直接コピーであるため、関数内のパラメーターの操作は外部変数に影響しません。元のコピーがヒープ内の元の値のアドレスである場合、操作を実行する前に、アドレスに基づいてヒープ内の対応する場所を見つける必要があります。アドレスのコピーが渡されるため、関数内の値に対する操作は外部変数から認識できます。

簡単に言うと、Java での転送は値によるもので、この値は実際にはオブジェクトへの参照です。
共有による受け渡しは、実際には値による受け渡しの特殊なケースにすぎません。したがって、Java での受け渡しは共有による受け渡し、または Java での受け渡しは値による受け渡しであると言えます。

したがって、関数内でパラメータを操作しても、外部変数には影響しません。元のコピーがヒープ内の元の値のアドレスである場合、操作を実行する前に、アドレスに基づいてヒープ内の対応する場所を見つける必要があります。アドレスのコピーが渡されるため、関数内の値に対する操作は外部変数から認識できます。

簡単に言うと、Java での転送は値によるもので、この値は実際にはオブジェクトへの参照です。

共有による受け渡しは、実際には値による受け渡しの特殊なケースにすぎません。したがって、Java での受け渡しは共有による受け渡し、または Java での受け渡しは値による受け渡しであると言えます。

プログラミング関連の知識について詳しくは、プログラミング教育をご覧ください。 !


値による受け渡し
参照による受け渡し

以上がJavaの値渡しと参照渡しの違いは何ですかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。