Object 类的 clone 是 protected 的,不能直接调用,可以被子类调用。Object 类的 clone 会知道对象的大小,为它分配足够的内存空间,并将旧对象的内容复制到新的对象中。但是,Object.clone() 执行其动作之前必须先检查 class 是否实现了 Cloneable 接口。
Cloneable 接口是一个标记接口,也就是没有什么内容,定义如下:1
2
3package java.lang;
public interface Cloneable {
}
只有实现了 Cloneable 接口,才可以在该类的实例上调用 clone() 方法, 否则会抛出 CloneNotSupportedException。1
protected native Object clone() throws CloneNotSupportedException;
一、引用拷贝
创建一个指向对象的引用变量的拷贝。
1 | public class Apple |
执行上述代码后控制台打印结果为:1
2
3apple1: name=Apple, num=1
apple1: name=Apple2, num=2
apple2: name=Apple2, num=2
二、对象拷贝
1. 浅拷贝:
被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。即对象的浅拷贝会对“主”对象进行拷贝, 但不会复制主对象里面的对象。“里面的对象”会在原来的对象和它的副本之间共享。
简而言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象。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
42public class Apple1 implements Cloneable
{
private int num = 1;
private StringBuilder name = new StringBuilder("Apple");
public void addNum()
{
this.num = num +1;
this.name = name.append(num);
}
public String toString()
{
return "name=" + name + ", num=" + num;
}
// 浅拷贝
public Apple1 clone()
{
try
{
Apple1 cloned = (Apple1) super.clone();
return cloned;
} catch (CloneNotSupportedException ex)
{
ex.printStackTrace();
return null;
}
}
public static void main(String[] args)
{
Apple1 apple1 = new Apple1();
System.out.println("apple1: " + apple1);
Apple1 apple2 = apple1.clone();
apple2.addNum();
System.out.println("apple1: " + apple1);
System.out.println("apple2: " + apple2);
}
}
执行上述代码后控制台打印结果为:1
2
3apple1: name=Apple, num=1
apple1: name=Apple2, num=1
apple2: name=Apple2, num=2
2. 深拷贝:
深拷贝是一个整个独立的对象拷贝,深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。 深拷贝相比于浅拷贝速度较慢并且花销较大。
简而言之,深拷贝把要复制的对象所引用的对象都复制了一遍。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
43public class Apple2 implements Cloneable
{
private int num = 1;
private StringBuilder name = new StringBuilder("Apple");
public void addNum()
{
this.num = num +1;
this.name = name.append(num);
}
public String toString()
{
return "name=" + name + ", num=" + num;
}
// 深拷贝
public Apple2 clone()
{
try
{
Apple2 cloned = (Apple2) super.clone();
cloned.name = new StringBuilder(name);
return cloned;
} catch (CloneNotSupportedException ex)
{
ex.printStackTrace();
return null;
}
}
public static void main(String[] args)
{
Apple2 apple1 = new Apple2();
System.out.println("apple1: " + apple1);
Apple2 apple2 = apple1.clone();
apple2.addNum();
System.out.println("apple1: " + apple1);
System.out.println("apple2: " + apple2);
}
}
执行上述代码后控制台打印结果为:1
2
3apple1: name=Apple, num=1
apple1: name=Apple, num=1
apple2: name=Apple2, num=2
3. 深拷贝与浅拷贝的区别:
- 深拷贝和浅拷贝之间的区别在于是否复制了子对象。
- 深拷贝与浅拷贝问题中,会发生深拷贝的有 Java 中的 8 中基本类型以及他们的封装类型,另外还有
String
类型。其余的都是浅拷贝。
三、Java 基本数据类型
Java 是一种强类型语言。这就意味着必须为没一个变量声明一种类型。在 Java 中,一共有 8 中基本数据类型,其中有 4 种整型、2 种浮点型、1 种表示 Unicode 编码的字符单元的字符类型 char 和 1 种用于表示真值的 boolean 类型。
1. Java 中的 8 种基本类型及其封装类型:
- 字符型:char(Character)
- 数值型:
1. 整数型:byte(Byte)、short(Short)、int(Integer)、long(Long)
2. 浮点型:float(Float)、double(Double) - 布尔型:boolean(Boolean)
2. 整型
类 型 | 存储需求 | 取值范围 |
---|---|---|
byte | 1 字节 | -128 ~ 127 |
short | 2 字节 | -32768 ~ 32767 |
int | 4 字节 | -2 147 483 648 ~ 2 147 483 647(正好超过 20 亿) |
long | 8 字节 | -9 233 372 036 854 775 808 ~ 9 233 372 036 854 775 807 |
整数用于表示没有小数部分的值,它允许是负数
。在通常情况下,int 类型最常用。长整形数值有一个后缀 L(例如:4000000000L)。在 Java 中,整型的范围与运行 Java 代码的机器无关,这就解决了软件从一个平台移植到另一个平台,或者在同一平台中的不同操作系统之间进行移植给程序员带来的诸多问题。与此相反,C 和 C++ 程序需要针对不同的处理器选择最为有效的整型。
3. 浮点类型
类 型 | 存储需求 | 取值范围 |
---|---|---|
float | 4 字节 | 大约 $\pm$3.402 823 47E + 38F(有效位数为 6~7 位) |
double | 8 字节 | 大约 $\pm$1.797 693 134 862 315 70E + 308(有效位数为 15 位) |
浮点类型用于表示有小数部分的数值。double 表示这种类型的数值精度是 float 类型的两倍(称其为双精度数值)。绝大多部分应用程序都采用 double 类型。float 类型的数值有一个后缀 F(例如:3.14F),没有后缀 F 的浮点数值(例如:3.14)默认为 double 类型,或者添加后缀 D(例如:3.14D)。
- 正无穷大 Double.POSITIVE_INFINITY
- 负无穷大 Double.NEGATIVE_INFINITY
- NaN Double.NaN(不是一个数字)
1 | if (Double.isNaN(x)) // check whether x is "not a number" |
4. char 类型
char 类型用于表示单个字符,通常用来表示字符常量。例如:’A’ 是编码为 65 所对应的字符常量。与 “A” 不同,”A” 是一个包含字符 A 的字符串。Unicode 编码单元可以表示为十六进制值,其范围从 \u0000 到 \Uffff。
(0-9a-zA-Z)
转义序列 | 名称 | Unicode 值 | 转义序列 | 名称 | Unicode 值 | |
---|---|---|---|---|---|---|
\b | 退格 | \u0008 | \” | 双引号 | \u0022 | |
\t | 制表 | \u0009 | \’ | 单引号 | \u0027 | |
\n | 换行 | \u000a | ||||
\r | 回车 | \u000d |
5. boolean 类型
boolean(布尔)类型有两个值:false 和 true,用来判定逻辑条件。整型值和布尔值之间不能进行相互转换。在 C++ 中,数值或指针可以代替 boolean 值,值 0 相当于布尔值 false,非 0 值相当于布尔值 true,在 Java 中则不是这样。