Java 的类型系统(Type System) 是其语言设计的核心,它决定了变量如何声明、赋值、传递、转换,以及编译器和运行时如何保证类型安全。下面从多个维度系统性地为你梳理 Java 类型系统的全貌。
Java 是一种 静态、强类型、面向对象、支持泛型和类型擦除 的编程语言。
特性 | 说明 |
|---|---|
静态类型(Static Typing) | 变量类型在编译时确定,编译器做类型检查。 |
强类型(Strong Typing) | 类型不兼容会编译报错。 |
面向对象类型系统 | 所有类形成继承树,支持多态、向上/向下转型。 |
支持泛型(Generics) | 编译期提供类型安全容器(如 List<String>),运行时类型擦除。 |
类型擦除(Type Erasure) | 泛型信息在运行时被擦除,仅用于编译期检查。 |
Java 类型分为两大类:
共 8 种,值不是对象:
类型 | 大小 | 默认值 | 包装类 |
|---|---|---|---|
boolean | 未明确规定(通常 1 bit) | false | Boolean |
byte | 1 字节 | 0 | Byte |
short | 2 字节 | 0 | Short |
int | 4 字节 | 0 | Integer |
long | 8 字节 | 0L | Long |
float | 4 字节 | 0.0f | Float |
double | 8 字节 | 0.0d | Double |
char | 2 字节(UTF-16) | '\u0000' | Character |
💡 基本类型没有继承关系。
所有“对象”的类型,变量存储的是引用(指针):
String s = new String("abc");List<String> list = new ArrayList<>();int[] arr = new int[10];
String[] strs = {"a", "b"};enum Color { RED, GREEN, BLUE }@Override
@Testvoid 类型void doSomething() { } // 方法返回 void
// void x; // 错误!不能声明 void 变量null —— 特殊引用值String s = null; // 可赋给任何引用类型💡 所有引用类型最终继承自
Object(数组、接口、枚举、注解都是Object子类型)。
小类型 → 大类型,无数据丢失
byte b = 10;
int i = b; // ✅ 自动
long l = i; // ✅ 自动
float f = l; // ✅ 自动
double d = f; // ✅ 自动
char c = 'A';
int x = c; // ✅ char → int(ASCII值)大类型 → 小类型,可能数据丢失或溢出
double d = 123.456;
int i = (int) d; // ✅ 强转,结果 123(小数部分截断)
long big = 9999999999L;
int small = (int) big; // ⚠ 溢出!结果不可预测Dog dog = new Dog();
Animal animal = dog; // ✅ 自动,子类 → 父类Animal a = new Dog();
Dog d = (Dog) a; // ✅ 成功
Animal a2 = new Cat();
Dog d2 = (Dog) a2; // ❌ 运行时抛 ClassCastException✅ 安全做法:
if (a instanceof Dog) {
Dog d = (Dog) a;
}Java 16+ 支持模式匹配:
if (a instanceof Dog d) {
d.bark(); // 直接使用 d,无需强转
}List<String> list = new ArrayList<>();
list.add("hello");
String s = list.get(0); // ✅ 编译器知道是 String,无需强转
list.add(123); // ❌ 编译错误!泛型只存在于编译期,运行时被擦除为原始类型(Raw Type)
List<String> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
System.out.println(list1.getClass() == list2.getClass()); // ✅ true!编译后都变成
List,泛型信息没了。
Java 方法调用是动态绑定(Dynamic Dispatch):
class Animal { void speak() { System.out.println("Animal"); } }
class Dog extends Animal { void speak() { System.out.println("Dog"); } }
Animal a = new Dog();
a.speak(); // ✅ 输出 "Dog" —— 根据实际类型调用编译器检查“声明类型是否有 speak() 方法”,JVM 执行时找“实际类型的方法实现”。
概念 | 编译时类型(声明类型) | 运行时类型(实际类型) |
|---|---|---|
定义 | 变量声明的类型(左边) | 对象实际的类(右边 new) |
作用 | 决定能访问哪些方法/字段 | 决定执行哪个方法实现 |
检查时机 | 编译期 | 运行期 |
获取方式 | 看代码 | obj.getClass() |
示例 | Animal a = new Dog(); → Animal | new Dog() → Dog.class |
ClassCastExceptionJava 的泛型是通过 类型擦除 实现的:
List<String> → List)ClassCastExceptionimport java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class TypeErasureReflectionIssue {
public static void main(String[] args) throws Exception {
// 1. 创建一个只能存 String 的列表
List<String> stringList = new ArrayList<>();
stringList.add("Hello");
// 2. 使用反射获取 List 的 add 方法
Method add = List.class.getMethod("add", Object.class);
// 3. 通过反射向 List<String> 中添加一个 Integer
add.invoke(stringList, 42); // ⚠ 绕过编译器检查!
// 4. 正常遍历(自动类型转换)
for (String s : stringList) {
System.out.println(s.toUpperCase());
}
}
}深色版本
Hello
Exception in thread "main" java.lang.ClassCastException:
java.lang.Integer cannot be cast to java.lang.String步骤 | 说明 |
|---|---|
List<String> | 编译期:只允许 String |
编译后 | 类型擦除 → 实际是 List,元素是 Object |
add.invoke(..., 42) | 反射调用 add(Object),JVM 允许 |
for (String s : ...) | 编译器插入 (String) 强制转换 |
取出 42 时 | (String)42 → ClassCastException |
因为:
invoke 会传什么类型stringList.add(42); // ❌ 编译错误!
add.invoke(stringList, 42); // ✅ 编译通过,运行时报错ArrayStoreExceptionObject[] arr = new String[10];
arr[0] = 123; // ❌ 运行时 ArrayStoreException(数组协变的代价)“尽可能在编译期发现错误,运行时保持高效和兼容。”
null 设计 → 简单但危险原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。