Java笔记(1) - 变量值引用和拷贝


java 中 Object 的操作都是引用。原始数据类型操作都是值拷贝。

这里的操作就包括 赋值 和 函数参数传递


这里说明下 java 中的概念:

java中所有的类都隐形继承了 Obejct 类

java中数组也是 Object官方原话:


An object is a class instance or an array.


赋值


Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class C{
public int c_value = 100;
}

class B{
public int b_value = 1;
public C b_c_class = new C();
}

class Test {
public static void main(String[] args) {
//类赋值
B b = new B();
B bb = b;

//数组赋值
Integer[] i = {1};
Integer[] ii = i;
ii[0] = 100;

//Int 赋值
Integer i2 = 3;
Integer ii2 = i2;
ii2 = 101;
}
}

Debug显示:

b = {B@785}
   b_value = 1
   b_c_class = {C@790}
bb = {B@785}
    b_value = 1
    b_c_class = {C@790}

i = {Integer[1]@786}
    0 = {Integer@788} 100
ii = {Integer[1]@786}
    0 = {Integer@788} 100

i2 = {Integer@787}
    value = 3
ii2 = {Integer@789}
    value = 101


可以发现,只有 Integer 的赋值是重新开辟了一块内存来存放原数据。

其他的 Object 类型操作都是 引用赋值。即直接将内存地址的指针指向原地址,复用原地址的数据,而不是重新开辟内存。这样导致的结果就是:

a变量直接赋值给b变量,修改 b变量,a变量也会受到影响,因为他们的内存地址指针指向的是同一块内存地址。


那如何让 Object 赋值的时候重新开辟内存呢?Object 中提供了一个 clone() 方法


数组值拷贝


Demo:

1
2
3
Integer[] i = {1};
Integer[] ii = i.clone();
ii[0] = 100;

Debug显示:

i = {Integer[1]@782}
    0 = {Integer@785} 1
ii = {Integer[1]@783}
    0 = {Integer@784} 100


数组值拷贝,使用 clone() 方法即可


类拷贝


如果要拷贝的对象是我们自定义的类的话,是没法直接 clone的。需要 实现 Cloneable 接口,重写 clone() 方法,并且 catch 住 CloneNotSupportedException


浅拷贝


Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class C{
public int c_value = 100;
}

class B implements Cloneable{
public int b_value = 1;
public C b_c_class = new C();

@Override
public Object clone(){
B bClone = null;
try{
bClone = (B) super.clone();
}catch (CloneNotSupportedException e){
e.printStackTrace();
}
return bClone;
}
}

class Test {
public static void main(String[] args) {
B b = new B();
B bb = (B)b.clone();

bb.b_value = 200;
bb.b_c_class.c_value = 300;
}
}

Debug 显示:

b = {B@783}
    b_value = 1
   b_c_class = {C@785}
       c_value = 300
bb = {B@784}
    b_value = 200
    b_c_class = {C@785}
       c_value = 300


对象浅拷贝即:拷贝时目标变量确实重新拷贝了原对象的值,但是原对象中保存的其他对象依然只是单纯地把地址拷贝到目标变量,并不是重新拷贝一份。

即上面Demo中,b_c_class 保存的是对象C 的值。 bb 中保存的 b_c_class 指向的地址bb_c_class 指向的地址是同一块内存,所以修改 bb对象 中的 b_c_class,b对象也会受到牵连


深拷贝


为了让 bb对象 中的 b_c_class 重新拷贝一份 C对象。需要修改下 clone() 方法。

实现 C类 的clone()方法,然后在 B类 处手动 clone b_c_class


Demo:

line 27 处将 C对象 重新clone一份

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class C implements Cloneable{
public int c_value = 100;

@Override
public Object clone(){
C cClone = null;
try {
cClone = (C) super.clone();
}catch (CloneNotSupportedException e){
e.printStackTrace();
}
return cClone;
}
}

class B implements Cloneable{
public int b_value = 1;
public C b_c_class = new C();

@Override
public Object clone(){
B bClone = null;
try{
//clone Object B
bClone = (B) super.clone();
//clone Object C
bClone.b_c_class = (C) bClone.b_c_class.clone();
}catch (CloneNotSupportedException e){
e.printStackTrace();
}
return bClone;
}
}

class Test {
public static void main(String[] args) {
B b = new B();
B bb = (B)b.clone();
bb.b_value = 200;
bb.b_c_class.c_value = 300;
}
}


Debug显示:

b = {B@784}
    b_value = 1
    b_c_class = {C@787}
       c_value = 100
bb = {B@785}
    b_value = 200
    b_c_class = {C@786}
       c_value = 300


现在确实可以将 对象中的对象 进行拷贝了。但是如果对象的层数很多的话,就显得很麻烦了。

还可以通过序列化的方式来进行对象拷贝,这种方式更方便。


序列化的时候需要注意:被序列化的类需要实现 Serializable 接口


Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import java.io.*;


class C implements Cloneable, Serializable {
public int c_value = 100;
public Integer[] c_array = {20};
}

class B implements Cloneable, Serializable{
public int b_value = 1;
public C b_c_class = new C();

public B serialize_clone() {
B bClone = null;
try {

//序列化本类
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);

oos.writeObject(this);

ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
bClone = (B) ois.readObject();

} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return bClone;
}
}

class Test {
public static void main(String[] args) {
B b = new B();
B bb = b.serialize_clone();
bb.b_value = 200;
bb.b_c_class.c_value = 300;
bb.b_c_class.c_array[0] = 30;
}
}

Debug显示:

b = {B@945}
    b_value = 1
    b_c_class = {C@950}
       c_value = 100
       c_array = {Integer[1]@951}
          0 = {Integer@952} 20
bb = {B@946}
    b_value = 200
    b_c_class = {C@947}
       c_value = 300
       c_array = {Integer[1]@948}
          0 = {Integer@949} 30


Reference:

https://www.cnblogs.com/fnlingnzb-learner/p/10649509.html