Skip to content

数据类型与变量 ​

学习目标 ​

  • 能区分八种基本类型、引用类型、包装类型和 null。
  • 能根据数据含义选择合适的变量类型,读懂常见字面量后缀和默认值。
  • 理解字段、数组元素和局部变量在初始化方面的不同,并知道自动装箱的风险。

核心知识点 ​

专业术语 ​

  • 基本类型(primitive type):byte、short、int、long、float、double、char、boolean,变量直接保存值。
  • 引用类型(reference type):类、接口、数组、枚举等类型,变量保存对象引用,也可以保存 null。
  • 包装类型(wrapper type):Integer、Long、Double 等对象类型,为基本值提供对象语义和泛型兼容能力。
  • 自动装箱/拆箱(boxing/unboxing):编译器在基本类型与对应包装类型之间插入转换代码。
  • 字面量(literal):直接写在源码中的值,如 42、3.14、'A'、true 和 "Java"。
  • 默认值(default value):字段和数组元素在未显式赋值时由 Java 提供的初始值;局部变量没有自动默认值。
类型常用字面量字段/数组默认值适合场景
byte12(赋值目标足够窄时)0原始字节、协议数据
short12000少量明确的 16 位整数
int42、0x2A、0b1010100普通计数、下标和整数计算
long42L0L时间戳、较大的整数标识
float3.14F0.0F需要较低精度的近似计算
double3.140.0D一般科学和测量计算
char'A'、'中'\u0000一个 UTF-16 码元
booleantrue、falsefalse开关和条件状态

白话解释与边界 ​

八种基本类型 ​

基本类型像直接写在变量格子里的值;引用类型像指向对象的地址标签。声明 String name = null 时没有创建字符串对象,调用 name.length() 会抛出 NullPointerException。数组也是引用类型,int[] 的元素默认是 0,但数组变量本身仍可能为 null。

引用类型与 null ​

引用类型的变量保存对象引用而不是对象内容;null 代表当前没有对象可访问,使用前应先定义是否允许空值。

包装类型与装箱拆箱 ​

包装类型用于集合和泛型,例如 List<Integer> 不能写成 List<int>。自动装箱让 int 可以放进 Integer,自动拆箱让 Integer 参与整数运算,但拆箱 null 会抛出 NullPointerException。需要允许“没有值”时,优先明确使用包装类型并在拆箱前校验。

字面量与默认值 ​

字面量的类型由写法决定:普通整数是 int,加 L 才是 long;普通小数是 double,加 F 才是 float;字符用单引号,字符串用双引号。下划线、二进制和十六进制前缀只改善书写,不改变数值含义。

char 与 Unicode 边界 ​

默认值只适用于字段和数组元素,方法里的局部变量必须显式赋值后才能读取。final 变量一旦完成赋值就不能再赋值,但它并不会把引用对象自动变成不可变对象。char 是 UTF-16 码元,不一定等于一个完整 Unicode 字符;转换和精度的详细边界见下一节。

常用用法 ​

基本类型:保存简单数值 ​

基本类型适合表达确定的数值、字符和真假值;选择类型时同时考虑取值范围和精度。

java
// 语义:基本类型适合表达确定的数值、字符和真假值。
// 初始状态:count 初始为 3;price 初始为 2.5。
public class PrimitiveDemo {
    public static void main(String[] args) {
        int count = 3;
// 关键变化:count 接收表达式 3 的计算结果。
// 初始状态:count 的初始值为 3。
        double price = 2.5;
        System.out.println(count * price);
        // 输出:7.5
    }
}

包装类型:在泛型中表达可空值 ​

包装类型可以用于集合和泛型,但拆箱 null 会抛出异常,使用前要明确空值策略。

java
// 语义:包装类型可以用于集合和泛型,但拆箱 null 会抛出异常,使用前要明确空值策略。
// 初始状态:count 初始为 3。
public class WrapperDemo {
    public static void main(String[] args) {
        Integer count = 3;
        System.out.println(count + 1);
        // 输出:4
    }
}

不常用但需要知道 ​

null:表示没有可用对象 ​

null 不是空字符串;调用它的方法前必须先判断或使用明确的空值处理方式。

java
public class NullDemo {
// 作用:null 不是空字符串;调用它的方法前必须先判断或使用明确的空值处理方式。
// 初始状态:value 被赋为 null,所以 value == null 的判断结果为 true。
    public static void main(String[] args) {
        String value = null;
// 关键变化:value 接收表达式 null 的计算结果。
// 初始状态:value 的初始值为 null。
        System.out.println(value == null);
        // 输出:true
    }
}

char 与码点:处理 Unicode 边界 ​

一个 Unicode 码点可能占两个 UTF-16 char;处理用户可见字符时不要只用 length() 推断字符数。

java
// 输入:text 只含表情 "🙂",它在 UTF-16 中由两个 char 组成。
public class CodePointDemo {
    public static void main(String[] args) {
    // 作用:一个 Unicode 码点可能占两个 UTF-16 char;处理用户可见字符时不要只用 length() 推断字符数。
        String text = "🙂";
// 关键变化:text 接收表达式 "🙂" 的计算结果。
// 初始状态:text 的初始值为 "🙂"。
        System.out.println(text.codePointCount(0, text.length()));
        // 输出:1
    }
}

简单案例 ​

java
import java.util.ArrayList;
import java.util.List;

public class TypeBasicsDemo {
    static int fieldCount;
    static int[] fieldArray = new int[2];

    public static void main(String[] args) {
        int count = 3;
        long id = 9_000_000_000L;
        double ratio = 0.75;
        char first = 'A';
        boolean enabled = true;
        Integer boxed = count;          // 自动装箱
        int unboxed = boxed;            // 自动拆箱
        Integer missing = null;
        List<Integer> scores = new ArrayList<>();
// 初始状态:scores 是空的 ArrayList<Integer>。
        scores.add(100);                // 集合需要包装类型
// 关键变化:scores.add(100) 追加整数 100,scores 变为 [100]。

        System.out.println(count + ", " + id + ", " + ratio);
        // 输出:3, 9000000000, 0.75
        System.out.println(first + ", " + enabled + ", " + scores);
        // 输出:A, true, [100]
        System.out.println("defaults=" + fieldCount + ", " + fieldArray[0]);
        // 输出:defaults=0, 0
        System.out.println("same value=" + (count == unboxed) + ", missing=" + (missing == null));
        // 输出:same value=true, missing=true
    }
}

count 和 unboxed 都保存整数值;boxed 是对象引用,missing 明确表示没有对象。fieldCount 和 fieldArray[0] 是字段与数组元素,因此拥有默认值;如果把相同声明放到方法内部而不初始化,编译器会拒绝读取。

易混点 ​

  • null 不是 0、false 或空字符串,只能赋给引用类型。
  • Integer 和 int 的身份不同;包装对象可能为 null,自动拆箱并不免费也不总是安全。
  • 字段和数组元素有默认值,局部变量没有默认值;两者的区别是“声明过”不等于“可以读取”。
  • 42L、42.0F、'4'、"4" 是四种不同类型的字面量。
  • char 是一个 UTF-16 码元;完整 Unicode 码点、用户可见字符和 String.length() 不是同一个概念。

课后小问 ​

  1. 为什么 List<int> 不能通过编译,而 List<Integer> 可以? 答案:Java 泛型的类型参数必须是引用类型,基本类型 int 需要使用对应的包装类型 Integer。 解析:集合内部依赖对象语义,编译器会在 scores.add(100) 处自动装箱;读取元素时又可能自动拆箱,所以必须留意元素是否可能为 null。

  2. 为什么方法里的 int count; System.out.println(count); 会报错,而类字段 int count; 可以打印 0? 答案:字段和数组元素有语言规定的默认值,局部变量必须由程序员明确赋值。 解析:局部变量未赋值通常意味着控制流可能没有给它可靠的值,编译器会在读取处阻止这种不确定状态;字段则在对象或类初始化阶段得到 0。

  3. Integer value = null; int n = value; 的问题是什么? 答案:赋值时发生自动拆箱,null 没有可拆出的整数值,会抛出 NullPointerException。 解析:可以先用 value == null 判断,或者使用明确的默认值策略,例如 int n = value == null ? 0 : value;不要让隐式拆箱掩盖空值含义。

本节小结 ​

  • 八种基本类型直接保存值,引用类型保存对象引用,null 只表示没有引用对象。
  • 包装类型让基本值能够进入泛型和集合,但自动拆箱可能触发空值异常。
  • 字面量后缀决定初始类型,字段和数组元素有默认值,局部变量必须明确初始化。
  • final 约束重新赋值,不能单独推出对象内容不可变;char 表示 UTF-16 码元。

快速回顾 ​

  • 能列出八种基本类型及其常见使用场景和字面量写法。
  • 能判断一个变量是否允许为 null,以及何时会发生自动拆箱。
  • 能区分字段、数组元素和局部变量的默认值规则。
  • 能解释 Integer、int、char 和 String 之间的基本边界。

继续阅读 ​