以下是本文的目录大纲:

  一.形参和实参简述

  二.基本数据类型变量的传值

  三.引用简述

  四.引用的实例说明

  五.总结

说到java中调用函数的参数引用,就要先说一下形参和实参,以便于更好地理解参数引用。

一.形参和实参简述

形参

  • parameter:形式参数(形参)
  • 形参出现在函数定义中,在整个函数体内都可以使用,离开该函数则不能使用

实参

  • argument:实际参数(实参)
  • 实参拥有明确的值,出现在主调函数中,值传给被调函数的形参

语法和语义

  • syntax:语法:词语的用法
  • semantic:语义:词语或语句的含义

举例一个语言的例子说明语法和语义:

1) “Lua java的是编写”,这句子你一定看不懂,因为它语法错误,不符合汉语的语法。

2) “Lua是Java编写的”,这句子语法正确,但是语义错误。

3) “Lua是C编写的”, 这句子,语法和语义都对。

功能

  • 形参和实参的功能是用于函数调用时的数据传送。

区别

  • 因为形参只有在函数内部有效,所以形参变量只有在被调用时才分配内存单元,在调用结束即刻释放所分配的内存单元。
  • 实参和形参在数量上,类型上,顺序上应严格一致, 否则会发生“类型不匹配”的错误。
  • 实参可以是常量、变量、表达式、函数等, 无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值, 以便把这些值传送给形参。 因此应预先用赋值,输入等办法使实参获得确定值。
  • 当形参和实参不是指针类型时,在该函数运行时,形参和实参是不同的变量,他们在内存中位于不同的位置,形参将实参的内容复制一份,在该函数运行结束的时候形参被释放,而实参内容不会改变。
  • 如果函数的参数是指针类型变量,在调用该函数的过程中,传给函数的是实参的地址,在函数体内部使用的也是实参的地址,即使用的就是实参本身。所以在函数体内部可以改变实参的值。

二.基本数据类型变量的传值

public class TestParam {
	public TestParam() {
		// 基本数据类型的测试
		int tempParam = 1;
		boolean flag = true;
		System.out.println("********原始值(实参值)********");
		System.out.println("tempParam=:" + tempParam);
		System.out.println("flag=:" + flag);
		System.out.println("********调用方法时(形参值)********");
		increseParam(tempParam);
		updateBoolean(flag);
		System.out.println("********调用方法后(实参值)********");
		System.out.println("tempParam=:" + tempParam);
		System.out.println("flag=:" + flag);
	}

	public int increseParam(int tempParam) {
		tempParam++;
		System.out.println("increseParam方法=:" + tempParam);
		return tempParam;
	}

	public boolean updateBoolean(boolean flag) {
		flag = !flag;
		System.out.println("updateBoolean方法=:" + flag);
		return flag;
	}

	public static void main(String[] args) {
		new TestParam();
	}
}

运行结果: 

********原始值(实参值)********
tempParam=:1
flag=:true
********调用方法时(形参值)********
increseParam方法=:2
updateBoolean方法=:false
********调用方法后(实参值)********
tempParam=:1
flag=:true

从以上结果可看出,虽然在 updateBoolean(boolean) 方法中改变了传进来的参数的值,但对这个参数源变量本身并没有影响,即对TestParam()方法里的 flag变量没有影响。以参数形式传递基本数据类型的变量时,实际上是将参数的值作了一个拷贝传进方法函数的,那么在方法函数里再怎么改变其值,其结果都是只改变了拷贝的值,而不是源值。从而说明,参数类型是基本数据类型的时候,形参不会改变实参的值,而且是按实际值传递的。

三.引用简述

Java 是传值还是传引用,问题主要出在对象的传递上,因为 Java中基本数据类型没有引用。

简单的说,引用其实就像是一个对象的名字或者别名(alias),一个对象在内存中会请求一块空间来保存数据,根据对象的大小,它可能需要占用的空间大小也不等。访问对象的时候,我们不会直接是访问对象在内存中的数据,而是通过引用去访问。引用也是一种数据类型,我们可以把它想象为类似C 语言中指针的东西,它指示了对象在内存中的地址——只不过我们不能够观察到这个地址究竟是什么。

综上所述,可以得到一下观点:

  • 引用是一种数据类型,保存了对象在内存中的地址,这种类型即不是我们平时所说的基本数据类型也不是类实例(对象);
  • 不同的引用可能指向同一个对象,换句话说,一个对象可以有多个引用,即该类类型的变量。比如 String a = "Hello";
    String b = a;
    这里,a 和 b 是不同的两个引用,我们使用了两个定义语句来定义它们。但它们的值是一样的,都指向同一个对象"Hello",这就是不同的两个引用指向了共同的对象。

四.引用的实例说明

  1. String类型
public class TestParam {

	public TestParam() {
		// 基本数据类型的测试
		String tempString = "TEST";
		System.out.println("********原始值(实参值)********");
		System.out.println("tempParam=:" + tempString);
		System.out.println("tempString的哈希值:" + tempString.hashCode());
		System.out.println("********调用方法时(形参值)********");
		updateString(tempString);
		System.out.println("********调用方法后(实参值)********");
		System.out.println("tempParam=:" + tempString);
	}

	public String updateString(String tempStr) {
		System.out.println("修改前tempStr的哈希值:" + tempStr.hashCode());
		tempStr = tempStr + "_APPEND";
		System.out.println("updateString方法" + tempStr);
		System.out.println("修改后tempStr的哈希值:" + tempStr.hashCode());
		return tempStr;
	}

	public static void main(String[] args) {
		new TestParam();
	}
}

运行结果:

********原始值(实参值)********
tempParam=:TEST
tempString的哈希值:2571410
********调用方法时(形参值)********
修改前tempStr的哈希值:2571410
updateString方法TEST_APPEND
修改后tempStr的哈希值:-271541401
********调用方法后(实参值)********
tempParam=:TEST

从以上结果可看出,虽然在 updateString(String) 方法中改变了传进来的参数的值,但对这个参数源变量本身并没有影响,即对TestParam()方法里的 tempString变量没有影响。为什么呢,因为修改前实参和形参的哈希值一样,而修改后形参的哈希值已经改变,所以tempStr只是改变了引用,而没有改变对象的值。那说明,参数类型是String类型的时候,形参不会改变实参的值,而且是按对象的地址传递的。这里看不明白的朋友,可以看看这个链接,有更详细的讲解:

  1. StringBuffer类型
public class TestParam {

	public TestParam() {
		// 基本数据类型的测试
		StringBuffer string = new StringBuffer("Hello");
		System.out.println("********原始值(实参值)********");
		System.out.println("tempString=:" + string);
		System.out.println("********调用方法时(形参值)********");
		updateStringBuffer(string);
		System.out.println("********调用方法后(实参值)********");
		System.out.println("tempString=:" + string);
	}

	public static void updateStringBuffer(StringBuffer str) {
		str.append(", World!");
		System.out.println("updateStringBuffer方法:" + str);
	}

	public static void main(String[] args) {
		new TestParam();
	}
}

运行结果:

********原始值(实参值)********
tempString=:Hello
********调用方法时(形参值)********
updateStringBuffer方法:Hello, World!
********调用方法后(实参值)********
tempString=:Hello, World!

从以上结果可看出,在 updateStringBuffer(StringBuffer) 方法中改变了传进来的参数的值,而且改变了参数源变量本身的值,即对TestParam()方法里的 string变量有影响。因为StringBuffer类型的引用地址是无法改变的,所以str只是改变了引用地址。那说明,参数类型是StringBuffer类型的时候,形参会改变实参的值,而且是按对象的地址传递的。

  1. ArrayList<T>类型
import java.util.ArrayList;

public class TestParam {

	public TestParam() {

		ArrayList<String> list = new ArrayList<String>();
		list.add("AAAAA");
		list.add("BBBBB");
		list.add("CCCCC");
		System.out.println("********原始值(实参值)********");
		System.out.println("size1=:" + list.size());
		System.out.println("********调用方法时(形参值)********");
		clearList(list);
		System.out.println("********调用方法后(实参值)********");
		System.out.println("size1=:" + list.size());
		System.out.println();
		System.out.println("**********************************");
		ArrayList<String> list2 = new ArrayList<String>();
		list2.add("AAAAA");
		list2.add("BBBBB");
		list2.add("CCCCC");
		System.out.println("********原始值(实参值)********");
		System.out.println("size2=:" + list2.size());
		System.out.println("********调用方法时(形参值)********");
		newList(list2);
		System.out.println("********调用方法后(实参值)********");
		System.out.println("size2=:" + list2.size());
	}

	public void clearList(ArrayList<String> claerlist) {
		claerlist.clear();
		System.out.println("clearList=:" + claerlist.size());
	}

	public void newList(ArrayList<String> newlist) {
		newlist = new ArrayList<String>();
		newlist.add("DDDDD");
		System.out.println("newList=:" + newlist.size());
	}

	public static void main(String[] args) {
		new TestParam();
	}
}

         运行结果:

********原始值(实参值)********
size1=:3
********调用方法时(形参值)********
clearList=:0
********调用方法后(实参值)********
size1=:0

**********************************
********原始值(实参值)********
size2=:3
********调用方法时(形参值)********
newList=:1
********调用方法后(实参值)********
size2=:3

从以上结果可看出,在 clearList(ArrayList<String>) 方法中改变了传进来的参数的值,而且改变了参数源变量本身的值,即对TestParam()方法里的 list变量有影响。由此看出ArrayList<String>类型的引用地址虽然没有改变,但是其引用对象的值已经发生了改变。而在 newList(ArrayList<String>) 方法中,通过newlist = new ArrayList<String>()首先改变了newlist的引用地址,再去改变newlist的值,所以对参数源变量没有影响。从而说明,参数类型是newList(ArrayList<String>)类型的时候,是按对象的地址传递的,形参不改变自身引用地址的情况下,会改变实参的值。

五.总结

 Java中,改变参数的值有两种情况:

第一种,使用赋值号“=”直接进行赋值使其改变,这种情况,其改变不会影响到方法该方法以外的数据,或者直接说源数据。如基本数据类型(传实际值)和String类型(传对象的地址)的赋值;

第二种,对于某些对象的引用(对象的地址),通过一定途径对其成员数据进行改变,这种方法,会影响到源数据——因为引用指示的对象没有变,对其成员数据进行改变则实质上是改变的该对象。如StringBuffer类型、ArrayList<T>类型、数组、Map的赋值。