终于明白了?java只有值传递,没有引用传递

参数类型:

  • 形参:方法被调用时需要传递进来参数,例如:public void run(int a)中的int a ,它只有run方法被调用间a才有意义,也就是被分配内存空间,在方法执行完毕后,方法出栈即被销毁释放内存空间,也就不存在了。
  • 实参:方法被调用时传递进来的实际值,它在方法被调用前就被初始化,并且在方法被调用时传入。例:成员变量和局部变量(方法内的局部变量除外)

值传递和引用传递:

  • 值传递:在方法被调用时,实参通过把他的内容副本传入方法内部,此时形参接收的内容是实参的拷贝。因此在方法内对实参的任何操作,都仅仅是对这个内容的副本进行操作,不影响原初始值的内容。值传递传递的是一个真实的内容副本,对副本的操作不影响原内容,也就是形参再怎么变化,也不影响实参对应的内容。在jvm内存中的说法也就是传递的堆地址。
  • 引用传递:“引用”也就是指向真实内容的地址值。在方法调用时,实参的地址通过放法调用传递给相应的形参,在方法体内,形参和实参指向同一块内存地址,对形参的操作会影响原来的内容。即传递的是栈地址。

java中不存在引用传递

看如下代码(基本类型参数的值传递):

public class Zcd {
	public static void main(String[] args) {
		int a=12;
		System.out.println("调用过程前a的值"+a);
		change(a);
		System.out.println("调用过程后a的值"+a);
	}

	private static void change(int a) {
		// TODO Auto-generated method stub
		a=100;
		System.out.println("调用过程中a的值"+a);
	}
}

输出结果:

 

我们发现值传递和我们上面所说一致,它是把实参复制一份通过形参传给方法,而这个方法改变的只是副本,无法对原来的数值改变。

如下图:值传递的jvm内存图,因为int a是局部变量,它只在方法区内它不占用堆内存也不在方法区内存放,方法出栈数据就会丢失,所以change方法结束后它会出栈,而且赋的值也根本不会占用内存,副本的值也不会对原数据产生改变。这就是基本数据类型的值传递

 

看如下代码(引用类型参数的值传递):

public class Valtransfer {
	public static void main(String[] args) {
		String name=new String("123");
		System.out.println(name);
		System.out.println("调用过程前堆地址"+name.hashCode());
		change(name);
		System.out.println(name);
		System.out.println("调用过程后堆地址"+name.hashCode());
	}

	private static void change(String name) {
		// TODO Auto-generated method stub
		name="lhs";
		System.out.println("调用过程中堆地址"+name.hashCode());
		System.out.println(name);
	}
}

结果:

 

我们发现值传递和我们上面所说不一致,String比较特殊,它的源代码是由final修饰的,你无法改变他原始的值,它是把实参复制一份通过形参传给方法,而这个方法改变的只是副本,并且和其他引用类型参数不同它改变了它的堆地址,所以也无法对原来的数值改变。

string源代码如下图

 

如下图:值传递的jvm内存图,change方法结束后它会出栈,又因为string源代码是final修饰,我们知道final修饰的引用类型固定的是一个堆地址,所以复制的那一份应该是开辟了新的堆地址,副本的值并不会对原数据产生改变。

 

那我们看这个代码:

public class Valtransfer {
	public static void main(String[] args) {
		StringBuffer a = new  StringBuffer("A");
		StringBuffer b = new  StringBuffer("B");
		change(a, b);
		System.out.println(a+","+b);
	}
	
	public static void change(StringBuffer x,StringBuffer y) {
		x.append(y);
		y = x;
	}
}

结果是:

会发现它会对原数据进行行了改变,那他就是引用传递吧?并不是,他还是值传递 ,java没有引用传递。

StringBuffer的append方法会对数据所在的内存进行字符串拼接,从而避免内存资源浪费,提高效率,从而导致了对原数据的改变。

还有一种看如下代码(引用类型参数的值传递)

public class Valtransfer {
	static String nameString;
	static int age;
	public Valtransfer(String name,int age) {
		this.nameString=name;
		this.age=age;
	}
	public static void main(String[] args) {
		Valtransfer a=new Valtransfer("qcby",26);
		System.out.println("调用过程前"+a.nameString);
		getName(a.nameString);
		System.out.println("调用后"+a.nameString);
	}
	
	public static void getName(String name) {
		nameString="gs";
		System.out.println("调用过程中"+nameString);
	}
}

结果为:

这时就会疑问,这总会是引用传递了吧?并不是,这是引用类型参数的值传递 。

我们看jvm内存图:

getName方法入栈后改变了静态常量池中的内容,之后它出栈,所以导致了内容的改变。它还是复制了一份副本,他们堆地址相同,栈地址不同。我们所说的对副本的更改对原本没有影响是指在入栈后方法内的数据,而对指向的堆和方法区内的更改是会对原数据进行更改的,这就是造成java有引用传递争论的原因,结论是没有值传递。

还有一种看如下代码(引用类型参数的值传递)

public class Valtransfer {
	String nameString;
	int age;
	public Valtransfer(String name,int age) {
		this.nameString=name;
		this.age=age;
	}
	public static void main(String[] args) {
		Valtransfer a=new Valtransfer("qcby",26);
		System.out.println("调用过程前"+a.nameString);
		System.out.println("调用过程前堆地址"+a.hashCode());
		getName(a);
		System.out.println("调用后"+a.nameString);
		System.out.println("调用过程后堆地址"+a.hashCode());
	}
	
	public static void getName(Valtransfer a) {
		a.nameString="gs";
		System.out.println("调用过程中"+a.nameString);
		System.out.println("调用过程中堆地址"+a.hashCode());
	}
}

有人就又会疑问了,形参里的是对象,那传递的应该是引用本身,这肯定是引用传递,我确实这样疑惑过,但仔细想想,其实还是值传递,因为他们指向同一个堆,所以改变堆内内容当然对原本有影响了,我们所说的没有影响是栈中的原数据,这里是对象a,副本a根本对原实参a无法造成影响,因为a是对象,所以你对对象a中数据的更改跟a没有关系,因为堆中的数据时共享的,很多人都会理解错这一点。 

结果如下图:

 

 还有这种值传递,如下图

 我们说的复制的堆地址上面形参中的jvm图是这样copy的:

上面我把多种值传递情况都列了出来,希望可以解决大家的疑惑 。

 

总结:java没有引用传递,值传递复制的是堆地址,是拷贝了一个副本。

我们所说的对副本的更改对原本没有影响是指在入栈后方法内的数据,而对指向的堆和方法区内的更改是会对原数据进行更改的,这就是造成java有引用传递争论的原因

参考资源链接:[OCJP 1z0-808 Java SE 8 Programmer I 试题答案详解与解析](https://wenku.csdn.net/doc/ckd5ija6w2?utm_source=wenku_answer2doc_content) Java SE 8编程中,理解接口实现和类继承对参数传递的影响是非常关键的。接口和类之间的关系直接影响着程序的行为和数据的传递方式。 首先,要明白Java中参数传递是基于值传递的。对于基本数据类型,传递的是值的副本;对于对象,传递的是引用(即对象在内存中的地址)的副本。因此,对象的方法调用会通过引用传递到对象本身。 接口在Java中是一组方法声明的集合,它允许类实现接口并提供这些方法的具体实现。当类实现接口时,它必须提供接口中所有方法的具体实现代码。如果接口的方法被调用,则执行的是类提供的实现代码。 继承则是面向对象编程中的一个基本特征,它允许一个类继承另一个类的属性和方法。子类继承父类的属性和方法后,可以在子类中重新定义或扩展这些方法。当父类的方法被调用时,如果子类没有覆盖该方法,则执行的是父类中的实现。 在处理接口和继承时,你需要注意方法重载和重写的区别。重载是同一个类中多个方法同名但参数类型或数量不同;而重写是指子类提供一个新的方法实现,来替换继承自父类的方法。 这些概念在OCJP 1z0-808考试中经常出现,例如在试题解析中所提到的,正确理解这些概念对于编写正确的代码和通过认证考试至关重要。为了深入理解这些概念,建议参考《OCJP 1z0-808 Java SE 8 Programmer I 试题答案详解与解析》一书。书中不仅解释了考试中的具体问题,还对Java编程中接口实现与类继承的关系进行了详细的阐述,是掌握Java核心概念的实用资源。 参考资源链接:[OCJP 1z0-808 Java SE 8 Programmer I 试题答案详解与解析](https://wenku.csdn.net/doc/ckd5ija6w2?utm_source=wenku_answer2doc_content)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sshm_666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值