Java参数传递

Java参数传递

一、 方法重载

一个类中可以存在多个同名的方法,只要这些方法的参数列表不同即可。

  • 参数列表不同:参数个数或者参数类型不同
  • 方法重载与修饰符、返回值类型等统统无关,只看参数列表

二、 可变个数的形参

从Java5.0开始,Java有了可变形参的语法,即形参的类型可以确定,但是形参的个数不确定

可变形参的格式:方法名(参数类型... 参数名)

比如:

public void f(int i, String... strings) {
    for (String string : strings) {
        System.out.println(string);
    }
}

此时,调用方法f时,可以传入1个或多个字符串,甚至可以不传入字符串。比如:

f(0); // 不传入字符串
f(1, "one"); // 传入1个字符串
f(2, "one", "two"); //入多个字符串

注:

  • 可变形参等价于使用数组形参,因此当两个方法一个使用可变形参、一个使用数组且其他参数列表相同时,将会报错。比如下面的语句将会报错:

    public void f(int i, String... strings) {
    
    }
    
    public void f(int i, String[] strings) {
        
    }
    
  • 可变形参需要放在方法参数列表的最后

  • 在一个方法中,最多只能声明一个可变形参

三、 值传递

3.1 两种传递方式

参数传递有引用传递以及值传递两种方式:

  • 引用传递,在调用方法时将参数的地址直接传递到方法中
  • 值传递,在调用方法时将参数的值拷贝一份传递到方法中

3.2 Java参数传递类型

Java在传递参数时,只有值传递这一种方式。从JVM层面来看,Java在传递参数时是将栈中变量的值拷贝一份传递给参数。

3.2.1 传递基本类型

当传递基本类型时,将基本类型的数值直接拷贝一份传递给方法,在方法内部对参数做的修改并不会影响到方法外部。比如:

public class Main {
    public static void main(String[] args) {
        int i = 10;
        System.out.println("调用 test 方法前 i 的值:" + i);
        
        test(i);
        System.out.println("调用 test 方法后 i 的值:" + i); // test 方法并不会修改 i 的值
    }

    public static void test(int m) {
        m = 20;
    }
}

调用test方法时,main方法将整形变量i的值10拷贝赋值给参数m(这相当于初始化test方法内部的变量m),在test内部对变量m的改动并不会影响main方法中的变量i

3.2.2 传递引用类型

引用类型与基本类型在内存中的存储方式略有不同:

  • 基本类型在栈中存放的是变量具体的值
  • 引用类型在栈中存放了一个地址,该地址指向堆中的某块内存区域,而该内存区域才是真正存放对象的地方
public class Main {
    public static void main(String[] args) {
        int i = 0;
        
        User user = new User(16, "main");
    }
}

class User {
    public int age;
    public String name;

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

比如,上面这段代码,main方法中有两个局部变量,一个是整形变量i,一个是引用类型user,这两个变量在内存中的分布如下图所示:

请添加图片描述

引用类型的这种存储特点使得其在参数传递时产生了一些副作用:

  1. 与传递基本类型一样,当传递引用类型时,将引用类型的数值直接拷贝一份传递给方法,此时不会影响到存放在栈中的值。比如:

    public class Main {
        public static void main(String[] args) {
            User user = new User(16, "main");
            System.out.println("调用 f1 方法前 user 的值:" + user);
    
            f1(user);
            System.out.println("调用 f1 方法后 user 的值:" + user); // user的值不会发生改变
        }
    
        public static void f1(User userArg) {
            userArg = new User(20, "f2");
    
            System.out.println("f1:" + userArg);
        }
    }
    
    class User {
        public int age;
        public String name;
    
        public User(int age, String name) {
            this.age = age;
            this.name = name;
        }
    }
    

    从输出结果可以看到,与传递基本类型一样,在调用f1方法前后user的值不会发生改变。

  2. 虽然不会影响到存放在栈中的值,但是会影响到堆中的值。比如:

    public class Main {
        public static void main(String[] args) {
            User user = new User(16, "main");
            System.out.println("调用 f2 方法前 user 的值:" + user.age + user.name);
    
            f2(user);
            System.out.println("调用 f2 方法后 user 的值:" + user.age + user.name); // user所指向的对象发生了改变
        }
    
        public static void f2(User userArg) {
            userArg.age = 18;
            userArg.name = "f2";
        }
    }
    
    class User {
        public int age;
        public String name;
    
        public User(int age, String name) {
            this.age = age;
            this.name = name;
        }
    }
    

    从输出结果可以看到,在调用f2方法后user对象的成员变量值发生了改变。这是因为虽然值传递是拷贝,但是useruserArg指向的地址是同一个,所以方法内部对引用类型内部的成员变量进行修改时,还是会影响到堆中的值。此时,useruserArg在内存中的存储情况如下:

    请添加图片描述

四、 笔/面试题目

  1. 问:Java是值传递还是引用传递

    答:Java是值传递

  2. 问:下面代码执行完之后,数组ints的元素值是多少

    public class Main {
        public static void main(String[] args) {
            int[] ints = new int[]{1, 2, 3};
            f(ints);
        }
    
        public static void f(int[] intsArg) {
            for (int i = 0; i < intsArg.length; i++) {
                intsArg[i] += 10;
            }
        }
    }
    

    答:数组也是一种引用类型,当调用方法f时,参入的是数组ints在栈中的值,该值指向堆中存存放数组具体内容的内存区域。当在方法f内部对数组内容进行操作时,将会修改堆中的内容,而由于intsintsArg指向同一块堆内存,因此ints所指向的数组也会被修改。因此最终数组ints的元素值是11、12、13

  3. 问:下面代码执行完之后,数组ints的元素值是多少

    public class Main {
        public static void main(String[] args) {
            int[] ints = new int[]{1, 2, 3};
            f(ints);
        }
    
        public static void f(int[] intsArg) {
            for (int i : intsArg) {
                i += 10;
            }
        }
    }
    

    答:这题与上题相识,但在方法f内部遍历数组的方式却不同:

    • 上一题是通过一个递增的变量i依次访问数组中的元素并修改元素的值
    • 本题是通过foreach语法依次取出数组中的元素值并将其赋值给一个新的变量i,然后修改这个变量i。修改的是变量i,并未对数组元素做修改

    因此最终数组ints的元素值仍然是1、2、3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值