一、数据类型
1.1 什么是数据类型
数据类型是编程语言中用来定义数据的属性和操作的规范。它确定了数据的存储方式、可操作的范围以及可进行的操作类型。数据类型指定了数据的大小、内存布局和数据值的范围。
1.2 java中的数据类型
在Java等静态类型语言中,数据类型在编译时就需要被明确地指定,并且在程序运行过程中保持不变。
Java数据类型(type)可以分为两大类:基本类型(primitive types)和引用类型(reference types)。基本数据类型是java预定义的,无需new(new出来的对象存放在堆里),基本数据类型存放在栈中。
graph LR
数据类型 --> 基本数据类型
数据类型 --> 引用数据类型
基本数据类型 --> 数值型
基本数据类型 --> c["字符型 (char)"]
基本数据类型 --> b["布尔型 (boolean)"]
数值型 --> i["整数类型 (byte,short,int,long)"]
数值型 --> f["浮点类型 (float,double)"]
引用数据类型 --> clazz["类 (class)"]
引用数据类型 --> inter["接口 (interface)"]
引用数据类型 --> arr["数组 (array)"]
二、基本数据类型
2.1 什么是基本数据类型
在Java中,基本数据类型(Primitive Data Types)是指用于存储基本数据值的数据类型,它们是语言内置的预定义类型。基本数据类型存在栈中,在内存中直接存储数据值。具有较高的执行效率和较小的内存占用。在大规模数据处理和性能要求较高的场景中,选择适当的数据类型可以提高程序的效率。
1
2
|
int x = 1;
int y = 2;
|
graph
subgraph 栈
direction TB
i1["int x = 1;"]
i2["int y = 2;"]
end
以下是java中的基本数据类型及其默认值和取值范围
| 数据类型 |
默认值 |
包装类 |
大小 |
范围 |
| byte |
0 |
Byte |
1字节(8bits) |
-128 ~ +127 |
| short |
0 |
Short |
2字节(16bits) |
-2^15 ~ +2^15-1 |
| int |
0 |
Integer |
4字节(32bits) |
-2^31 ~ +2^31-1 |
| long |
0L |
Long |
8字节(64bits) |
-2^63 ~ +2^63-1 |
| char |
\u0000 |
Character |
2字节(16 位 Unicode 字符) |
\u0000(十进制等效值为0) ~ \uffff(即为65535) |
| float |
0.0f |
Float |
4字节(32bits) |
见 IEEE 754 |
| double |
0.0d |
Double |
8字节(64bits) |
见 IEEE 754 |
| boolean |
false |
Boolean |
一般为1 字节(实际实现可能依赖于 JVM,但通常会占用1 字节) |
取值为true或false |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public class Test {
static char c;
static byte b;
static short s;
static int i;
static long l;
static float f;
static double d;
static boolean bl;
public static void main(String[] args) {
System.out.println(c); // \u0000 也可以一个空格
System.out.println(b); // 0
System.out.println(s); // 0
System.out.println(i); // 0
System.out.println(l); // 0
System.out.println(f); // 0.0
System.out.println(d); // 0.0
System.out.println(bl); // false
}
}
|
2.2 自动类型转换
Java支持自动类型转换,即将一个数据类型转换为另一个兼容的数据类型,如将int类型赋值给long类型。在进行类型转换时,需要注意数据范围和精度的变化,避免数据丢失或不准确。通常的浮点型数据在不声明的情况下都是double型的,如果要表示一个数据时float 型的,可以在数据后面加上 “F” 。浮点型的数据是不能完全精确的,有时候在计算时可能出现小数点最后几位出现浮动,这是正常的。
graph LR
byte --> short --> int --> long;
char --> int;
int --> float;
int --> double;
float --> double;
long --> double;
long --> float
1
2
3
4
5
6
7
8
9
10
11
12
|
public class Test {
public static void main(String[] args) {
int x = 100;
long y = x;
System.out.println(y); // 100
int b =2;
float c = 6f;
System.out.println(b/c); // 0.33333334 b会转化为float类型
}
}
|
char本质上是一种特殊的int
1
2
3
4
5
6
7
8
|
public class Test {
public static void main(String[] args) {
char ch1,ch2;
ch1 = 'Y';
ch2 = 89 ; // 可以将一个整数值赋给字符型变量
System.out.println(ch1==ch2); // true
}
}
|
2.3 强制类型转换
有时需要将一个数据类型强制转换为另一个不兼容的数据类型,如将long类型转换为int类型。在进行强制类型转换时,需要注意可能的数据丢失和精度问题,确保转换操作的正确性。
1
2
3
4
5
6
7
8
|
public class Test {
public static void main(String[] args) {
long y = 100;
int x = (int) y;
System.out.println(x); // 100
}
}
|
2.4 包装类和自动装箱
基本类型都有对应的包装类型,基本类型与其对应的包装类型之间的赋值使用自动装箱与拆箱完成。基本数据类型自动装箱过程都是调用其对应的valueOf() 方法。
-
new Integer(2) 与 Integer.valueOf(2) 的区别在于:new Integer(2) 每次都会新建一个对象,Integer.valueOf(123) 会先使用缓存池中的对象(多次调用会取得同一个对象的引用),缓存中没有才会使用new 创建一个新对象。
-
在 Java 8 中,Integer 缓存池的大小默认为 -128~127。除 Integer 类型,其他基本类型也维护了缓冲池,用于缓存一定范围内的对象。下面是各个基本类型的缓冲池范围:
- Byte:缓冲池范围是 -128 到 127。
- Short:缓冲池范围是 -128 到 127。
- Long:缓冲池范围是 -128 到 127。
- Character:缓冲池范围是 0 到 127。
- Boolean:Boolean 类型没有缓冲池,只有两个常量对象:Boolean.TRUE 和 Boolean.FALSE。
需要注意的是,浮点数类型(float 和 double)以及字符类型(char)没有实现缓冲池机制。
这些基本类型的缓冲池的作用与 Integer 缓冲池类似,即在创建对象时优先从缓冲池中获取,而不是每次都创建新的对象。这样可以节省内存,并提高性能。
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
|
public class PrimitiveCacheExample {
public static void main(String[] args) {
Integer x = 2; // 装箱
int y = x; // 拆箱
Integer i1 = 100;
Integer i2 = 100;
System.out.println(i1 == i2); // true
Integer i3 = 129;
Integer i4 = 129;
System.out.println(i3 == i4); // false
Byte b1 = 127;
Byte b2 = 127;
System.out.println(b1 == b2); // true
Short s1 = 100;
Short s2 = 100;
System.out.println(s1 == s2); // true
Short s3 = 200;
Short s4 = 200;
System.out.println(s3 == s4); // false
Long l1 = 100L;
Long l2 = 100L;
System.out.println(l1 == l2); // true
Long l3 = 200L;
Long l4 = 200L;
System.out.println(l3 == l4); // false
Character c1 = 'A';
Character c2 = 'A';
System.out.println(c1 == c2); // true
Boolean bo1 = true;
Boolean bo2 = true;
System.out.println(bo1 == bo2); // true
}
}
|
三、引用数据类型
3.1 什么是引用数据类型
引用类型均继承Object类(Objec也是引用类型)。使用Java内存堆和内存栈来进行这种类型的数据存储,简单地讲,“引用”(存储对象在内存堆上的地址)是存储在有序的内存栈上的,而对象本身的值存储在内存堆上的。
3.2 对象实例化及赋值过程
实例化一个对象可以分为三个步骤:
- 分配内存空间。
- 初始化对象。
- 将内存空间的地址赋值给对应的引用。
java中无论是基本类型还是引用类型,都是值传递:
- 对于基本类型而言,传递的是具体的值———-是在赋值
- 对于引用类型而言,传递的是具体的地址值—-是在指向一个对象
例:
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
45
46
47
48
|
package packages;
public class Test {
public static void main(String[] args) {
Dog dog = new Dog("小黑");
System.out.println(dog);
System.out.println(dog.getName());
new Test().updateDog(dog);
System.out.println(dog);
System.out.println(dog.getName());
}
void updateDog(Dog dog){
System.out.println(dog);
System.out.println(dog.getName());
dog.setName("小白");
System.out.println(dog);
System.out.println(dog.getName());
}
}
class Dog {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Dog(String name){
this.name = name;
}
}
/**
输出结果:
packages.Dog@6536e911
小黑
packages.Dog@6536e911
小黑
packages.Dog@6536e911
小白
packages.Dog@6536e911
小白
*/
|
上述结果比较好理解,不做解释。
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
45
46
47
|
package packages;
public class Test {
public static void main(String[] args) {
Dog dog = new Dog("小黑");
System.out.println(dog);
System.out.println(dog.getName());
new Test().updateDog(dog);
System.out.println(dog);
System.out.println(dog.getName());
}
void updateDog(Dog dog1){
System.out.println(dog1);
System.out.println(dog1.getName());
dog1 = new Dog("小白");
System.out.println(dog1);
System.out.println(dog1.getName());
}
}
class Dog {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Dog(String name){
this.name = name;
}
}
/**
输出结果:
packages.Dog@6536e911
小黑
packages.Dog@6536e911
小黑
packages.Dog@520a3426
小白
packages.Dog@6536e911
小黑
*/
|
- 第一步创建了一个Dog引用并指向了一个Dog对象,假设Dog对象是0x111,那么Dog的引用就指向了这个0x111的内存地址。
graph LR
direction LR
subgraph 栈
direction LR
i1["Dog dog"] -->f1["0x111"]
end
subgraph 堆
direction LR
i2["0x111 new Dog(“小黑”)"]
end
f1 --> i2
graph LR
direction LR
subgraph 栈
direction LR
i1["Dog dog"] --> f1["0x111"]
end
subgraph s2["updateDog()栈区"]
direction LR
i2["Dog dog1"] -- 取消 --> f2["0x111"]
i2["Dog dog1"] --> f3["0x222"]
end
subgraph 堆
direction LR
i3["0x111 new Dog(“小黑”)"]
i4["0x222 new Dog(“小白”)"]
end
f1 --> i3
f2 --> i3
f3 --> i4
文章作者
necor
上次更新
2024-12-30