java其实只有栈变量的值传递(深度理解参数传递)

1.JVM内存区域的简单划分

在这里插入图片描述

2.基本数据类型与引用数据类型
int num = 10;
double dec = 1.23;
  • 上述基本类型在内存中是这样的:
    在这里插入图片描述
int[][] twoDim = new int[][]{{6, 6, 6},
							 {7, 7, 7}};
  • 上述表达式完全合法,你可以去试试。
  • 数组也是引用类型,以二维数组为例查看引用类型在内存中的状态:
    在这里插入图片描述
3.现在我们来传参数
public static void main(String[] args){
int num = 10;
double dec = 1.23;
plus(num, dec);
}//main方法结束

public static void plus(int num, double dec){
num++;
dec--;
}//plus方法结束
  • 上述传递参数的过程是这样的:
    在这里插入图片描述
  • 由于传递全部发生在栈内,栈变量的变量名与数值是一对一的关系,一个改变与另一个无关,传递的仅仅是栈变量存储的值。
public static void main(String[] args){
int[][] twoDim = new int[][]{{6, 6, 6},
							 {7, 7, 7}};
pass(twoDim);
}//main结束

public static void pass(int[][] twoDim){
twoDim[0][0] = 0;
twoDim = null;
}//pass方法结束
  • 上述引用类型传递参数是这样的:
    在这里插入图片描述
  • 这个所谓的“地址传递”,本质上也是栈变量的值传递,与基本数据类型的值传递是相通的机制,区分开反而加深了误解。
  • 应该区分的是栈变量堆对象,引用类型的数据都存放在堆中,栈内存放的是对堆数据的引用。本质上,是数学“一对一”和“一对多”的问题,栈变量的变量名与其代表的数据一 一对应,都是相互独立的,任何一个改变都不影响其它。引用类型的数据空间在堆,数据引用在栈,并且一个堆数据可以对应多个栈引用,这多个栈引用都可以访问堆数据,无论是谁顺着地址去改变堆数据的内容,其它栈引用都能感知到这个变化,请注意,栈引用本身也是一个栈变量,如果仅仅对这个栈变量本身进行改变,而不顺着地址去改变堆内容,那么就不会影响其它栈引用,比如array = null ; 就是一个只针对引用本身的改变。
4.我们来看看String的传递
  • 有人说,String是个特例,先来看看
public static void main(String[] args) {

		String string = "我会改变吗?";
		System.out.println(string);//我会改变吗?

		MethodParameter thisClass = new MethodParameter();
		thisClass.changeString(string);
		
		System.out.println(string);//我会改变吗?
	}//main方法结束
	public void changeString(String string){
		string = "我改变了!";
		System.out.println("这是changeString内的输出:" + string);//我改变了!
	}
//以下是输出结果
我会改变吗?
这是changeString内的输出:我改变了!
我会改变吗?
  • 有人以类似的例子来作为String是例外的证据,但这是理解偏差导致的
  • String真正的特殊之处在于:
    任何双引号包起来的字符串都是一个String对象,而不是一个值,“我是String对象”,这些对象保存在字符串常量池中。
  • 因此,方法内的这句话:
public void changeString(String string){
		string = "我改变了!";
}

只是把本方法的栈变量string原本指向"我会改变吗?"的地址,改成了指向"我改变了!"的地址,但是并没有沿着原本的地址去改变原本的数据空间的内容,因此main方法内的string根本就不会改变。

  • 事实上,要想顺着地址去改变,只能在方法中这样写:
public void changeString(String string){
		char[] newString = {'我', '改', '变', '了', '!'};
 		string.value = newString;
 		//这个 .(点) 才叫顺着地址去改变数据空间的内容
}
  • 但是上面能成功吗?我们来瞅一眼String的源码:
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];
//		……
}
  • 看到final你就该明白,上述顺着地址去改变内容的行为是会报错的,因为被final修饰的变量一经赋值便不能再改变。
  • 因此,如果不断地使用字符串,那就会不断地消耗存储空间,因为每次都是一个final修饰,而且,字符串又是最常用的引用类型,一个较大的程序很快就会因为内存耗完而崩溃;正是基于此需求,字符串常量池技术才应运而生,相同的字符串,如果不是显性要求重开数据空间,都是会保留一个对象数据空间,保留多个对该对象的引用,从而大大减少内存的消耗。
5.写在最后

画图不易,梳理更难,如果对你有帮助,请给个赞,谢谢^ V ^

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值