同僚を困惑させた:Javaメソッド呼び出しは値または参照によって渡されましたか

Javaメソッド呼び出しのパラメーターは、値または参照によって渡されますか?開発をしているすべての学生がこの問題に遭遇したと思います。Javaを実行している学生だけでなく、C#とPythonで開発している学生もこの問題に遭遇したに違いありません。また、複数回発生する可能性が非常に高いです。

それで、それは値によって渡されるのですか、それともJavaでは参照によって渡されるのですか?答えは値によってです。Javaでは参照による渡の概念はありません。

データ型とメモリ割り当て

Javaで要約できるデータ型には、基本型と参照型の2種類があります。

ベーシックタイプ

Byte、short、int、long、float、double、char、booleanは、Javaの8つの基本的な型です。基本的なタイプのメモリ割り当ては、JVMの仮想マシンスタックであるスタックで実行されます。つまり、次のステートメントを使用する場合:

int i = 89;

4バイトのスペースが、ストレージ用に仮想マシンスタックに割り当てられます。

参照型

参照型には、クラス、インターフェイス、配列、およびnullが含まれます。私たちが通常よく知っているさまざまなカスタムエンティティクラスは、このカテゴリに含まれます。

オブジェクトを定義し、newキーワードを使用してオブジェクトをインスタンス化する場合。

User user = new User();

次の3つの手順を実行します。

1.参照変数userを宣言し、仮想マシンスタックにスペースを割り当てます。

2. newキーワードを使用してオブジェクトインスタンスを作成し、ヒープにスペースを割り当てて属性情報をオブジェクトに格納します。

3.ヒープ上のオブジェクトをユーザー変数にリンクします。これにより、スタックに格納されるのは、実際にはヒープに格納されているオブジェクトのアドレス情報になります。

配列オブジェクトについても同じことが言えます。スタックには、ヒープに実際に割り当てられた配列スペースを指すアドレスのみが格納され、実際の値はヒープに格納されます。

スペース割り当てを明確に示すために、タイプスペース割り当てのサンプル図を作成しました。
ここに写真の説明を挿入
論争のない
基本型メソッドパラメーターとして8つの基本型を渡す場合、争いはなく、渡されるもの(つまり、実際のパラメーター)、およびメソッドで受信されるもの(つまり、仮パラメーター)です。渡されるものは1であり、受け取られるものは1であり、渡されるものは真であり、受け取られるものは真です。

次の例を見て、変数oldIntValueをchangeIntValueメソッドに渡し、メソッドのパラメーター値を変更しても、最終的な出力結果は1のままです。

public static void main( String[] args ) throws Exception{
    
    
	int oldIntValue = 1;
	System.out.println( oldIntValue );
	passByValueOrRef.changeIntValue( oldIntValue );
	System.out.println( oldIntValue );
}

public static void changeIntValue( int oldValue ){
    
    
	int newValue = 100;
	oldValue = newValue;
}

パラメータ値を変更しても、元の変数の値は変更されません。そうです、Javaは値を渡します。

配列とクラス
配列

一部の学生はそれが間違っていると言いました。以下のコードを見てください、そうではありません。

public static void main( String[] args ) throws Exception{
    
    
	int[] oldArray = new int[] {
    
     1, 2 };
	System.out.println( oldArray[0] );
	changeArrayValue( oldArray );
	System.out.println( oldArray[0] );
}

public static void changeArrayValue( int[] newArray ){
    
    
	newArray[0] = 100;
}

このコードの出力は次のとおりです。

1
100

changeArrayValueメソッドが呼び出されると、渡された配列パラメーターの最初の項目が変更された後、元の変数の内容が変更されることを説明します。この値はどのように転送されますか?

心配しないでください。JVMでの配列のメモリ割り当ての例を示す次の図を見てください。
ここに写真の説明を挿入
実際、changeArrayValueメソッドが受け取るパラメーターは元の変数oldArrayのコピーであることが理解できますが、配列参照はヒープ内の配列スペースの最初のアドレスのみを指しているため、changeArrayValueメソッドが呼び出されると、oldArrayおよびnewArrayが形成されます。スタック内の2つの変数の参照アドレスはすべて、同じ配列アドレスを指します。したがって、パラメーターの各要素を変更することは、元の変数の要素を変更することと同じです。

クラス

一般に、開発プロセスでクラスインスタンスをパラメータとして使用する場合が多く、抽象化するさまざまなオブジェクトがメソッド間で渡されることがよくあります。たとえば、ユーザーエンティティクラスを定義します。

public class User {
    
    

    private String name;

    private int age;

    public User(String name, int age) {
    
    
        this.name = name;
        this.age = age;
    }

    public String getName() {
    
    
        return name;
    }

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

    public int getAge() {
    
    
        return age;
    }

    public void setAge(int age) {
    
    
        this.age = age;
    }

    @Override
    public String toString() {
    
    
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

たとえば、プリミティブエンティティのUserクラスオブジェクトがあり、このエンティティオブジェクトをメソッドに渡します。このメソッドには論理処理が含まれている場合があります。たとえば、ユーザーのname属性を取得し、名前が空であることがわかった場合、 name属性を指定します「User398988」などのランダムな名前を付けます。これは非常に一般的なシナリオです。

通常、このように使用し、ユーザーインスタンスをパラメーターとして渡し、処理の完了後に返します。

public static void main( String[] args ) throws Exception{
    
    
	User oldUser = new User( "原始姓名", 8 );
	System.out.println( oldUser.toString() );
	oldUser = changeUserValue( oldUser );
	System.out.println( oldUser.toString() );
}

public static User changeUserValue( User newUser ){
    
    
	newUser.setName( "新名字" );
	newUser.setAge( 18 );
  return newUser;
}

ただし、変更が完了しても戻らない場合でも、元の変数oldUserのプロパティも次のように変更されることがわかったという学生もいます。

public static void main( String[] args ) throws Exception{
    
    
	User oldUser = new User( "原始姓名", 8 );
	System.out.println( oldUser.toString() );
	changeUserValue( oldUser );
	System.out.println( oldUser.toString() );
}

public static void changeUserValue( User newUser ){
    
    
	newUser.setName( "新名字" );
	newUser.setAge( 18 );
}

返される結果は次のとおりです。

User{
    
    name='原始姓名', age=8}
User{
    
    name='新名字', age=18}

これは単に参照渡しではありませんか?パラメーターの属性を変更すると、元の変数の属性も変更されます。まだ写真を見てください。
ここに写真の説明を挿入
実際には、それはまだ参照パスではありません。C++を学ぶとき、私たちはしばしばポインタである参照パスを使用します。そして、ここで渡されるのは実際にはコピーであり、ヒープスペースオブジェクトエンティティを指すアドレスのみがコピーに格納されます。パラメータnewUserのプロパティを間接的に変更して、元の変数のプロパティを変更します。

一部の学生は、絵を描いてこれが事実だと言いましたか?コピーはコピーであると言いますか?私はそれが引用された参照であり、元の変数であり、理にかなっていると言います。

それは本当に理にかなっています、もしそれが本当に参照によって渡されれば、それは確かに効果です。次に、反例があります。

public static void main( String[] args ) throws Exception{
    
    
	User oldUser = new User( "原始姓名", 8 );
	System.out.println( oldUser.toString() );
	wantChangeUser( oldUser );
	System.out.println( oldUser.toString() );
}

public static void wantChangeUser( User newUser ){
    
    
	newUser = new User( "新姓名", 18 );
}

参照渡しと仮定すると、newUserメソッドとmainメソッドのoldUserは同じ参照オブジェクトであるため、wantChangeUserメソッドのUserエンティティを更新してnewUserに割り当てました。参照渡しの引数に従って、パラメータの値。元の変数に割り当てられ、割り当て操作が完了すると、元の変数oldUserはname = "new name"、age = 18になります。

次に、実行して出力を確認します。

User{
    
    name='原始姓名', age=8}
User{
    
    name='原始姓名', age=8}

結果は変更前の値のままです。元の変数に影響を与えずにnewUserを変更しましたが、これは明らかに参照によって渡されません。

結論として

Javaでのパラメーターの受け渡しは値による受け渡しであり、Javaでの参照による受け渡しの概念はありません。ポインタの概念があるため、通常はC言語やCのようなものから、参照による受け渡しについて話します。

また、CおよびC ++では、プログラマーが自分でメモリを管理する必要があり、ポインターを使用するとメモリリークなどの問題が発生することがよくあります。Javaはガベージコレクション戦略管理を使用してプログラマーを解放します。メモリ、最も重要なポイントの1つこれは、ポインタの使用を回避するためのものであるため、Javaの世界ではいわゆるポインタの受け渡しはありません。

ゼロファンデーションでJavaプログラミングを学びましょう。私の10年間のJava学習分野に参加できます

おすすめ

転載: blog.csdn.net/weixin_49794051/article/details/112468084