图形化理解Java中的形参和实参

方法的参数传递

在具体理解形参和实参的区别前,我们需要明白几个基本概念:

  • 形参:形式参数,方法声明时的参数,它用来接收调用者传递来的实参;它只有在被调用时JVM才会为其分配内存单元,调用结束后回收内存单元,因此它的作用域只限于方法内部,对于形参的改变不会影响实参
  • 实参: 方法调用时实际传给形参的参数值,实参在传递给其他参数时要先赋值

Java里方法的参数传递只有值传递一种:值传递! 即将实际参数值得副本传入方法,而参数本身不受影响。

  • 值传递:如果方法的形参是基本数据类型,那么实参(实际的数据)像形参传递参数时,就是把实参的值复制给了形参
  • 引用传递:如果方法的形参是对象,那么实参(实际的对象)向形参传递参数时也是把值传递给形参,而这个值是实参在栈内存中的值,这个值也是引用对象在堆内存种的地址

那么什么时候是值传递,什么时候是引用传递呢?顾名思义,当实参为Java中的八种基本数据类型时,在调用方法传递实参时就是值传递;当实参是除了八种基本类型之外的对象时,调用方法时就是引用传递,即传递给被调用方法的时对象在堆内存的地址。

  • 基本数据类型(八种):整数类型(byte, short, int, long)、浮点类型(float, double)、字符类型(char)、boolean类型(true, false)

    • long型变量赋值时后面要在值后面跟上字母 l
    • float型变量赋值时要在值后面跟上字母 f
    • byte、int、long、和short都可以用十进制、16进制以及8进制的方式来表示。当使用常量的时候,前缀 0 表示 8 进制,而前缀 0x 代表 16 进制
    package com.company;
    
    public class Main {
    
        public static void main(String[] args) {
            // byte
            System.out.println("基本类型:byte 二进制位数:" + Byte.SIZE);
            System.out.println("包装类:java.lang.Byte");
            System.out.println("最小值:Byte.MIN_VALUE=" + Byte.MIN_VALUE);
            System.out.println("最大值:Byte.MAX_VALUE=" + Byte.MAX_VALUE);
            System.out.println();
    
            // short
            System.out.println("基本类型:short 二进制位数:" + Short.SIZE);
            System.out.println("包装类:java.lang.Short");
            System.out.println("最小值:Short.MIN_VALUE=" + Short.MIN_VALUE);
            System.out.println("最大值:Short.MAX_VALUE=" + Short.MAX_VALUE);
            System.out.println();
    
            // int
            System.out.println("基本类型:int 二进制位数:" + Integer.SIZE);
            System.out.println("包装类:java.lang.Integer");
            System.out.println("最小值:Integer.MIN_VALUE=" + Integer.MIN_VALUE);
            System.out.println("最大值:Integer.MAX_VALUE=" + Integer.MAX_VALUE);
            System.out.println();
    
            // long
            System.out.println("基本类型:long 二进制位数:" + Long.SIZE);
            System.out.println("包装类:java.lang.Long");
            System.out.println("最小值:Long.MIN_VALUE=" + Long.MIN_VALUE);
            System.out.println("最大值:Long.MAX_VALUE=" + Long.MAX_VALUE);
            System.out.println();
    
            // float
            System.out.println("基本类型:float 二进制位数:" + Float.SIZE);
            System.out.println("包装类:java.lang.Float");
            System.out.println("最小值:Float.MIN_VALUE=" + Float.MIN_VALUE);
            System.out.println("最大值:Float.MAX_VALUE=" + Float.MAX_VALUE);
            System.out.println();
    
            // double
            System.out.println("基本类型:double 二进制位数:" + Double.SIZE);
            System.out.println("包装类:java.lang.Double");
            System.out.println("最小值:Double.MIN_VALUE=" + Double.MIN_VALUE);
            System.out.println("最大值:Double.MAX_VALUE=" + Double.MAX_VALUE);
            System.out.println();
    
            // char
            System.out.println("基本类型:char 二进制位数:" + Character.SIZE);
            System.out.println("包装类:java.lang.Character");
            // 以数值形式而不是字符形式将Character.MIN_VALUE输出到控制台
            System.out.println("最小值:Character.MIN_VALUE="
                    + (int) Character.MIN_VALUE);
            // 以数值形式而不是字符形式将Character.MAX_VALUE输出到控制台
            System.out.println("最大值:Character.MAX_VALUE="
                    + (int) Character.MAX_VALUE);
        }
    }
    
    /*
    基本类型:byte 二进制位数:8
    包装类:java.lang.Byte
    最小值:Byte.MIN_VALUE=-128
    最大值:Byte.MAX_VALUE=127
    
    基本类型:short 二进制位数:16
    包装类:java.lang.Short
    最小值:Short.MIN_VALUE=-32768
    最大值:Short.MAX_VALUE=32767
    
    基本类型:int 二进制位数:32
    包装类:java.lang.Integer
    最小值:Integer.MIN_VALUE=-2147483648
    最大值:Integer.MAX_VALUE=2147483647
    
    基本类型:long 二进制位数:64
    包装类:java.lang.Long
    最小值:Long.MIN_VALUE=-9223372036854775808
    最大值:Long.MAX_VALUE=9223372036854775807
    
    基本类型:float 二进制位数:32
    包装类:java.lang.Float
    最小值:Float.MIN_VALUE=1.4E-45
    最大值:Float.MAX_VALUE=3.4028235E38
    
    基本类型:double 二进制位数:64
    包装类:java.lang.Double
    最小值:Double.MIN_VALUE=4.9E-324
    最大值:Double.MAX_VALUE=1.7976931348623157E308
    
    基本类型:char 二进制位数:16
    包装类:java.lang.Character
    最小值:Character.MIN_VALUE=0
    最大值:Character.MAX_VALUE=65535
    */
    
    
  • 引用数据类型: 除了八种基本数据类型之外的都是引用类型,如:

    • String:由0或多个字母数字符号组成的串;可由null初始化;值不可变

更详细内容可见:Java 基本数据类型

JVM的内存需要划分为5个部分:

  • 栈(Stack):存放的是方法中的局部变量

    • 局部变量:方法的参数或是是方法{}内部的变量
    • 作用域:一旦超出作用域,立刻从栈内存中消失
  • 堆(Heap):凡是new出来的东西都在堆中,堆内存的里面的东西都有一个16进制地址;堆内存里面的数据都有默认值:

    • 整形:0
    • 浮点型:0.0
    • 字符:“\u0000”
    • 布尔:false
    • 引用类型:null
  • 方法区(Method Area):存储.class相关信息,包含方法的信息

  • 本地方法栈(Native Method Stack):与OS有关

  • 寄存器(pc Register):与CPU有关

我们重点关注前三个部分,它们在JVM内存的简要示意图如下所示:


在这里插入图片描述

下面先看一个值传递的例子:

public class argsTest {
    public static void main(String[] args) {
        int a = 1;
        int b = 2;
        System.out.println("没有交换前:" + "a = " + a + ",b = " + b);
        swapNumbers(a, b);
        System.out.println("交换后:" + "a = " + a + ",b = " + b);
    }

    private static void swapNumbers(int a, int b) {
         int temp = a;
         a = b;
         b = temp;

        System.out.println("交换中:" + "a = " + a + ",b = " + b);
    }
}

/**
 * OUTPUT:
 * 没有交换前:a = 1,b = 2
 * 交换中:a = 2,b = 1
 * 交换后:a = 1,b = 2
 */

如下图所示,当在main()中初始化a,b时,由于它们属于基本数据类型,因此JVM会为ab在栈中分配内存空间用于存放初始化的值。而当在调用swapNumbers(int a, int b)方法时,main()swapNumbers(a, b)中的ab是最初a和b的副本,JVM会为它们另外开辟空间存放,由于它们在栈中存放的地址不同,因此被调用方法内对于ab的改变不影响最初的值。


在这里插入图片描述

下面再看一个引用传递的例子:

package com.company;

class Data{
    int a = 1;
    int b = 2;

}
public class argsTest {
    public static void main(String[] args) {
        Data d = new Data();

        System.out.println("没有交换前:" + "a = " + d.a + ",b = " + d.b);
        swapNumbers(d);
        System.out.println("交换后:" + "a = " + d.a + ",b = " + d.b);
    }

    private static void swapNumbers(Data d) {
         int temp = d.a;
         d.a = d.b;
         d.b = temp;

        System.out.println("交换中:" + "a = " + d.a + ",b = " + d.b);
    }
}

/**
 * OUTPUT:
 * 没有交换前:a = 1,b = 2
 * 交换中:a = 2,b = 1
 * 交换后:a = 2,b = 1
 */

而当我们传递给swapNumbers()的是一个类对象时,实际传递的是该引用对象在堆中的地址。swapNumbers()中的对象和main()中的对象虽然在栈中地址不同,但它们保存的都是相同的内容,即所指向的引用对象在堆中的地址。因此,swapNumbers()中对于对象的改变实际上直接改变了堆中的对象,那么main()中的对象的值自然也就发生了改变。

如下所示,当执行Data d = new Data()时,JVM首先会在堆中开辟空间存放创建的new Data(),然后在栈中为d开辟空间存放new Data()的地址,即d的值。然后当执行swapNumbers()时,JVM首先为其在栈中开辟空间存放传入的对象在堆中的地址,然后执行下面的流程。因此,它们指向的是堆中同样的对象,swapNumbers()中对于对象的改变也会影响main()d对应的值。


在这里插入图片描述

Java 形参与实参 中讲解了关于包装类型在作为形参和实参时的一些情况,深入理解可以继续往下看~

发布了448 篇原创文章 · 获赞 122 · 访问量 22万+

猜你喜欢

转载自blog.csdn.net/Forlogen/article/details/105575189