数据类型与类型转换
基本类型、引用类型、自动装箱与类型转换规则。
前置知识
- 程序结构与基本语法:建议先完成前一篇的学习
学习目标
- 掌握「0. 本节阅读指引(先读这一节)」的核心机制、典型用法与常见陷阱
- 掌握「1. 数据类型分类」的核心机制、典型用法与常见陷阱
- 掌握「2. 基本数据类型详解」的核心机制、典型用法与常见陷阱
- 掌握「3. 引用数据类型」的核心机制、典型用法与常见陷阱
- 掌握「4. 类型转换」的核心机制、典型用法与常见陷阱
0. 本节阅读指引(先读这一节)
本篇是「数据类型与类型转换」,目标:分清 8 种基本类型与引用类型,理解自动/强制转换规则。
零基础第一遍只读:
- 第 1 节 数据类型分类、2. 基本数据类型详解、3. 引用数据类型;
- 第 4 节 类型转换;5. 类型转换的特殊情况(装箱拆箱、类型提升、字符串拼接)建议一并看,最常踩坑。
可跳过:浮点精度等二进制细节不理解不阻塞,二刷再看;6-7 节浏览即可。
记住:小范围自动转大范围,大范围强转小范围可能丢精度。
1. 数据类型分类
Java 是一种强类型语言,数据类型分为两大类:
1.1 基本数据类型 (Primitive Types)
- 存储方式:直接存储在栈内存中,值存储在变量分配的内存空间里。
- 特点:占用空间小,存取速度快。
- 种类:共有 8 种基本数据类型,分为四大类。
1.2 引用数据类型 (Reference Types)
- 存储方式:存储在堆内存中,栈内存中存储的是对象的引用(地址)。
- 特点:占用空间较大,存取速度相对较慢。
- 种类:类 (Class)、接口 (Interface)、数组 (Array)、枚举 (Enum) 等。
2. 基本数据类型详解
2.1 整数类型
| 类型 | 占用字节 | 默认值 | 取值范围 | 适用场景 |
|---|---|---|---|---|
byte | 1 | 0 | -128 ~ 127 | 节省内存,适用于存储小范围整数 |
short | 2 | 0 | -32,768 ~ 32,767 | 适用于存储中等范围整数 |
int | 4 | 0 | -2^31 ~ 2^31-1 (约 21亿) | 最常用的整数类型 |
long | 8 | 0L | -2^63 ~ 2^63-1 | 适用于存储大范围整数 |
示例:
byte b = 100; // 正确,在 byte 范围内
short s = 1000; // 正确,在 short 范围内
int i = 100000; // 正确,在 int 范围内
long l = 10000000000L; // 注意:需要加 L 后缀
2.2 浮点数类型
| 类型 | 占用字节 | 默认值 | 精度 | 适用场景 |
|---|---|---|---|---|
float | 4 | 0.0f | 单精度,约 7 位小数 | 节省内存,适用于对精度要求不高的场景 |
double | 8 | 0.0d | 双精度,约 15-17 位小数 | 最常用的浮点数类型,精度更高 |
示例:
float f = 3.14f; // 注意:需要加 F 后缀
double d = 3.1415926535; // double 是默认的浮点数类型
2.3 字符类型
| 类型 | 占用字节 | 默认值 | 取值范围 | 适用场景 |
|---|---|---|---|---|
char | 2 | ‘\u0000’ | 0 ~ 65535 | 存储单个字符,使用 Unicode 编码 |
示例:
char c1 = 'A'; // 字符字面量
char c2 = 65; // ASCII 码,对应 'A'
char c3 = '\u0041'; // Unicode 编码,对应 'A'
2.4 布尔类型
| 类型 | 占用字节 | 默认值 | 取值范围 | 适用场景 |
|---|---|---|---|---|
boolean | 1 (依赖 JVM) | false | / false | 用于条件判断 |
示例:
boolean flag = true;
boolean isReady = false;
3. 引用数据类型
3.1 类 (Class)
- 定义:使用
class关键字定义的类型。 - 示例:
String,Integer,ArrayList等。 示例:
String str = "Hello, Java!"; // 字符串对象
ArrayList<String> list = new ArrayList<>(); // 集合对象
3.2 接口 (Interface)
- 定义:使用
interface关键字定义的类型。 - 示例:
Runnable,Comparable等。 示例:
Runnable runnable = () -> System.out.println("Hello");
3.3 数组 (Array)
- 定义:使用
[]符号定义的类型。 - 示例:
int[],String[]等。 示例:
int[] numbers = {1, 2, 3, 4, 5};
String[] names = new String[3];
4. 类型转换
4.1 自动类型转换 (Implicit Conversion)
定义:小容量类型向大容量类型的转换,由编译器自动完成。 转换规则:
- 整数类型:
byte→short→int→long - 浮点类型:
float→double - 整数到浮点:
byte→short→int→long→float→double - 字符到整数:
char→int→long→float→double示例:
byte b = 100;
short s = b; // 自动转换:byte → short
int i = s; // 自动转换:short → int
long l = i; // 自动转换:int → long
float f = l; // 自动转换:long → float
double d = f; // 自动转换:float → double
char c = 'A';
int i2 = c; // 自动转换:char → int,值为 65
4.2 强制类型转换 (Explicit Conversion)
定义:大容量类型向小容量类型的转换,需要显式指定目标类型。
语法:(目标类型) 表达式
注意事项:
- 可能导致数据溢出或精度丢失。
- 应该在转换前检查值是否在目标类型的范围内。 示例:
// 精度丢失
double pi = 3.14159;
int num = (int) pi; // 结果为 3,小数部分被截断
// 数据溢出
int i = 130;
byte b = (byte) i; // 结果为 -126,因为 130 超出了 byte 的范围
// 安全的强制转换
int i2 = 100;
byte b2 = (byte) i2; // 结果为 100,在 byte 范围内
4.3 基本类型与引用类型的转换
4.3.1 装箱 (Boxing)
定义:将基本类型转换为对应的包装类。 示例:
int i = 100;
integer iObj = Integer.valueOf(i); // 手动装箱
integer iObj2 = i; // 自动装箱(Java 5+)
boolean b = true;
Boolean bObj = Boolean.valueOf(b); // 手动装箱
Boolean bObj2 = b; // 自动装箱
4.3.2 拆箱 (Unboxing)
定义:将包装类转换为对应的基本类型。 示例:
integer iObj = 100;
int i = iObj.intValue(); // 手动拆箱
int i2 = iObj; // 自动拆箱(Java 5+)
Boolean bObj = true;
boolean b = bObj.booleanValue(); // 手动拆箱
boolean b2 = bObj; // 自动拆箱
4.4 字符串与基本类型的转换
4.4.1 基本类型转字符串
方法:
- 使用
String.valueOf()方法 - 使用
+运算符 - 使用包装类的
toString()方法 示例:
int i = 100;
String s1 = String.valueOf(i); // 方法 1
String s2 = i + "";
// 方法 2
String s3 = Integer.toString(i); // 方法 3
double d = 3.14;
String s4 = String.valueOf(d);
String s5 = d + "";
String s6 = Double.toString(d);
4.4.2 字符串转基本类型
方法:使用包装类的静态 parseXxx() 方法。
示例:
String s1 = "100";
int i = Integer.parseInt(s1);
String s2 = "3.14";
double d = Double.parseDouble(s2);
String s3 = "";
boolean b = Boolean.parseBoolean(s3);
5. 类型转换的特殊情况
5.1 整数默认类型
- 整数字面量:默认类型为
int。 - 长整型:需要在数值后加
L或l(建议使用大写L,避免与数字1混淆)。 示例:
int i = 100; // 正确
long l1 = 100; // 正确,int 自动转换为 long
long l2 = 10000000000L; // 必须加 L,否则会超出 int 范围
5.2 浮点数默认类型
- 浮点数字面量:默认类型为
double。 - 单精度浮点数:需要在数值后加
F或f。 示例:
double d = 3.14; // 正确
float f1 = (float) 3.14; // 强制转换
float f2 = 3.14f; // 正确,加 F 后缀
5.3 运算中的类型提升
- 规则:在运算中,
byte,short,char类型会先提升为int类型再进行计算。 - 结果类型:运算结果的类型为参与运算的最高类型。 示例:
byte b1 = 10;
byte b2 = 20;
// byte b3 = b1 + b2; // 错误:b1 + b2 结果为 int 类型
int i = b1 + b2; // 正确
int i1 = 100;
double d1 = 3.14;
double d2 = i1 + d1; // 结果为 double 类型
5.4 字符串拼接
- 规则:使用
+运算符时,若有一方为字符串,则整体变为字符串连接。 - 运算顺序:从左到右依次计算。 示例:
int i = 10;
int j = 20;
String s = "Result: " + i + j; // 结果为 "Result: 1020"
String s2 = "Result: " + (i + j); // 结果为 "Result: 30"
// 混合类型
String s3 = "Pi is " + 3.14; // 结果为 "Pi is 3.14"
String s4 = 10 + 20 + " is the sum"; // 结果为 "30 is the sum"
6. 类型转换的最佳实践
6.1 避免数据溢出
- 检查范围:在进行强制类型转换前,检查值是否在目标类型的范围内。
- 使用包装类的方法:使用包装类的
MIN_VALUE和MAX_VALUE常量进行范围检查。 示例:
int i = 130;
if (i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE) {
byte b = (byte) i;
System.out.println("转换成功: " + b);
}
System.out.println("转换失败:值超出 byte 范围");
}
6.2 避免精度丢失
- 使用适当的类型:根据需要选择合适的浮点类型。
- BigDecimal:对于需要精确计算的场景,使用
BigDecimal类。 示例:
// 精度丢失问题
double d1 = 0.1;
double d2 = 0.2;
double sum = d1 + d2; // 结果为 0.30000000000000004
// 使用 BigDecimal
import java.math.BigDecimal;
BigDecimal bd1 = new BigDecimal("0.1");
BigDecimal bd2 = new BigDecimal("0.2");
BigDecimal bdSum = bd1.add(bd2); // 结果为 0.3
6.3 合理使用装箱和拆箱
- 自动装箱/拆箱:Java 5+ 支持自动装箱和拆箱,但应避免在循环中频繁使用,可能影响性能。
- 缓存机制:包装类对某些值有缓存机制(如
Integer对 -128 到 127 的值),可以提高性能。 示例:
// 缓存机制示例
integer i1 = 100;
integer i2 = 100;
System.out.println(i1 == i2); // 结果为 true,因为 100 在缓存范围内
integer i3 = 200;
integer i4 = 200;
System.out.println(i3 == i4); // 结果为 false,因为 200 不在缓存范围内
System.out.println(i3.equals(i4)); // 结果为 true,推荐使用 equals 比较
6.4 字符串转换的安全性
- 异常处理:在将字符串转换为基本类型时,应捕获
NumberFormatException异常。 - 空值检查:在转换前检查字符串是否为
null。 示例:
String s = "123";
try {
int i = Integer.parseInt(s);
System.out.println("转换成功: " + i);
}
System.out.println("转换失败: " + e.getMessage());
}
// 空值检查
String s2 = null;
if (s2 != null) {
int i2 = Integer.parseInt(s2);
}
System.out.println("字符串为 null");
}
7. 实际应用示例
7.1 示例 1:温度转换
import java.util.Scanner;
public class TemperatureConverter {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("请输入摄氏度: ");
double celsius = sc.nextDouble();
// 转换为华氏度
double fahrenheit = celsius * 9 / 5 + 32;
System.out.println(celsius + " 摄氏度 = " + fahrenheit + " 华氏度");
sc.close();
}
}
7.2 示例 2:计算圆的面积
import java.util.Scanner;
public class CircleArea {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("请输入圆的半径: ");
double radius = sc.nextDouble();
// 计算面积
double area = Math.PI * radius * radius;
System.out.println("圆的面积: " + area);
sc.close();
}
}
整数类型
基本写法:byte 类型
byte <变量名> = <值>;
// 声明 1 字节整数
byte b = 100;
基本写法:short 类型
short <变量名> = <值>;
// 声明 2 字节整数
short s = 1000;
基本写法:int 类型
int <变量名> = <值>;
// 声明 4 字节整数(默认)
int i = 100000;
基本写法:long 类型
long <变量名> = <值>L;
// 声明 8 字节整数必须加 L 后缀
long l = 10000000000L;
浮点数类型
基本写法:float 类型
float <变量名> = <值>F;
// 声明单精度浮点数必须加 F 后缀
float f = 3.14f;
基本写法:double 类型
double <变量名> = <值>;
// 声明双精度浮点数(默认)
double d = 3.1415926535;
字符类型
基本写法:字符字面量
char <变量名> = '<字符>';
// 使用字符字面量声明
char c1 = 'A';
基本写法:ASCII 码赋值
char <变量名> = <ASCII码>;
// 使用 ASCII 码赋值
char c2 = 65;
基本写法:Unicode 编码赋值
char <变量名> = '\u<编码>';
// 使用 Unicode 编码赋值
char c3 = '\u0041';
布尔类型
基本写法:布尔类型
boolean <变量名> = <true|false>;
// 声明布尔类型变量
boolean flag = true;
引用数据类型
基本写法:类类型声明
<类名> <变量名> = new <类名>();
// 声明集合对象
ArrayList<String> list = new ArrayList<>();
基本写法:字符串声明
String <变量名> = "<字符串>";
// 声明字符串对象
String str = "Hello, Java!";
基本写法:数组类型声明
<类型>[] <变量名> = { <元素> };
// 声明并初始化数组
int[] numbers = {1, 2, 3, 4, 5};
自动类型转换
基本写法:小类型转大类型
<大类型> <变量名> = <小类型变量>;
// byte 自动转换为 short
byte b = 100;
short s = b;
基本写法:int 转 long
long <变量名> = <int变量>;
// int 自动转换为 long
int i = 100;
long l = i;
基本写法:char 转 int
int <变量名> = <char变量>;
// char 自动转换为 int
char c = 'A';
int i = c;
强制类型转换
基本写法:强制类型转换
(<目标类型>) <表达式>
// double 强制转换为 int
double pi = 3.14159;
int num = (int) pi;
基本写法:int 转 byte
byte <变量名> = (byte) <int变量>;
// int 强制转换为 byte
int i = 100;
byte b = (byte) i;
装箱与拆箱
基本写法:手动装箱
<包装类> <变量名> = <包装类>.valueOf(<基本类型>);
// int 手动装箱为 Integer
int i = 100;
Integer iObj = Integer.valueOf(i);
基本写法:自动装箱
<包装类> <变量名> = <基本类型>;
// int 自动装箱为 Integer
Integer iObj = 100;
基本写法:手动拆箱
<基本类型> <变量名> = <包装类变量>.<xxxValue>();
// Integer 手动拆箱为 int
Integer iObj = 100;
int i = iObj.intValue();
基本写法:自动拆箱
<基本类型> <变量名> = <包装类变量>;
// Integer 自动拆箱为 int
Integer iObj = 100;
int i = iObj;
字符串与基本类型转换
基本写法:基本类型转字符串
String <变量名> = String.valueOf(<基本类型>);
// int 转换为字符串
int i = 100;
String s = String.valueOf(i);
基本写法:字符串转 int
int <变量名> = Integer.parseInt(<字符串>);
// 字符串转换为 int
String s = "100";
int i = Integer.parseInt(s);
基本写法:字符串转 double
double <变量名> = Double.parseDouble(<字符串>);
// 字符串转换为 double
String s = "3.14";
double d = Double.parseDouble(s);
基本写法:字符串转 boolean
boolean <变量名> = Boolean.parseBoolean(<字符串>);
// 字符串转换为 boolean
String s = "true";
boolean b = Boolean.parseBoolean(s);
运算中的类型提升
基本写法:byte 运算提升为 int
int <变量名> = <byte变量1> + <byte变量2>;
// 两个 byte 相加结果为 int
byte b1 = 10;
byte b2 = 20;
int i = b1 + b2;
基本写法:int 与 double 运算
double <变量名> = <int变量> + <double变量>;
// int 与 double 运算结果为 double
int i = 100;
double d = 3.14;
double result = i + d;
字符串拼接
基本写法:字符串与数字拼接
String <变量名> = <字符串> + <数字>;
// 字符串与数字拼接
String s = "Result: " + 42;
基本写法:括号优先拼接
String <变量名> = <字符串> + (<表达式>);
// 使用括号先计算再拼接
String s = "Result: " + (10 + 20);
BigDecimal 精确计算
基本写法:创建 BigDecimal
BigDecimal <变量名> = new BigDecimal("<数值>");
// 使用字符串创建 BigDecimal
BigDecimal bd = new BigDecimal("0.1");
基本写法:BigDecimal 加法
BigDecimal <结果> = <bd1>.add(<bd2>);
// 两个 BigDecimal 相加
BigDecimal bd1 = new BigDecimal("0.1");
BigDecimal bd2 = new BigDecimal("0.2");
BigDecimal sum = bd1.add(bd2);