基本概念
值传递:传递对象的一个副本,即使副本被改变,也不会影响源对象,因为值传递的时候,实际上是将实参的值复制一份给形参。
引用传递:传递的并不是实际的对象,而是对象的引用,外部对引用对象的改变也会反映到源对象上,因为引用传递的时候,实际上是将实参的地址值复制一份给形参。
Java的函数传参方式都是值传递,唯一区别是形参是基本数据类型,还是引用数据类型,而这两种数据类型造成在函数体中修改了参数的额值,在函数体外是否跟着发生改变,根本原因是,两种数据类型变量在内存中存储的方式不一样,基本类型的变量保存原始值,即它代表的值就是数值本身;而引用类型的变量保存的是引用值,"引用值"指向内存空间的地址,代表了某个对象的引用,而不是对象本身,对象本身存放在这个引用值所表示的地址的位置。即基本类型的变量直接保存在函数调用栈中,而对象的变量的值其实是保存在堆中,然后将这个对象在堆中的值的地址引用存放到函数调用栈中。
————————————————
版权声明:本文为CSDN博主「凌凌小博客」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_35923749/article/details/79703700
基本数据类型(byte/short/int/long/float/double/char/boolean)
public static void main(String[] args) {
int a = 10,b=20;
exchange(a,b);
System.out.println("main: "+"a: "+a+" b: "+b);
// a:10;b:20
}
static void exchange(int a,int b){
int c = a;
a = b;
b= c;
System.out.println("exchange: "+"a: "+a+" b: "+b);
//a:20;b:10
}
引用数据类型(String,类,数组)
String
public static void main(String[] args) {
String a = "123";
exchange(a);
System.out.println("main: "+"a: "+a);
/**
* a = "456";
* a的值不会发生改变;a的引用会在栈内存中,引用的地址指向静态方法区的常量池中(先判断常量池中是否有‘456’,如果常量池中有‘456’直接引用)
* a = new String("456");
* a的值不会发生改变;a的引用会在栈内存中,引用指向的对象存在于堆内存当中(每new一次,便会产生一个新的‘456’对象)
*/
}
static void exchange(String a){
// a = "456"; a的值未改变 1
// a = new String("456"); a的值未改变 2
/**
* 在未赋值之前,exchange中的a和main中的a都的引用都指向堆内存中的同一对象‘123’,
* 赋值开始,系统会创建一个新的‘456’堆内存对象(假设堆里没有这个对象,2会创建一个新的,有的话1会直接引用)
* 然后让a的引用指向这个地址,此时exchange中的a和main中的a会变成两个截然不同的对象,分别指向不同的引用。
*/
}
字符串是一个特殊的数据类型,它的底层是一个final 型的ch[]数组,属于无法更改,所以字符串在作为参数传递时,可以当做一个特殊的数组进行操作,同样的它也是将复制一份原本的对象引用给了副本,此时副本对象的引用和原本对象的引用都指向原始字符串的位置,也就是exchange中的a和在刚开始初始化时它指向的地址和原对象main中的a指向的位置一致,即exchange中的a的初始hashcode值和原对象main中的a的hashcode值一样,exchange中的a经过a=“456”操作后,由于字符串的不可变性,此时会exchange中的a一个新的对象引用,即此时exchange中的a指向“456”的位置。exchange中的a的hashcode值会变化,但是原本main中的a它的对象引用没有发生改变,并且“123”也未发生改变,所以main中的a任然指向”123”。
StringBuffer
public static void main(String[] args) {
// String a = "123";
StringBuffer a = new StringBuffer("123");
exchange(a);
System.out.println("main: "+"a: "+a);
}
static void exchange(StringBuffer a){
// a = new StringBuffer("456"); //a的值未改变 1
// a.append("456"); //a的值发生改变 2
/**
* 1:原理同String一样,创建了新的对象
* 2:append方法和insert方法都是对原字符串直接操作,所以会改变a的值
*
*/
}
数组
public static void main(String[] args) {
int a[] = {1,2,3};
exchange(a);
System.out.println(a[0]); //a[0]的值会发生改变,传递的是数组的引用
}
static void exchange(int a[]){
a[0] = 100;
}
对象
public static void main(String[] args) {
User a = new User(1, "tom");
exchange(a);
System.out.println(a.toString());
}
static void exchange(User a){
// a = new User(1,"jack"); //值未变,a重新指向了堆内存当中的一个新对象
// a.setName("jack"); // 值改变,直接对原来的对象进行操作,所以发生改变
}
参考博客:
- https://blog.csdn.net/SummerOfFoam/article/details/109570841
- https://www.cnblogs.com/zwwhnly/p/10826530.html
- https://blog.csdn.net/weixin_43030796/article/details/81974190
- https://blog.csdn.net/weixin_48510456/article/details/107869715