面试官————欺负我不懂Java为什么只有值传递???

今天来整理一篇常见的面试问题Java为什么只有值传递???

01 理解变量的赋值

如果等号右边变量是基本数据类型,此时赋值的是变量所保存的数据值。

代码如下:

public class VariableTest {
    public static void main(String[] args) {
        int m = 10;
        int n = m;
        n = 20;
        System.out.println("m="+m+"  n="+n);
    }
}

图解:
在这里插入图片描述
分析:
第一行 声明了局部变量m赋值为10。

第二行 声明了n变量赋值为m变量,m变量是基本数据类型,此时赋值为m变量的数据值,也就是10。

第三行 n变量重新赋值为20。

程序结果:

m=10 n=20

如果等号右边变量是引用数据类型,此时赋值的是变量所保存的数据的地址值。

代码如下:

public class VariableTest {
    public static void main(String[] args) { 
        Order o1 = new Order();
        o1.orderId = 1001;
        Order o2 = o1;
        System.out.println("o1.orderId="+o1.orderId+" o2.orderId="+o2.orderId);
    }
}
class Order{
    int orderId;
}

图解:
在这里插入图片描述

分析:
第一行 创建了对象 在堆内存开辟了一片内存空间,产生一个地址值,将这个地址值,赋值给引用变量o1(也就是o1指向了0x001)。

第二行 o1对成员变量orderId赋值为1001。

第三行 声明了o2引用,将o1的地址值赋值给o2。

结论: 这时o1、o2引用指向了同一个对象。

程序结果:

o1.orderId=1001 o2.orderId=1001

对引用数据类型稍作修改:

 		Order o1 = new Order();
        o1.orderId = 1001;
        Order o2 = o1;
        o2.orderId = 1002;
        System.out.println("o1.orderId="+o1.orderId+" o2.orderId="+o2.orderId);

图解:
在这里插入图片描述

分析:
这里只对第四行解释:
因为o1、o2都指向了同一个对象,所以当o2引用对 对象的属性修改为1002时,两个引用打印的orderId都是1002。

程序结果:

o1.orderId=1002 o2.orderId=1002

经过上面的铺垫,我们接着继续。

涉及到 Java传递机制 无非两种情况:值传递、引用传递。
下面的三个小块是本篇博客的中心内容——Java为什么只有值传递。

基础知识点:

什么是形式参数?
方法声明时的参数

什么是实际参数?
方法调用时,实际传递给形参的参数值

02 传递基本数据类型

通过对上面的题目的分析,相信你了点眉头,继续往下看。

代码如下:

public class ValueTransferTest {
    public static void main(String[] args) {
        int a = 8;
        add(a);
        System.out.println("a="+a);
    }
    public static void add(int a) {
        int c = 10;
        System.out.println(a + c);
        a = 20;
    }
}

图解:
在这里插入图片描述

分析:
第一行 声明了变量a ,赋值为8
第二行 调用add方法时,将实参传给形参时,形参a拷贝了一份实参a值作为副本。
方法内又声明了变量c,赋值为10。然后输出 a + c的值为18,紧接着重新给形参a赋值为20。
第三行 add方法执行完,同时被垃圾回收期清除,此时并不会影响变量a的值。

程序结果:

18
a=8

结论:如果参数是基本数据类型,此时实参赋给形参的是实参的真实存储的数据值。

03 传递引用数据类型

代码如下:

public class ValueTransferTest {
    public static void main(String[] args) {
        Student stu = new Student("王五");
        System.out.println("改变前: stu.name = "+stu.name);
        change(stu);
        System.out.println("改变后: stu.name = "+stu.name);
    }
    public static void change(Student student) {
        student.name="李四";
        System.out.println("方法内 student.name = "+student.name);
    }
}

class Student {
    String name;
    public Student(String name) {
        this.name = name;
    }
}

图解:
在这里插入图片描述

分析:
第一行 创建了对象 在堆内存开辟了一片内存空间,产生一个地址值,将这个地址值,赋值给引用变量stu(也就是stu指向了0x5566),同时初始化成员变量为王五。
第三行 执行change方法 ,将实参传给形参时,形参拷贝了一份实参地址作为副本,此时的形参student的地址值也为0x5566,两个引用指向了同一个对象。
方法内修改对象中name的值为李四。
此时stu引用指向对象的属性也发生了改变。

程序结果:

改变前: stu.name = 王五
方法内 student.name = 李四
改变后: stu.name = 李四

结论:如果参数是引用数据类型,实参传给形参的是实参存储数据的地址值。

04 传递不可变类型

除了常见的String类,还有基本数据类型的包装类比如:Integer、Long等。
着重看一下String类在Java传递机制中的变化。

代码如下:

public class StringTransferTest {
    public static void main(String[] args) {
        String tt = "程序员控";
        System.out.println("修改前 tt="+tt);
        test(tt);
        System.out.println("修改后 tt=" +tt);

    }
    public static void test(String str) {
        str = "关注公众号";
        System.out.println("test中tt= " +str);
    }
}

当看完这段代码中,我们快速得到了答案:修改前 程序员控 修改后 关注公众号
然而结果出乎意料 。。。。。。

其实结果是这样的

修改前 tt=程序员控
test中tt= 关注公众号
修改后 tt=程序员控

咦?String类是引用类型的啊,别忘了String类同时也是一个不可变类,对它的任何操作都会产生一个新的对象。

画个图了解一下:
在这里插入图片描述

分析:
第一行 创建了字符串 程序员控,开辟内存空间,产生一个地址值0x7788,赋值给引用tt,tt指向常量池中的字符串对象。

第二行 调用方法test 将实参传递给形参,形参拷贝了一份实参的地址值作为副本,方法内中声明了str引用,同时指向了常量池对象。

当执行str="关注公众号"语句时并没有改变str所指向字符串的值,str指向了另外一个String类的对象,对象内容为关注公众号。

关键就在 原来的字符串程序员控还存在内存中,没有改变。

综上所述:
Java里方法的参数传递方式只有一种:值传递。即将实际参数值传递给形式参数,形式参数拷贝了一份实际参数的地址值,也就是两个公用了一个地址值,所以一个引用修改了对象的属性内容,另一个引用调用该属性是被修改过的。

05 再来两道题练练手

第一道面试题:

public class ValueTest {
    public void change(String str,char[] ch){
        str = "test ok";
        ch[0] = 'g';
    }

    public static void main(String[] args) {
        String str = new String("good");
        char[] ch = {'a','b','c'};
        ValueTest v = new ValueTest();
        v.change(str,ch);
        System.out.println(str+"and");
        System.out.println(ch);
    }
}

图解:
在这里插入图片描述
分析:
调用change方法,将实参传递个形参,形参拷贝了一份实参的地址值作为副本,虽然方法内中的引用str也指向字符串内容为good的对象,但是由于String类不可变,对它的任何操作都会产生一个新的对象,所以str并没有改变内存中的值,而是指向了新的一个字符串对象 test ok ,而对于字符数组来它不具有不可变特性,所以ch引用指向的对象其中索引为0的字符修改为 ‘g’ ,结束change方法后,出栈。垃圾回收清除。

main方法中的引用str指向字符串good也就没有被改变。

程序结果:

goodand
gbc

第二题:(摘自尚硅谷 值传递机制的练习题)
代码如下:

public class TransferTest {
    public static void main(String[] args) {
        first();
    }
    public static void first() {
        int i = 5;
        Value v = new Value();
        v. i = 25;
        second(v,i);
        System.out.println(v.i);
    }
    public static void second(Value v, int i) {
        i = 0;
        v.i = 20;
        Value val = new Value();
        v = val;
        System.out.println(v.i+" "+i);
    }

}
class Value {
    int i = 15;
}

图解:
在这里插入图片描述

这道题就是方法嵌套了方法,逻辑一样。自己可以动手画一画。

程序结果:

15 0
20

如果这篇文章帮助到了你,记得三连哦~~

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值