今天来整理一篇常见的面试问题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
如果这篇文章帮助到了你,记得三连哦~~