前置知识: Java

数据类型与类型转换

7 min入门

基本类型、引用类型、自动装箱与类型转换规则。

前置知识

学习目标

  • 掌握「0. 本节阅读指引(先读这一节)」的核心机制、典型用法与常见陷阱
  • 掌握「1. 数据类型分类」的核心机制、典型用法与常见陷阱
  • 掌握「2. 基本数据类型详解」的核心机制、典型用法与常见陷阱
  • 掌握「3. 引用数据类型」的核心机制、典型用法与常见陷阱
  • 掌握「4. 类型转换」的核心机制、典型用法与常见陷阱

0. 本节阅读指引(先读这一节)

本篇是「数据类型与类型转换」,目标:分清 8 种基本类型与引用类型,理解自动/强制转换规则。

零基础第一遍只读:

  1. 第 1 节 数据类型分类、2. 基本数据类型详解、3. 引用数据类型;
  2. 第 4 节 类型转换;5. 类型转换的特殊情况(装箱拆箱、类型提升、字符串拼接)建议一并看,最常踩坑。

可跳过:浮点精度等二进制细节不理解不阻塞,二刷再看;6-7 节浏览即可。

记住:小范围自动转大范围,大范围强转小范围可能丢精度。

1. 数据类型分类

Java 是一种强类型语言,数据类型分为两大类:

1.1 基本数据类型 (Primitive Types)

  • 存储方式:直接存储在栈内存中,值存储在变量分配的内存空间里。
  • 特点:占用空间小,存取速度快。
  • 种类:共有 8 种基本数据类型,分为四大类。

1.2 引用数据类型 (Reference Types)

  • 存储方式:存储在堆内存中,栈内存中存储的是对象的引用(地址)。
  • 特点:占用空间较大,存取速度相对较慢。
  • 种类:类 (Class)、接口 (Interface)、数组 (Array)、枚举 (Enum) 等。

2. 基本数据类型详解

2.1 整数类型

类型占用字节默认值取值范围适用场景
byte10-128 ~ 127节省内存,适用于存储小范围整数
short20-32,768 ~ 32,767适用于存储中等范围整数
int40-2^31 ~ 2^31-1 (约 21亿)最常用的整数类型
long80L-2^63 ~ 2^63-1适用于存储大范围整数

示例:

 byte b = 100; // 正确,在 byte 范围内
 short s = 1000; // 正确,在 short 范围内
 int i = 100000; // 正确,在 int 范围内
 long l = 10000000000L; // 注意:需要加 L 后缀

2.2 浮点数类型

类型占用字节默认值精度适用场景
float40.0f单精度,约 7 位小数节省内存,适用于对精度要求不高的场景
double80.0d双精度,约 15-17 位小数最常用的浮点数类型,精度更高

示例:

 float f = 3.14f; // 注意:需要加 F 后缀
 double d = 3.1415926535; // double 是默认的浮点数类型

2.3 字符类型

类型占用字节默认值取值范围适用场景
char2‘\u0000’0 ~ 65535存储单个字符,使用 Unicode 编码

示例:

 char c1 = 'A'; // 字符字面量
 char c2 = 65; // ASCII 码,对应 'A'
 char c3 = '\u0041'; // Unicode 编码,对应 'A'

2.4 布尔类型

类型占用字节默认值取值范围适用场景
boolean1 (依赖 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 基本类型转字符串

方法:

  1. 使用 String.valueOf() 方法
  2. 使用 + 运算符
  3. 使用包装类的 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);