方法参数
方法可以包含 0 个或任意个参数。方法参数用于接收传递给方法的变量值。调用方法时,必须严格按照参数的定义一一传递。例如:
classPerson{// ... 其他代码publicvoidsetNameAndAge(Stringname,intage){// ... 方法体}}调用这个setNameAndAge()方法时,必须有两个参数,且第一个参数必须为String,第二个参数必须为int:
Personming=newPerson();ming.setNameAndAge("Xiao Ming");// 编译错误:参数个数不对ming.setNameAndAge(12,"Xiao Ming");// 编译错误:参数类型不对可变参数
1. 场景:请客吃饭点菜(传姓名列表)
背景:你写了一个Dinner类,要邀请朋友来吃饭。
1.1 🔴 用数组写法(繁琐)
classDinner{privateString[]guests;publicvoidsetGuests(String[]guests){this.guests=guests;}}publicclassMain{publicstaticvoidmain(String[]args){Dinnerdinner=newDinner();// 请3个人:必须手动 new String[]dinner.setGuests(newString[]{"张三","李四","王五"});// 请2个人:还是得 new String[]dinner.setGuests(newString[]{"赵六","孙七"});// 就请1个人:依然要 new String[]dinner.setGuests(newString[]{"周八"});// 谁也不请(空桌):还是得 new String[0]dinner.setGuests(newString[0]);}}痛点:每次都要写new String[]{},请 3 个人要写,请 1 个人也要写,代码冗余。
1.2 🟢 用可变参数(简洁)
classDinner{privateString[]guests;publicvoidsetGuests(String...guests){// 参数类型后加 ...this.guests=guests;}}publicclassMain{publicstaticvoidmain(String[]args){Dinnerdinner=newDinner();// 请3个人:直接传名字!dinner.setGuests("张三","李四","王五");// 请2个人:直接传名字!dinner.setGuests("赵六","孙七");// 请1个人:直接传名字!dinner.setGuests("周八");// 谁也不请:括号里留空即可!dinner.setGuests();// 代码简洁}}参数绑定
调用方把参数传递给实例方法时,调用时传递的值会按参数位置一一绑定。
2.1 基本类型参数绑定
我们先观察一个基本类型参数的传递:
// 基本类型参数绑定publicclassMain{publicstaticvoidmain(String[]args){Personp=newPerson();intn=15;// 在 main 方法的栈帧中,变量 n 存储值 15p.setAge(n);// 传入 n 的值:将 n 的副本 15 赋值给参数 ageSystem.out.println(p.getAge());// 15n=20;// 将 n 的值改为 20System.out.println(p.getAge());// 输出是 15 还是 20?}}classPerson{privateintage;publicintgetAge(){returnthis.age;}publicvoidsetAge(intage){this.age=age;}}运行代码,从结果可知,修改外部的局部变量n,不影响实例p的age字段。
原因:setAge()方法获得的参数,复制了n的值,因此,p.age和局部变量n互不影响。
核心结论:基本类型参数的传递,是调用方值的复制。双方各自的后续修改,互不影响。
通俗比喻:“按值传递”相当于传递复印件的复印件。外面
n是原件(值 15),里面age是复印件(值 15)。你后来把原件涂改成 20,复印件依然是 15,两者独立。
2.2 引用类型参数绑定
另一个传递引用参数的例子:
// 引用类型参数绑定publicclassMain{publicstaticvoidmain(String[]args){Personp=newPerson();String[]fullname=newString[]{"Homer","Simpson"};p.setName(fullname);// 传入 fullname 数组的引用System.out.println(p.getName());// "Homer Simpson"fullname[0]="Bart";// 修改 fullname 数组的第一个元素为 "Bart"System.out.println(p.getName());// 输出是 "Homer Simpson" 还是 "Bart Simpson"?}}classPerson{privateString[]name;publicStringgetName(){returnthis.name[0]+" "+this.name[1];}publicvoidsetName(String[]name){this.name=name;}}注意到setName()的参数是一个数组。一开始,把fullname数组传进去,然后修改fullname数组的内容,结果发现,实例p的字段p.name也被修改了!
核心结论:引用类型参数的传递,调用方的变量和接收方的参数变量,指向的是同一个对象。双方任意一方对这个对象的修改,都会影响对方。
2.3 深入理解:String 类型的特殊情况
有了上面的结论,我们再看一个特殊的例子:
// 引用类型参数绑定(String 类型)publicclassMain{publicstaticvoidmain(String[]args){Personp=newPerson();Stringbob="Bob";p.setName(bob);// 传入 bob 变量(引用)System.out.println(p.getName());// "Bob"bob="Alice";// 将 bob 变量重新赋值System.out.println(p.getName());// 输出是 "Bob" 还是 "Alice"?}}classPerson{privateStringname;publicStringgetName(){returnthis.name;}publicvoidsetName(Stringname){this.name=name;}}不要怀疑引用参数绑定的机制,请解释为什么上面的代码两次输出都是"Bob"。
bob="Alice";// 关键:这句话不是去修改 "Bob" 这个字符串对象(String 是不可变的)// 它是在堆中创建了一个新的字符串对象 "Alice"(假设地址 0x300)// 然后把 main 方法里的变量 bob,从指向 0x200 ("Bob") 改为指向 0x300 ("Alice")// 此时:bob 指向 0x300 ("Alice"),但 p.name 依然指向 0x200 ("Bob")// 因此,p.getName() 仍然返回 "Bob"// 若想修改 p.name,需要调用:p.setName("Alice");3. 练习
给Person类增加getAge/setAge方法:
publicclassMain{publicstaticvoidmain(String[]args){Personming=newPerson();ming.setName("小明");System.out.println(ming.getName());ming.setAge(12);System.out.println(ming.getAge());}}classPerson{privateStringname;privateintage;publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicintgetAge(){returnage;}publicvoidsetAge(intage){this.age=age;}}声明:本文内容为本人基于 廖雪峰 Java 教程 进行实操学习后的个人笔记与代码实践记录。
文中所有知识点、代码示例及讲解均源自上述网站,著作权归原作者廖雪峰及其团队所有。本文仅用于个人学习总结与知识分享,不涉及任何商业用途。如涉及侵权,请联系删除。