学习-Java类和对象之参数传值机制之求球面积
第1关:学习-Java类和对象之参数传值机制之求球面积100过关任务参考答案评论任务描述相关知识形参和实参值传递和引用传递基本数据类型参数的传值引用类型参数的传值编程要求测试说明任务描述本关任务:已知一个球的半径为 12.0,求该球的表面积。球的表面积计算公式:S=4\pi R^2S=4πR2,RR为球的半径。相关知识为了完成本关任务,你需要掌握:基本数据类型参数的传值;引用类型参数的传值;形参和
第1关:学习-Java类和对象之参数传值机制之求球面积
任务描述
本关任务:已知一个球的半径为 12.0,求该球的表面积。
球的表面积计算公式:S=4\pi R^2S=4πR2,RR为球的半径。
相关知识
为了完成本关任务,你需要掌握:
- 基本数据类型参数的传值;
- 引用类型参数的传值;
形参和实参
我们知道,在 Java 中定义方法时,是可以定义参数的,比如:
public static void main(String[] args){
}
这里的 args 就是一个字符串数组类型的参数。
在程序设计语言中,参数有形式参数和实际参数之分,先来看下它们的定义:
-
形式参数:是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数,简称“形参”;
-
实际参数:在主调函数中调用一个函数时,函数名后面括号中的参数称为“实际参数”,简称“实参”。
举个栗子:
public class Demo {
public static void main(String[] args) {
Demo demo = new Demo();
// 实际参数为“张三”
demo.fun("张三");
}
public void fun(String name) {
// 形式参数为 name
System.out.print(name);
}
}
上面例子中,Demo 类中定义了一个 fun 方法,该方法有个 String 类型的参数 name,该参数即为形参。在 main 方法中,调用了 main 方法,传入了一个参数“张三”,该参数即为实参。
那么,实参值是如何传入方法的呢?这是由方法的参数传递机制来控制的。
值传递和引用传递
参数传递机制有两种:值传递和引用传递。我们先来看下程序语言中是如何定义和区分值传递和引用传递的:
-
值传递:是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数;
-
引用传递:是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
那么,在参数传递时,如何区分使用的是值传递还是引用传递? Java 中方法参数共有两种类型:
- 基本数据类型;
- 引用数据类型。
首先我们来看基本数据类型的传值。
基本数据类型参数的传值
例子:
public class Demo {
public static void main(String[] args) {
Demo demo = new Demo();
int i = 10;
System.out.println("pass方法调用前,i的值为=" + i);
demo.pass(i);
System.out.print("pass方法调用后,i的值为=" + i);
}
public void pass(int i) {
i *= 3;
System.out.println("pass方法中,i的值为=" + i);
}
}
上面代码中,我们在类中定义了一个 pass 方法,方法内部将传入的参数 i 的值增加至 3 倍,然后分别在 pass 方法和 main 方法中打印参数的值,执行结果:
pass方法执行前,i的值为=10
pass方法中,i的值为=30
pass方法执行后,i的值为=10
从上面运行结果来看,pass 方法中,i 的值是 30,pass 方法执行结束后,变量 i 的值依然是 10。
可以看出,main 方法里的变量 i,并不是 pass 方法里的 i,pass 方法内部对 i 的值的修改并没有改变实际参数 i 的值,改变的只是 pass 方法中 i 的值(pass 方法中,i=30),因为 pass 方法中的 i 只是 main 方法中变量 i 的复制品。
因此很容易得出结论:在 Java 中,一个方法不可能修改一个基本数据类型的参数 ,所以是值传递。
引用类型参数的传值
上面介绍的是基本数据类型的参数传递,那对于引用数据类型,参数传递又是怎么样的呢?先来看以下代码:
public class Demo {
public static void main(String[] args) {
Demo p = new Demo();
User user = new User();
user.name="张三";
user.age=18;
System.out.println("pass方法调用前,user=" + user.toString());
p.pass(user);
System.out.println("pass方法调用后,user=" + user.toString());
}
public void pass(User user) {
user.name="李四";
System.out.println("pass方法中,user = " + user.toString());
}
}
class User {
String name;
int age;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
上面代码中,定义了一个 User 类,在 main 方法中,new 了一个新的 User 对象 user,然后给 user 对象的成员变量赋值,pass 方法中,修改了传入的 user 对象的属性。
执行结果:
pass方法调用前,user= User{name='张三', age=18}
pass方法中,user = User{name='李四', age=18}
pass方法调用后,user= User{name='李四', age=18}
经过 pass 方法执行后,实参的值竟然被改变了!!!那按照上面的引用传递的定义,实际参数的值被改变了,这不就是引用传递了么?
难道在 Java 的方法中,在传递基本数据类型的时候是值传递,在传递引用数据类型的时候是引用传递?
其实不然,Java 中传递引用数据类型的时候也是值传递。
为什么呢?先给大家说一下概念中的重点:
值传递,是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递,是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
两者的区别如下:
值传递 | 引用传递 | |
---|---|---|
根本区别 | 会创建副本 | 不会创建副本 |
影响结果 | 函数中无法改变原始对象 | 函数中可以改变原始对象 |
复制的是参数的引用(地址值),并不是引用指向的存在于堆内存中的实际对象。
main 方法中的 user 是一个引用(也就是一个指针),它保存了 User 对象的地址值,当把 user 的值赋给 pass 方法的 user 形参后,即让 pass 方法的 user 形参也保存了这个地址值,即也会引用到堆内存中的 User 对象。
上面代码中,之所以产生引用传递的错觉,是因为参数保存的是实际对象的地址值,你改变的只是地址值指向的堆内存中的实际对象,并没有真正改变参数,参数的地址值没有变。
下面结合生活中的场景,再来深入理解一下值传递和引用传递。
你有一把钥匙,当你的朋友想要去你家的时候,如果你直接把你的钥匙给他了,这就是引用传递。这种情况下,如果他对这把钥匙做了什么事情,比如他在钥匙上刻下了自己名字,那么这把钥匙还给你的时候,你会看到钥匙上有他的名字。
你有一把钥匙,当你的朋友想要去你家的时候,你复刻了一把新钥匙给他,旧钥匙还在自己手里,这就是值传递。这种情况下,他对这把钥匙做什么都不会影响你手里的这把钥匙。
但是,不管上面哪种情况,你的朋友拿着你给他的钥匙,进到你的家里,假如把你家的电视砸了,那你说你会不会受到影响?
我们在 pass 方法中,改变 user 对象的 name 属性的值的时候,不就是在“砸电视”么。你改变的不是那把钥匙(地址值),而是钥匙打开的房子(地址值对应的实际对象)。
那我们怎样才能真正的改变参数呢,看如下代码:
public class Demo {
public static void main(String[] args) {
Demo p = new Demo();
User user = new User();
user.name = "张三";
user.age = 18;
System.out.println("pass方法调用前,user=" + user.toString());
p.pass(user);
System.out.println("pass方法调用后,user=" + user.toString());
}
public void pass(User user) {
user = new User();
user.name = "李四";
user.age = 20;
System.out.println("pass方法中,user = " + user.toString());
}
}
class User {
String name;
int age;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
在这段代码中,pass 方法中,我们真正的改变了 user 参数,因为它指向了一个新的地址(user = new User()),即参数的地址值改变了。执行结果:
pass方法调用前,user= User{name='张三', age=18}
pass方法中,user = User{name='李四', age=20}
pass方法调用后,user= User{name='张三', age=18}
从结果看出,虽然 pass 对参数进行了修改,但是没有影响到实际参数。
所以说,Java 中其实还是值传递的,只不过对于引用类型参数,值的内容是对象的引用。
编程要求
仔细阅读右侧编辑区内给出的代码框架及注释,按照提示编写程序代码。
测试说明
平台将使用测试集运行你编写的程序代码,若全部的运行结果正确,则通关。 可在右侧“测试结果”区查看具体的测试集详情。
开始你的任务吧,祝你成功!
/**
* 任务:已知一个球的半径为 12.0,求该球的表面积。
* 类名为:Sphere
*/
public class Sphere {
// 请在下面的Begin-End之间按照注释中给出的提示编写正确的代码
/********** Begin **********/
// 定义圆的半径和π,π为 Math中的π
double r;
// 无参构造
Sphere(){
}
// 有参构造
Sphere(double r){
this.r=r;
}
/**
* 定义一个方法,该方法实现计算球的表面积,返回值为double,携带一个参数,为球的半径
*/
public static double mian(double r){
return 4*(Math.PI)*r*r;
}
// 定义主方法
public static void main(String[] args){
// 通过无参构造创建球对象
Sphere sphere=new Sphere();
// 调用计算球面积的方法,将半径 r 的值传入
double result = sphere.mian(12.0);
// 四舍五入格式化不换行输出球的面积,输出格式:球的表面积为xx
System.out.print("球的表面积为"+ String.format("%.2f",result));
}
/********** End **********/
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)