// 先看案例一:
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()方法的空间,里面有:类名的引用Person。
2.Person指向方法区中Person类的静态方法区的地址。
3.调用静态方法区中的country,并输出。
注意:要想使用类中的成员,必须调用。通过什么调用?有:类名、this、super