案例一:
public class Solution { private int a; private int b; public void setA(int a) { this.a = a; } public void setB(int b) { this.b = b; } }
Solution s1 = new Solution(); s1.setA(1); s1.setB(2); Solution s2 = s1; s2.setA(-1);
Ps1:对于Solution s1 = new Solution();这条语句,这条语句执行的动作是创建一个对象,我们都很明白,但是它确包含了四个步骤:
右边“new Solution”,表示以Solution类为模板,在堆空间中创建一个Solution类对象;
“()”,这对括号表示,在对象创建后,会立马调用Solution类的构造函数,由于没有给参数,所以会调用默认的无参构造。
左边的“Solution s1 ”创建了一个Solution类的引用变量,也就是说用来指向Solution对象的对象引用。这和C语言中的指针可以理解为一个意思。
“=”,这个等号操作符表示使对象引用s1指向刚创建的Solution对象。
Ps2:为了形象地说明对象、引用及它们之间的关系,可以做一个比喻:
A有一栋房子,他把地址告诉了B,B就知道了A家的地址,然后B又把A家的地址告诉了C。此时,B和C都知道了A家的地址,但是这栋房子确是属于A的。如果此时,B去A家拿了一个东西,然后C去了后会发现少了一样东西。所以B和C只是知道A家的地址,可以随时访问,但是确不是独有的。
Ps3:两个结论:
(1)一个对象引用可以指向0个或1个对象(一个人可以知道房子的地址,也可以不知道)。
(2)一个对象可以有N个引用指向它(可以有N个人知道房子的地址)。
再理解了对象和引用的关系后,再来看参数传递。程序设计语言中,将参数传递给方法(或函数)有两种方法。按值传递(call by value)表示方法接受的是调用者提供的值;按引用调用(call by reference)表示方法接受的是调用者提供的变量地址。Java程序设计语言都是采用按值传递。【但是Java只有一种参数传递方式:那就是按值传递】,即Java中传递任何东西都是传值。如果传入方法的是基本类型的东西,你就得到此基本类型的一份拷贝。如果是传递引用,就得到引用的拷贝。
案例二:
/
public class Solution { // 基本类型的参数传递 public static void fun1(int m){ m = 100; } // 参数为对象,不改变引用的值 public static void fun2(StringBuffer s){ s.append("fun2"); } // 参数为对象,改变引用的值 public static void fun3(StringBuffer s){ s = new StringBuffer("fun3"); } public static void main(String[] args) { int i = 1; fun1(i); System.out.println(i);// i = 1 StringBuffer ss = new StringBuffer("main"); System.out.println(ss.toString());// main fun2(ss); System.out.println(ss.toString());// mainfun2 fun3(ss); System.out.println(ss.toString());// mainfun2 } }
Ps:以上结果中:
fun1方法的参数是基本类型,尽管在 fun1 中参数 m 的值发生了改变,但是并不影响 i。
fun2方法的参数是一个对象,当把ss传给参数s时,s得到的是ss的拷贝,所以s和ss指向的是同一个对象,因此,使用s操作对象,ss也会受影响。
fun3方法的参数虽然也是一个对象,当ss传给参数s时,s得到的是ss的拷贝,但是,在fun3中改变了s指向的对象,给s重新赋值后,s与ss已经没有关系,它和ss指向了不同的对象,所以不管对s做什么操作,ss都不会受影响。
案例三:
public class ParamTest { public static void main(String[] args) { /* *Test1: Methods can't modify numeric parameters */ System.out.println("Testing tripleValue:"); double percent = 10; System.out.println("Before: percent=" + percent); tripleValue(percent); System.out.println("After: percent=" + percent); /* *Test2: Methods can change the state of object parameters */ System.out.println("\nTesting tripleSalary"); Employee harry = new Employee("Harry", 50000); System.out.println("Before: salary=" + harry.getSalary()); tripleSalary(harry); System.out.println("After: salary=" + harry.getSalary()); /* *Test3: Methods can't attach new objects to object parameters */ System.out.println("\nTesting swap"); Employee a = new Employee("Alice", 30000); Employee b = new Employee("Bob", 60000); System.out.println("Before: a=" + a.getName()); System.out.println("Before: b=" + b.getName()); swap(a, b); System.out.println("After: a=" + a.getName()); System.out.println("After: b=" + b.getName()); } public static void tripleValue(double x) { x *= 3; System.out.println("End of method: x=" + x); } public static void tripleSalary(Employee x) { x.raiseSalary(200); System.out.println("End of method: salary=" + x.getSalary()); } public static void swap(Employee x, Employee y) { Employee temp = x; x = y; y = temp; System.out.println("End of method: x=" + x.getName()); System.out.println("End of method: y=" + y.getName()); } } class Employee { private String name; private double salary; public Employee(){} public Employee(String name, double salary){ this.name = name; this.salary = salary; } public String getName() { return name; } public double getSalary() { return salary; } public void raiseSalary(double byPercent){ double raise = salary * byPercent / 100; salary += raise; } }
Ps:从以上例题可以总结Java中方法参数的使用情况:
- 一个方法不能修改一个基本数据类型的参数(即数值型或布尔型)。
- 一个方法可以改变一个对象(数组)参数的状态。
- 一个方法不能让对象参数(数组)引用一个新的对象。