学习-Java类和对象之参数传值机制之求球面积

本文详细解释了Java中参数的值传递和引用传递机制,通过实例演示了如何计算球的表面积,指出尽管使用的是引用类型,但实质上是值传递,因为改变的是对象的引用而非对象本身。
摘要由CSDN通过智能技术生成

第1关:学习-Java类和对象之参数传值机制之求球面积

任务描述

本关任务:已知一个球的半径为 12.0,求该球的表面积。

球的表面积计算公式:S=4πR2,R为球的半径。

相关知识

为了完成本关任务,你需要掌握:

  1. 基本数据类型参数的传值;
  2. 引用类型参数的传值;
形参和实参

我们知道,在 Java 中定义方法时,是可以定义参数的,比如:

 
  1. public static void main(String[] args){
  2. }

这里的 args 就是一个字符串数组类型的参数。

在程序设计语言中,参数有形式参数和实际参数之分,先来看下它们的定义:

  • 形式参数:是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数,简称“形参”;

  • 实际参数:在主调函数中调用一个函数时,函数名后面括号中的参数称为“实际参数”,简称“实参”。

举个栗子:

 
  1. public class Demo {
  2. public static void main(String[] args) {
  3. Demo demo = new Demo();
  4. // 实际参数为“张三”
  5. demo.fun("张三");
  6. }
  7. public void fun(String name) {
  8. // 形式参数为 name
  9. System.out.print(name);
  10. }
  11. }

上面例子中,Demo 类中定义了一个 fun 方法,该方法有个 String 类型的参数 name,该参数即为形参。在 main 方法中,调用了 main 方法,传入了一个参数“张三”,该参数即为实参。

那么,实参值是如何传入方法的呢?这是由方法的参数传递机制来控制的。

值传递和引用传递

参数传递机制有两种:值传递和引用传递。我们先来看下程序语言中是如何定义和区分值传递和引用传递的:

  • 值传递:是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数;

  • 引用传递:是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

那么,在参数传递时,如何区分使用的是值传递还是引用传递? Java 中方法参数共有两种类型:

  • 基本数据类型;
  • 引用数据类型。

首先我们来看基本数据类型的传值。

基本数据类型参数的传值

例子:

 
  1. public class Demo {
  2. public static void main(String[] args) {
  3. Demo demo = new Demo();
  4. int i = 10;
  5. System.out.println("pass方法调用前,i的值为=" + i);
  6. demo.pass(i);
  7. System.out.print("pass方法调用后,i的值为=" + i);
  8. }
  9. public void pass(int i) {
  10. i *= 3;
  11. System.out.println("pass方法中,i的值为=" + i);
  12. }
  13. }

上面代码中,我们在类中定义了一个 pass 方法,方法内部将传入的参数 i 的值增加至 3 倍,然后分别在 pass 方法和 main 方法中打印参数的值,执行结果:

 
  1. pass方法执行前,i的值为=10
  2. pass方法中,i的值为=30
  3. 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 中,一个方法不可能修改一个基本数据类型的参数 ,所以是值传递。

引用类型参数的传值

上面介绍的是基本数据类型的参数传递,那对于引用数据类型,参数传递又是怎么样的呢?先来看以下代码:

 
  1. public class Demo {
  2. public static void main(String[] args) {
  3. Demo p = new Demo();
  4. User user = new User();
  5. user.name="张三";
  6. user.age=18;
  7. System.out.println("pass方法调用前,user=" + user.toString());
  8. p.pass(user);
  9. System.out.println("pass方法调用后,user=" + user.toString());
  10. }
  11. public void pass(User user) {
  12. user.name="李四";
  13. System.out.println("pass方法中,user = " + user.toString());
  14. }
  15. }
  16. class User {
  17. String name;
  18. int age;
  19. @Override
  20. public String toString() {
  21. return "User{" +
  22. "name='" + name + '\'' +
  23. ", age=" + age +
  24. '}';
  25. }
  26. }

上面代码中,定义了一个 User 类,在 main 方法中,new 了一个新的 User 对象 user,然后给 user 对象的成员变量赋值,pass 方法中,修改了传入的 user 对象的属性。

执行结果:

 
  1. pass方法调用前,user= User{name='张三', age=18}
  2. pass方法中,user = User{name='李四', age=18}
  3. pass方法调用后,user= User{name='李四', age=18}

经过 pass 方法执行后,实参的值竟然被改变了!!!那按照上面的引用传递的定义,实际参数的值被改变了,这不就是引用传递了么?

难道在 Java 的方法中,在传递基本数据类型的时候是值传递,在传递引用数据类型的时候是引用传递?

其实不然,Java 中传递引用数据类型的时候也是值传递。

为什么呢?先给大家说一下概念中的重点:

值传递,是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。

引用传递,是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

两者的区别如下:

值传递引用传递
根本区别会创建副本不会创建副本
影响结果函数中无法改变原始对象函数中可以改变原始对象

复制的是参数的引用(地址值),并不是引用指向的存在于堆内存中的实际对象。

main 方法中的 user 是一个引用(也就是一个指针),它保存了 User 对象的地址值,当把 user 的值赋给 pass 方法的 user 形参后,即让 pass 方法的 user 形参也保存了这个地址值,即也会引用到堆内存中的 User 对象。

上面代码中,之所以产生引用传递的错觉,是因为参数保存的是实际对象的地址值,你改变的只是地址值指向的堆内存中的实际对象,并没有真正改变参数,参数的地址值没有变。

下面结合生活中的场景,再来深入理解一下值传递和引用传递。

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

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

但是,不管上面哪种情况,你的朋友拿着你给他的钥匙,进到你的家里,假如把你家的电视砸了,那你说你会不会受到影响?

我们在 pass 方法中,改变 user 对象的 name 属性的值的时候,不就是在“砸电视”么。你改变的不是那把钥匙(地址值),而是钥匙打开的房子(地址值对应的实际对象)。

那我们怎样才能真正的改变参数呢,看如下代码:

 
  1. public class Demo {
  2. public static void main(String[] args) {
  3. Demo p = new Demo();
  4. User user = new User();
  5. user.name = "张三";
  6. user.age = 18;
  7. System.out.println("pass方法调用前,user=" + user.toString());
  8. p.pass(user);
  9. System.out.println("pass方法调用后,user=" + user.toString());
  10. }
  11. public void pass(User user) {
  12. user = new User();
  13. user.name = "李四";
  14. user.age = 20;
  15. System.out.println("pass方法中,user = " + user.toString());
  16. }
  17. }
  18. class User {
  19. String name;
  20. int age;
  21. @Override
  22. public String toString() {
  23. return "User{" +
  24. "name='" + name + '\'' +
  25. ", age=" + age +
  26. '}';
  27. }
  28. }

在这段代码中,pass 方法中,我们真正的改变了 user 参数,因为它指向了一个新的地址(user = new User()),即参数的地址值改变了。执行结果:

 
  1. pass方法调用前,user= User{name='张三', age=18}
  2. pass方法中,user = User{name='李四', age=20}
  3. 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 main(double r){
        return 4*(Math.PI)*r*r;
    }



    // 定义主方法
public static void main(String[]args){

    // 通过无参构造创建球对象
Sphere sphere=new Sphere();

    // 调用计算球面积的方法,将半径 r 的值传入
double result=sphere.main(12.0);
    // 四舍五入格式化不换行输出球的面积,输出格式:球的表面积为xx
System.out.print("球的表面积为"+String.format("%.2f",result));
}

/********** End **********/

}

  • 24
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值