您的位置:首页 > 编程语言 > Java开发

JAVA方法参数传递的是值还是引用

2017-08-14 23:56 417 查看
// 先看案例一:
publicclass
TestMain{
 
       public static
voidmain(String[] args) {
               List<Integer>list= new ArrayList<Integer>();
              for (int i=
0; i<
10; i++) {
                       list.add(i);
              }
               add(list);
              for (Integer j
:list) {
                       System.err.print(j+",");;
              }
               System.err.println("");
               System.err.println("*********************");
               String a="A";
               append(a);
               System.err.println(a);
              int num=5;
               addNum(num);
               System.err.println(num);
       }
       
       static
voidadd(List<Integer> list){
               list.add(100);
       }
       
       static
voidappend(String str){
               str+="is a";
       }
       static
voidaddNum(int a){
               a=a+10;
       }
}
打印出来的结果是:

0,1,2,3,4,5,6,7,8,9,100,

*********************

A

5

//  案例二

public class TestFinal {

/**
* @param args
*/
static int num1= 5;
int num4 = 0;
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
add(list);
for (Integer j : list) {
System.err.print(j+",");;
}
System.err.println("");
System.err.println("*********************");
String a="A";
append(a);
System.err.println(a);
//分析 static的加载时机问题
int num2 = 5;
addNum(num1);
addNum(num2);
//在类加载时加载,故这里打印  5,因为 addNun()调用是在类加载时进行的,加载时直接将 5传入方法中
System.err.println("-----------num1 = "+num1);//  5
System.err.println("-----------num2 = "+num2);//  5

//区分 下面情况
TestFinal t = new TestFinal();
int num3 = 5;
t.addNum3(num3);
//这里num3跟addNum3(num3)中的num3不是同一个变量,如果addNum3()有返回int值,那么这里就会
//返回重新赋值给原num3,这时就会打印 15
System.out.println("-----------num3 = "+num3+"-----------");// 5  
t.num4 = 5;
t.addNum3(t.num4);
System.out.println("-----------t.num4 = "+t.num4+"-----------");// 5
}

static void addNum(int a){
System.out.println("addNum : "+a);
a += 10;
System.out.println("addNum : "+a);
}

public void addNum3(int n){
System.out.println("addNum3 : "+n);
n += 10;
System.out.println("addNum3 : "+n);
}
static void add(List<Integer> list){
System.out.println("说明:static 加载时机");
list.add(100);
}

static void append(String str){
str+="is a";
}
/**
* 打印结果:
0,1,2,3,4,5,6,5
15
7,8,9,100,
*********************
A
-----------5
*/

}

分析原因:

参数传递基本上就是赋值操作
java中方法参数传递方式是按值传递。
如果参数是基本类型,传递的是基本类型的字面量值的拷贝。

如果参数是引用类型,传递的是该参量所引用的对象在堆中地址值的拷贝。

值传递与引用传递,在计算机领域是专有名词,如果你没有专门了解过,一般很难自行悟出其含义。而且在理解下面的解释时,请不要把任何概念往你所熟悉的语言功能上套。很容易产生误解。比如Reference,请当个全新的概念,它和C#引用类型中的引用,和C++的&,一点儿关系都没有。
 
值传递和引用传递,属于函数调用时参数的求值策略(Evaluation Strategy),这是对调用函数时,求值和传值的方式的描述,而非传递的内容的类型(内容指:是值类型还是引用类型,是值还是指针)。值类型/引用类型,是用于区分两种内存分配方式,值类型在调用栈上分配,引用类型在堆上分配。(不要问我引用类型里定义个值类型成员或反之会发生什么,这不在这个本文的讨论范畴内,而且你看完之后,你应该可以自己想明白)。一个描述内存分配方式,一个描述参数求值策略,两者之间无任何依赖或约束关系。

第一个例子:基本类型
void foo(int value) {
    value = 100;
}
foo(num); // num 没有被改变
 
第二个例子:没有提供改变自身方法的引用类型
void foo(String text) {
    text ="windows";
}
foo(str); // str 也没有被改变
 
第三个例子:提供了改变自身方法的引用类型
StringBuilder sb = new StringBuilder("iphone");
void foo(StringBuilder builder) {
   builder.append("4");
}
foo(sb); // sb 被改变了,变成了"iphone4"。
 
第四个例子:提供了改变自身方法的引用类型,但是不使用,而是使用赋值运算符。
StringBuilder sb = new StringBuilder("iphone");
void foo(StringBuilder builder) {
    builder = newStringBuilder("ipad");
}
foo(sb); // sb 没有被改变,还是 "iphone"。

”java程序设计语言总是采用值调用。也就是说,方法得到的是所有参数值的一个拷贝,特别是,方法不能修改传递给它的任何参数变量的内容。“

Java总是采用call by value
方法参数有2种类型:

1.基本数据类型(int,double,....)

2.对象引用

如果说你是call by reference 那么下面的代码将会交换A , B2个对象

void swap( test A , test B ) {

test C = A;

A = B;

B = C;

}

然而 你可以去试一下 并没有交换。 交换的只是拷贝出来的2个test对象。

总结来看
1.一个方法不能修改一个基本数据类型的参数

2.一个方法可以改变一个对象参数的状态

3.一个方法不能让对象参数引用一个新的对象

PS:来源Java 核心技术卷I

附上Java类加载机制部分会更容易理解

1.Personp=new Person("zhangsan",20);
该句话所做的事情:

1.在栈内存中,开辟main函数的空间,建立main函数的变量 p。

2.加载类文件:因为new要用到Person.class,所以要先从硬盘中找到Person.class类文件,并加载到内存中。
加载类文件时,除了非静态成员变量(对象的特有属性)不会被加载,其它的都会被加载。
记住:加载,是将类文件中的一行行内容存放到了内存当中,并不会执行任何语句。---->加载时期,即使有输出语句也不会执行。
静态成员变量(类变量)  ----->方法区的静态部分
静态方法             ----->方法区的静态部分
非静态方法(包括构造函数)  ----->方法区的非静态部分
静态代码块 ----->方法区的静态部分
构造代码块 ----->方法区的静态部分

注意:在Person.class文件加载时,静态方法和非静态方法都会加载到方法区中,只不过要调用到非静态方法时需要先实例化一个对象,对象才能调用非静态方法。如果让类中所有的非静态方法都随着对象的实例化而建立一次,那么会大量消耗内存资源,
所以才会让所有对象共享这些非静态方法,然后用this关键字指向调用非静态方法的对象。

3.执行类中的静态代码块:如果有的话,对Person.class类进行初始化。

4.开辟空间:在堆内存中开辟空间,分配内存地址。

5.默认初始化:在堆内存中建立对象的特有属性,并进行默认初始化。

6.显示初始化:对属性进行显示初始化。
7.构造代码块:执行类中的构造代码块,对对象进行构造代码块初始化。

8.构造函数初始化:对对象进行对应的构造函数初始化。

9.将内存地址赋值给栈内存中的变量p。

2.p.setName("lisi");

1.在栈内存中开辟setName方法的空间,里面有:对象的引用this,临时变量name

2.将p的值赋值给this,this就指向了堆中调用该方法的对象。

3.将"lisi"
赋值给临时变量name。

4.将临时变量的值赋值给this的name。

3.Person.showCountry();

1.在栈内存中,开辟showCountry(
b29e
)方法的空间,里面有:类名的引用Person。

2.Person指向方法区中Person类的静态方法区的地址。

3.调用静态方法区中的country,并输出。

  注意:要想使用类中的成员,必须调用。通过什么调用?有:类名、this、super
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java
相关文章推荐