Java中的赋值与引用
该课堂笔记为华东师范大学在mooc网上的Java核心技术课程学习笔记。1
在Java中,如果对基本类型进行赋值,实际上是直接值拷贝的;如果对对象进行赋值,实际上是引用赋值,这个引用赋值可以理解为C/C++中的指针。
举两个例子,
赋值
新建两个变量和两个对象,然后对其中一个变量初始化,另一个变量和初始化后的变量相同。即:
对于数值型,a初始化为10, b = a;
对于对象型,obj1中的num初始化为5, obj2 = obj1;
package chapter4;
public class ComparedObjectWithVariable {
public static void main(String[] args) {
int a = 10, b;
b = a;
System.out.println("before: a is " + a + ", and b is " + b);
b = 20;
System.out.println("after: a is " + a + ", and b is " + b);
MyNumber obj1 = new MyNumber();
MyNumber obj2 = new MyNumber();
obj1.num = 5;
obj2 = obj1;
System.out.println("before: obj1.num is " + obj1.num + ", and obj2.num is " + obj2.num);
obj2.num = 20;
System.out.println("after: obj1.num is " + obj1.num + ", and obj2.num is " + obj2.num);
}
}
MyNumber
这个类如下:
package chapter4;
public class MyNumber {
int num = 5;
}
此时,输出结果为:
before: a is 10, and b is 10
after: a is 10, and b is 20
before: obj1.num is 5, and obj2.num is 5
after: obj1.num is 20, and obj2.num is 20
从结果中可以看出:
1、数值型变量重新赋值时,不改变原来变量a的数值。这是因为对数值型变量赋值时,直接拷贝,重新开辟内存空间的过程。因此,输出为一个5, 一个 10。
2、类初始化的时候,初始状态如下:
然后obj1 = obj2
,此时,相当于将 obj1 的指针指向 obj2,即两者共用一个内存空间。
此时,将 obj2.num
赋值为 20, 即为两者的值都改变了。
变量交换
MyNumber
类的代码和赋值章节中的代码一致。变量交换代码如下:
package chapter4;
public class ArgumentPassing {
public static void swap(int m, int n) {
System.out.println("before: m is " + m + ", and n is " + n);
int s = m;
m = n;
n = s;
System.out.println("after: m is " + m + ", and n is " + n);
}
public static void swap(MyNumber obj3, MyNumber obj4) {
int m = obj3.num;
obj3.num = obj4.num;
obj4.num = m;
}
public static void main(String[] args) {
int a = 10, b = 5;
System.out.println("before: a is " + a + ", and b is " + b);
swap(a, b);
System.out.println("after: a is " + a + ", and b is " + b);
MyNumber obj1 = new MyNumber();
MyNumber obj2 = new MyNumber();
obj2.num = 10;
System.out.println("before: obj1.num is " + obj1.num + ", and obj2.num is " + obj2.num);
swap(obj1, obj2);
System.out.println("after: obj1.num is " + obj1.num + ", and obj2.num is " + obj2.num);
}
}
该代码运行结果如下:
before: a is 10, and b is 5
before: m is 10, and n is 5
after: m is 5, and n is 10
after: a is 10, and b is 5
before: obj1.num is 5, and obj2.num is 10
after: obj1.num is 10, and obj2.num is 5
发现交换前后,对于数值型变量来说,交换并没有成功;而对于对象的数值型交换成功了。这是为什么呢?
是这个样子的,
- 对于数值型变量赋值,之前说了是直接赋值的,那么经过
swap
函数之后呢,在内存空间里是这个样子的:
故而在变量交换前后,是指m和n的数值交换,但是对于a和b来说,交换前后输出的值不变。 - 对于对象来说,对象的赋值是赋值指针,那么,交换开始时四个对象在内存空间中分布如下:
obj1
和obj3
指向同一个内存,obj2
和obj4
指向同一个内存。
在交换完成后,交换的是obj3
和obj4
的num
数值。但是因为obj1
和obj3
指向同一个内存,obj2
和obj4
指向同一个内存,故而,obj1
和obj2
的num
数值也完成了交换。
对象的生成
对象如何建立
产生一个对象:A obj = new A();
99%的情况下是通过关键字 new
来进行建立,1% 的情况下是用克隆和反射生成。
new 出一个对象后的默认值
对于数值型的对象默认值为0
(或者0.0
),对于布尔变量,默认为 false
, 对于 char
变量,默认为 \u0000
。类的成员变量都是有默认值的,函数中的临时变量必须要初始化(没有默认值)。
构造函数
作用
作用:是指在类初始化的时候就给类的成员变量赋值。
举个例子,
package chapter4;
public class ConstructorFunction {
int id;
public ConstructorFunction(int id2) {
id = id2;
}
public static void main(String[] args) {
ConstructorFunction constructorFunction = new ConstructorFunction(10);
System.out.println(constructorFunction.id);
}
}
此时,ConstructorFunction
这个构造函数就对该类成员变量id
赋值了,上述代码输出为 10
。
构造函数注意点
对于构造函数来说,有以下几点需要注意:
Java中没有析构函数,那么变量消亡的时候,如何回收内存的呢?这是因为Java有个内存自动回收机制。对象回收的效率依赖于垃圾回收器(GC),其回收算法关系到性能好坏。
另外,
- 对于一个类来说,可以有多个构造函数,只要形参列表不相同就好。【这个类似于函数重载(over load),函数重载是指函数名相同,形参不同】
- 在
new
对象的时候,根据实参的不同自动挑选相应的构造函数。如果实参形参匹配不上,则会报错。
举个例子,
package chapter4;
public class MyPairNumber {
int m;
int n;
public MyPairNumber() {
}
public MyPairNumber(int a) {
m = a;
}
public MyPairNumber(int a, int b) {
m = a;
n = b;
}
public static void main(String[] args) {
MyPairNumber myPairNumber1 = new MyPairNumber();
MyPairNumber myPairNumber2 = new MyPairNumber(10);
MyPairNumber myPairNumber3 = new MyPairNumber(10, 20);
// MyPairNumber myPairNumber4 = new MyPairNumber(10, 20, 30); // error
System.out.println("myPairNumber1 has " + myPairNumber1.m + "," + myPairNumber1.n);
System.out.println("myPairNumber2 has " + myPairNumber2.m + "," + myPairNumber2.n);
System.out.println("myPairNumber3 has " + myPairNumber3.m + "," + myPairNumber3.n);
}
}
输出结果如下:
myPairNumber1 has 0,0
myPairNumber2 has 10,0
myPairNumber3 has 10,20
信息隐藏和this
信息隐藏
在Intellij中如何自动生成 get/set 方法:
1、右键点击Generat(或者mac电脑用 command + N)
2、选择Getter/Setter方法
3、选择需要产生的get/set方法(如果需要选择多个,可以按住shift键)
举个例子,
package chapter4;
public class Person {
private int age;
private int height;
private int name;
private int weight;
public void setAge(int age) {
this.age = age;
}
public void setHeight(int height) {
this.height = height;
}
public void setName(int name) {
this.name = name;
}
public void setWeight(int weight) {
this.weight = weight;
}
public int getAge() {
return age;
}
public int getHeight() {
return height;
}
public int getName() {
return name;
}
public int getWeight() {
return weight;
}
}
this
形参的id
优先级更高一些,this
指向本类中的成员变量。因此,函数内部的id
指的是形参id
,this
指向的是本类中的成员变量id
。在上述例子中靠左边的 id = id2
中id
在本类成员变量中只有一个id
变量,故而省略了this
,将 this.id = id2
写成 id = id2
。
举个例子,
package chapter4;
public class MyPairNumber {
int m;
int n;
public MyPairNumber() {
this.n = 0;
this.m = 0;
}
public MyPairNumber(int a) {
this(a, 0);
}
public MyPairNumber(int a, int b) {
this.m = a;
this.n = b;
}
public static void main(String[] args) {
MyPairNumber myPairNumber1 = new MyPairNumber();
MyPairNumber myPairNumber2 = new MyPairNumber(10);
MyPairNumber myPairNumber3 = new MyPairNumber(10, 20);
// MyPairNumber myPairNumber4 = new MyPairNumber(10, 20, 30); // error
System.out.println("myPairNumber1 has " + myPairNumber1.m + "," + myPairNumber1.n);
System.out.println("myPairNumber2 has " + myPairNumber2.m + "," + myPairNumber2.n);
System.out.println("myPairNumber3 has " + myPairNumber3.m + "," + myPairNumber3.n);
}
}
上述代码中,
public MyPairNumber() {
this.n = 0;
this.m = 0;
}
即表示this
可以表示本类中的临时变量。
public MyPairNumber(int a) {
this(a, 0);
}
即表示this
可以表示本类中的构造函数MyPairNumber(int a, int b)
。
输出结果如下:
myPairNumber1 has 0,0
myPairNumber2 has 10,0
myPairNumber3 has 10,20